Merge remote-tracking branch 'upstream/master'

master
James 2017-12-02 21:38:15 -05:00
commit aedc3113d6
106 changed files with 20591 additions and 17974 deletions

View File

@ -19,6 +19,3 @@ EmulationIssues =
[Video_Stereoscopy]
StereoConvergence = 64
[Video_Hacks]
ImmediateXFBEnable = True

View File

@ -19,6 +19,3 @@ EmulationIssues =
[Video_Hacks]
EFBToTextureEnable = False
[Video_Hacks]
ImmediateXFBEnable = True

View File

@ -16,6 +16,3 @@ EmulationIssues =
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
ImmediateXFBEnable = True

View File

@ -19,6 +19,3 @@ EmulationIssues = Classic mode score report needs real xfb. Nes masterpieces and
[Video_Stereoscopy]
StereoConvergence = 136
[Video_Hacks]
ImmediateXFBEnable = True

View File

@ -16,6 +16,3 @@ EmulationStateId = 5
[ActionReplay]
# Add action replay cheats here.
[Video_Hacks]
ImmediateXFBEnable = True

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1870,6 +1870,9 @@ const GLFunc gl_function_array[] = {
GLFUNC_REQUIRES(glDispatchCompute, "GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"),
GLFUNC_REQUIRES(glDispatchComputeIndirect,
"GL_ARB_compute_shader !VERSION_4_3 |VERSION_GLES_3_1"),
// ARB_get_texture_sub_image
GLFUNC_REQUIRES(glGetTextureSubImage, "GL_ARB_get_texture_sub_image !VERSION_4_5"),
};
namespace GLExtensions

View File

@ -14,6 +14,7 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h" // Local core functions
#include "Core/HW/Memmap.h"
#include "InputCommon/ControlReference/ControlReference.h" // For background input check
#ifdef _WIN32
#include <windows.h>
@ -72,7 +73,8 @@ IPCCommandResult USB_KBD::Write(const ReadWriteRequest& request)
IPCCommandResult USB_KBD::IOCtl(const IOCtlRequest& request)
{
if (SConfig::GetInstance().m_WiiKeyboard && !Core::WantsDeterminism() && !m_MessageQueue.empty())
if (SConfig::GetInstance().m_WiiKeyboard && !Core::WantsDeterminism() &&
ControlReference::InputGateOn() && !m_MessageQueue.empty())
{
Memory::CopyToEmu(request.buffer_out, &m_MessageQueue.front(), sizeof(SMessageData));
m_MessageQueue.pop();

View File

@ -906,9 +906,9 @@ void MainWindow::OnImportNANDBackup()
},
[this] {
return RunOnObject(this, [this] {
return QFileDialog::getOpenFileName(this, tr("Select the OTP/SEEPROM dump"),
return QFileDialog::getOpenFileName(this, tr("Select the keys file (OTP/SEEPROM dump)"),
QDir::currentPath(),
tr("BootMii OTP/SEEPROM dump (*.bin);;"
tr("BootMii keys file (*.bin);;"
"All Files (*)"))
.toStdString();
});

View File

@ -1306,10 +1306,11 @@ void CFrame::OnImportBootMiiBackup(wxCommandEvent& WXUNUSED(event))
DiscIO::NANDImporter().ImportNANDBin(
file_name, [&dialog] { dialog.Pulse(); },
[this] {
return WxStrToStr(wxFileSelector(
_("Select the OTP/SEEPROM dump"), wxEmptyString, wxEmptyString, wxEmptyString,
_("BootMii OTP/SEEPROM dump (*.bin)") + "|*.bin|" + wxGetTranslation(wxALL_FILES),
wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, this));
return WxStrToStr(wxFileSelector(_("Select the keys file (OTP/SEEPROM dump)"),
wxEmptyString, wxEmptyString, wxEmptyString,
_("BootMii keys file (*.bin)") + "|*.bin|" +
wxGetTranslation(wxALL_FILES),
wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, this));
});
wxPostEvent(GetMenuBar(), wxCommandEvent{DOLPHIN_EVT_UPDATE_LOAD_WII_MENU_ITEM});
}

View File

@ -17,61 +17,63 @@
namespace DX11
{
HINSTANCE hD3DCompilerDll = nullptr;
D3DREFLECT PD3DReflect = nullptr;
using D3DREFLECT = HRESULT(WINAPI*)(LPCVOID, SIZE_T, REFIID, void**);
static HINSTANCE s_d3d_compiler_dll;
static int s_d3dcompiler_dll_ref;
static D3DREFLECT s_d3d_reflect;
pD3DCompile PD3DCompile = nullptr;
int d3dcompiler_dll_ref = 0;
CREATEDXGIFACTORY PCreateDXGIFactory = nullptr;
HINSTANCE hDXGIDll = nullptr;
int dxgi_dll_ref = 0;
static HINSTANCE s_dxgi_dll;
static int s_dxgi_dll_ref;
static D3D11CREATEDEVICE PD3D11CreateDevice = nullptr;
HINSTANCE hD3DDll = nullptr;
int d3d_dll_ref = 0;
static D3D11CREATEDEVICE s_d3d11_create_device;
static HINSTANCE s_d3d_dll;
static int s_d3d_dll_ref;
namespace D3D
{
ID3D11Device* device = nullptr;
ID3D11Device1* device1 = nullptr;
ID3D11DeviceContext* context = nullptr;
static IDXGISwapChain1* swapchain = nullptr;
static ID3D11Debug* debug = nullptr;
D3D_FEATURE_LEVEL featlevel;
D3DTexture2D* backbuf = nullptr;
HWND hWnd;
std::vector<DXGI_SAMPLE_DESC> aa_modes; // supported AA modes of the current adapter
static IDXGISwapChain1* s_swapchain;
static ID3D11Debug* s_debug;
static D3D_FEATURE_LEVEL s_featlevel;
static D3DTexture2D* s_backbuf;
bool bgra_textures_supported;
bool allow_tearing_supported;
static std::vector<DXGI_SAMPLE_DESC> s_aa_modes; // supported AA modes of the current adapter
#define NUM_SUPPORTED_FEATURE_LEVELS 3
const D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = {
static bool s_bgra_textures_supported;
static bool s_allow_tearing_supported;
static unsigned int s_xres;
static unsigned int s_yres;
constexpr UINT NUM_SUPPORTED_FEATURE_LEVELS = 3;
constexpr D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = {
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
unsigned int xres, yres;
bool bFrameInProgress = false;
HRESULT LoadDXGI()
{
if (dxgi_dll_ref++ > 0)
if (s_dxgi_dll_ref++ > 0)
return S_OK;
if (hDXGIDll)
if (s_dxgi_dll)
return S_OK;
hDXGIDll = LoadLibraryA("dxgi.dll");
if (!hDXGIDll)
s_dxgi_dll = LoadLibraryA("dxgi.dll");
if (!s_dxgi_dll)
{
MessageBoxA(nullptr, "Failed to load dxgi.dll", "Critical error", MB_OK | MB_ICONERROR);
--dxgi_dll_ref;
--s_dxgi_dll_ref;
return E_FAIL;
}
// Even though we use IDXGIFactory2 we use CreateDXGIFactory1 to create it to maintain
// compatibility with Windows 7
PCreateDXGIFactory = (CREATEDXGIFACTORY)GetProcAddress(hDXGIDll, "CreateDXGIFactory1");
PCreateDXGIFactory = (CREATEDXGIFACTORY)GetProcAddress(s_dxgi_dll, "CreateDXGIFactory1");
if (PCreateDXGIFactory == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for CreateDXGIFactory1!", "Critical error",
MB_OK | MB_ICONERROR);
@ -81,20 +83,20 @@ HRESULT LoadDXGI()
HRESULT LoadD3D()
{
if (d3d_dll_ref++ > 0)
if (s_d3d_dll_ref++ > 0)
return S_OK;
if (hD3DDll)
if (s_d3d_dll)
return S_OK;
hD3DDll = LoadLibraryA("d3d11.dll");
if (!hD3DDll)
s_d3d_dll = LoadLibraryA("d3d11.dll");
if (!s_d3d_dll)
{
MessageBoxA(nullptr, "Failed to load d3d11.dll", "Critical error", MB_OK | MB_ICONERROR);
--d3d_dll_ref;
--s_d3d_dll_ref;
return E_FAIL;
}
PD3D11CreateDevice = (D3D11CREATEDEVICE)GetProcAddress(hD3DDll, "D3D11CreateDevice");
if (PD3D11CreateDevice == nullptr)
s_d3d11_create_device = (D3D11CREATEDEVICE)GetProcAddress(s_d3d_dll, "D3D11CreateDevice");
if (s_d3d11_create_device == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3D11CreateDevice!", "Critical error",
MB_OK | MB_ICONERROR);
@ -103,16 +105,16 @@ HRESULT LoadD3D()
HRESULT LoadD3DCompiler()
{
if (d3dcompiler_dll_ref++ > 0)
if (s_d3dcompiler_dll_ref++ > 0)
return S_OK;
if (hD3DCompilerDll)
if (s_d3d_compiler_dll)
return S_OK;
// The older version of the D3D compiler cannot compile our ubershaders without various
// graphical issues. D3DCOMPILER_DLL_A should point to d3dcompiler_47.dll, so if this fails
// to load, inform the user that they need to update their system.
hD3DCompilerDll = LoadLibraryA(D3DCOMPILER_DLL_A);
if (!hD3DCompilerDll)
s_d3d_compiler_dll = LoadLibraryA(D3DCOMPILER_DLL_A);
if (!s_d3d_compiler_dll)
{
PanicAlertT("Failed to load %s. If you are using Windows 7, try installing the "
"KB4019990 update package.",
@ -120,11 +122,11 @@ HRESULT LoadD3DCompiler()
return E_FAIL;
}
PD3DReflect = (D3DREFLECT)GetProcAddress(hD3DCompilerDll, "D3DReflect");
if (PD3DReflect == nullptr)
s_d3d_reflect = (D3DREFLECT)GetProcAddress(s_d3d_compiler_dll, "D3DReflect");
if (s_d3d_reflect == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3DReflect!", "Critical error",
MB_OK | MB_ICONERROR);
PD3DCompile = (pD3DCompile)GetProcAddress(hD3DCompilerDll, "D3DCompile");
PD3DCompile = (pD3DCompile)GetProcAddress(s_d3d_compiler_dll, "D3DCompile");
if (PD3DCompile == nullptr)
MessageBoxA(nullptr, "GetProcAddress failed for D3DCompile!", "Critical error",
MB_OK | MB_ICONERROR);
@ -134,41 +136,41 @@ HRESULT LoadD3DCompiler()
void UnloadDXGI()
{
if (!dxgi_dll_ref)
if (!s_dxgi_dll_ref)
return;
if (--dxgi_dll_ref != 0)
if (--s_dxgi_dll_ref != 0)
return;
if (hDXGIDll)
FreeLibrary(hDXGIDll);
hDXGIDll = nullptr;
if (s_dxgi_dll)
FreeLibrary(s_dxgi_dll);
s_dxgi_dll = nullptr;
PCreateDXGIFactory = nullptr;
}
void UnloadD3D()
{
if (!d3d_dll_ref)
if (!s_d3d_dll_ref)
return;
if (--d3d_dll_ref != 0)
if (--s_d3d_dll_ref != 0)
return;
if (hD3DDll)
FreeLibrary(hD3DDll);
hD3DDll = nullptr;
PD3D11CreateDevice = nullptr;
if (s_d3d_dll)
FreeLibrary(s_d3d_dll);
s_d3d_dll = nullptr;
s_d3d11_create_device = nullptr;
}
void UnloadD3DCompiler()
{
if (!d3dcompiler_dll_ref)
if (!s_d3dcompiler_dll_ref)
return;
if (--d3dcompiler_dll_ref != 0)
if (--s_d3dcompiler_dll_ref != 0)
return;
if (hD3DCompilerDll)
FreeLibrary(hD3DCompilerDll);
hD3DCompilerDll = nullptr;
PD3DReflect = nullptr;
if (s_d3d_compiler_dll)
FreeLibrary(s_d3d_compiler_dll);
s_d3d_compiler_dll = nullptr;
s_d3d_reflect = nullptr;
}
std::vector<DXGI_SAMPLE_DESC> EnumAAModes(IDXGIAdapter* adapter)
@ -181,9 +183,9 @@ std::vector<DXGI_SAMPLE_DESC> EnumAAModes(IDXGIAdapter* adapter)
ID3D11Device* _device;
ID3D11DeviceContext* _context;
D3D_FEATURE_LEVEL feat_level;
HRESULT hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS,
D3D11_SDK_VERSION, &_device, &feat_level, &_context);
HRESULT hr = s_d3d11_create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS,
D3D11_SDK_VERSION, &_device, &feat_level, &_context);
if (FAILED(hr) || feat_level == D3D_FEATURE_LEVEL_10_0)
{
DXGI_SAMPLE_DESC desc;
@ -216,9 +218,9 @@ std::vector<DXGI_SAMPLE_DESC> EnumAAModes(IDXGIAdapter* adapter)
D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter)
{
D3D_FEATURE_LEVEL feat_level = D3D_FEATURE_LEVEL_9_1;
PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, supported_feature_levels,
NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, nullptr, &feat_level,
nullptr);
s_d3d11_create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, supported_feature_levels,
NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, nullptr, &feat_level,
nullptr);
return feat_level;
}
@ -252,8 +254,8 @@ HRESULT Create(HWND wnd)
RECT client;
GetClientRect(hWnd, &client);
xres = client.right - client.left;
yres = client.bottom - client.top;
s_xres = client.right - client.left;
s_yres = client.bottom - client.top;
hr = LoadDXGI();
if (SUCCEEDED(hr))
@ -286,11 +288,11 @@ HRESULT Create(HWND wnd)
}
// get supported AA modes
aa_modes = EnumAAModes(adapter);
s_aa_modes = EnumAAModes(adapter);
if (std::find_if(aa_modes.begin(), aa_modes.end(), [](const DXGI_SAMPLE_DESC& desc) {
if (std::find_if(s_aa_modes.begin(), s_aa_modes.end(), [](const DXGI_SAMPLE_DESC& desc) {
return desc.Count == g_Config.iMultisamples;
}) == aa_modes.end())
}) == s_aa_modes.end())
{
Config::SetCurrent(Config::GFX_MSAA, UINT32_C(1));
UpdateActiveConfig();
@ -306,7 +308,7 @@ HRESULT Create(HWND wnd)
sizeof(allow_tearing));
factory5->Release();
}
allow_tearing_supported = SUCCEEDED(hr) && allow_tearing;
s_allow_tearing_supported = SUCCEEDED(hr) && allow_tearing;
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.BufferCount = 2;
@ -316,26 +318,26 @@ HRESULT Create(HWND wnd)
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.Scaling = DXGI_SCALING_STRETCH;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swap_chain_desc.Width = xres;
swap_chain_desc.Height = yres;
swap_chain_desc.Width = s_xres;
swap_chain_desc.Height = s_yres;
swap_chain_desc.Stereo = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer;
// This flag is necessary if we want to use a flip-model swapchain without locking the framerate
swap_chain_desc.Flags = allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
swap_chain_desc.Flags = s_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
// Creating debug devices can sometimes fail if the user doesn't have the correct
// version of the DirectX SDK. If it does, simply fallback to a non-debug device.
if (g_Config.bEnableValidationLayer)
{
hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_DEBUG,
supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS,
D3D11_SDK_VERSION, &device, &featlevel, &context);
hr = s_d3d11_create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_DEBUG,
supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS,
D3D11_SDK_VERSION, &device, &s_featlevel, &context);
// Debugbreak on D3D error
if (SUCCEEDED(hr) && SUCCEEDED(device->QueryInterface(__uuidof(ID3D11Debug), (void**)&debug)))
if (SUCCEEDED(hr) && SUCCEEDED(device->QueryInterface(__uuidof(ID3D11Debug), (void**)&s_debug)))
{
ID3D11InfoQueue* infoQueue = nullptr;
if (SUCCEEDED(debug->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&infoQueue)))
if (SUCCEEDED(s_debug->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&infoQueue)))
{
infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
infoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
@ -353,22 +355,22 @@ HRESULT Create(HWND wnd)
if (!g_Config.bEnableValidationLayer || FAILED(hr))
{
hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, supported_feature_levels,
NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, &device, &featlevel,
&context);
hr = s_d3d11_create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS,
D3D11_SDK_VERSION, &device, &s_featlevel, &context);
}
if (SUCCEEDED(hr))
{
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
&swapchain);
&s_swapchain);
if (FAILED(hr))
{
// Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to
// a sequential swapchain
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
&swapchain);
&s_swapchain);
}
if (FAILED(hr))
@ -377,7 +379,7 @@ HRESULT Create(HWND wnd)
// BitBlt-model swapchain
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
&swapchain);
&s_swapchain);
}
}
@ -389,7 +391,7 @@ HRESULT Create(HWND wnd)
_T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR);
SAFE_RELEASE(device);
SAFE_RELEASE(context);
SAFE_RELEASE(swapchain);
SAFE_RELEASE(s_swapchain);
return E_FAIL;
}
@ -411,34 +413,35 @@ HRESULT Create(HWND wnd)
if (SConfig::GetInstance().bFullscreen && !g_ActiveConfig.bBorderlessFullscreen)
{
swapchain->SetFullscreenState(true, nullptr);
swapchain->ResizeBuffers(0, xres, yres, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_desc.Flags);
s_swapchain->SetFullscreenState(true, nullptr);
s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM,
swap_chain_desc.Flags);
}
ID3D11Texture2D* buf;
hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
if (FAILED(hr))
{
MessageBox(wnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"),
MB_OK | MB_ICONERROR);
SAFE_RELEASE(device);
SAFE_RELEASE(context);
SAFE_RELEASE(swapchain);
SAFE_RELEASE(s_swapchain);
return E_FAIL;
}
backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
SAFE_RELEASE(buf);
CHECK(backbuf != nullptr, "Create back buffer texture");
SetDebugObjectName(backbuf->GetTex(), "backbuffer texture");
SetDebugObjectName(backbuf->GetRTV(), "backbuffer render target view");
CHECK(s_backbuf != nullptr, "Create back buffer texture");
SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture");
SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view");
context->OMSetRenderTargets(1, &backbuf->GetRTV(), nullptr);
context->OMSetRenderTargets(1, &s_backbuf->GetRTV(), nullptr);
// BGRA textures are easier to deal with in TextureCache, but might not be supported by the
// hardware
UINT format_support;
device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support);
bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
s_bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
g_Config.backend_info.bSupportsST3CTextures = SupportsS3TCTextures(device);
g_Config.backend_info.bSupportsBPTCTextures = SupportsBPTCTextures(device);
@ -449,12 +452,12 @@ HRESULT Create(HWND wnd)
void Close()
{
// we can't release the swapchain while in fullscreen.
swapchain->SetFullscreenState(false, nullptr);
s_swapchain->SetFullscreenState(false, nullptr);
// release all bound resources
context->ClearState();
SAFE_RELEASE(backbuf);
SAFE_RELEASE(swapchain);
SAFE_RELEASE(s_backbuf);
SAFE_RELEASE(s_swapchain);
SAFE_DELETE(stateman);
context->Flush(); // immediately destroy device objects
@ -463,16 +466,16 @@ void Close()
ULONG references = device->Release();
#if defined(_DEBUG) || defined(DEBUGFAST)
if (debug)
if (s_debug)
{
--references; // the debug interface increases the refcount of the device, subtract that.
if (references)
{
// print out alive objects, but only if we actually have pending references
// note this will also print out internal live objects to the debug console
debug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
s_debug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
}
SAFE_RELEASE(debug)
SAFE_RELEASE(s_debug)
}
#endif
@ -493,9 +496,9 @@ void Close()
const char* VertexShaderVersionString()
{
if (featlevel == D3D_FEATURE_LEVEL_11_0)
if (s_featlevel == D3D_FEATURE_LEVEL_11_0)
return "vs_5_0";
else if (featlevel == D3D_FEATURE_LEVEL_10_1)
else if (s_featlevel == D3D_FEATURE_LEVEL_10_1)
return "vs_4_1";
else /*if(featlevel == D3D_FEATURE_LEVEL_10_0)*/
return "vs_4_0";
@ -503,9 +506,9 @@ const char* VertexShaderVersionString()
const char* GeometryShaderVersionString()
{
if (featlevel == D3D_FEATURE_LEVEL_11_0)
if (s_featlevel == D3D_FEATURE_LEVEL_11_0)
return "gs_5_0";
else if (featlevel == D3D_FEATURE_LEVEL_10_1)
else if (s_featlevel == D3D_FEATURE_LEVEL_10_1)
return "gs_4_1";
else /*if(featlevel == D3D_FEATURE_LEVEL_10_0)*/
return "gs_4_0";
@ -513,9 +516,9 @@ const char* GeometryShaderVersionString()
const char* PixelShaderVersionString()
{
if (featlevel == D3D_FEATURE_LEVEL_11_0)
if (s_featlevel == D3D_FEATURE_LEVEL_11_0)
return "ps_5_0";
else if (featlevel == D3D_FEATURE_LEVEL_10_1)
else if (s_featlevel == D3D_FEATURE_LEVEL_10_1)
return "ps_4_1";
else /*if(featlevel == D3D_FEATURE_LEVEL_10_0)*/
return "ps_4_0";
@ -523,25 +526,25 @@ const char* PixelShaderVersionString()
D3DTexture2D*& GetBackBuffer()
{
return backbuf;
return s_backbuf;
}
unsigned int GetBackBufferWidth()
{
return xres;
return s_xres;
}
unsigned int GetBackBufferHeight()
{
return yres;
return s_yres;
}
bool BGRATexturesSupported()
{
return bgra_textures_supported;
return s_bgra_textures_supported;
}
bool AllowTearingSupported()
{
return allow_tearing_supported;
return s_allow_tearing_supported;
}
// Returns the maximum width/height of a texture. This value only depends upon the feature level in
@ -572,55 +575,34 @@ u32 GetMaxTextureSize(D3D_FEATURE_LEVEL feature_level)
void Reset()
{
// release all back buffer references
SAFE_RELEASE(backbuf);
SAFE_RELEASE(s_backbuf);
UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
// resize swapchain buffers
RECT client;
GetClientRect(hWnd, &client);
xres = client.right - client.left;
yres = client.bottom - client.top;
D3D::swapchain->ResizeBuffers(0, xres, yres, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags);
s_xres = client.right - client.left;
s_yres = client.bottom - client.top;
s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags);
// recreate back buffer texture
ID3D11Texture2D* buf;
HRESULT hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
HRESULT hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
if (FAILED(hr))
{
MessageBox(hWnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"),
MB_OK | MB_ICONERROR);
SAFE_RELEASE(device);
SAFE_RELEASE(context);
SAFE_RELEASE(swapchain);
SAFE_RELEASE(s_swapchain);
return;
}
backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
SAFE_RELEASE(buf);
CHECK(backbuf != nullptr, "Create back buffer texture");
SetDebugObjectName(backbuf->GetTex(), "backbuffer texture");
SetDebugObjectName(backbuf->GetRTV(), "backbuffer render target view");
}
bool BeginFrame()
{
if (bFrameInProgress)
{
PanicAlert("BeginFrame called although a frame is already in progress");
return false;
}
bFrameInProgress = true;
return (device != nullptr);
}
void EndFrame()
{
if (!bFrameInProgress)
{
PanicAlert("EndFrame called although no frame is in progress");
return;
}
bFrameInProgress = false;
CHECK(s_backbuf != nullptr, "Create back buffer texture");
SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture");
SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view");
}
void Present()
@ -634,22 +616,25 @@ void Present()
if (AllowTearingSupported() && !g_ActiveConfig.IsVSync() && !GetFullscreenState())
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
if (swapchain->IsTemporaryMonoSupported() && g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
if (s_swapchain->IsTemporaryMonoSupported() &&
g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
{
present_flags |= DXGI_PRESENT_STEREO_TEMPORARY_MONO;
}
// TODO: Is 1 the correct value for vsyncing?
swapchain->Present((UINT)g_ActiveConfig.IsVSync(), present_flags);
s_swapchain->Present((UINT)g_ActiveConfig.IsVSync(), present_flags);
}
HRESULT SetFullscreenState(bool enable_fullscreen)
{
return swapchain->SetFullscreenState(enable_fullscreen, nullptr);
return s_swapchain->SetFullscreenState(enable_fullscreen, nullptr);
}
bool GetFullscreenState()
{
BOOL state = FALSE;
swapchain->GetFullscreenState(&state, nullptr);
s_swapchain->GetFullscreenState(&state, nullptr);
return !!state;
}

View File

@ -60,11 +60,8 @@ extern ID3D11Device* device;
extern ID3D11Device1* device1;
extern ID3D11DeviceContext* context;
extern HWND hWnd;
extern bool bFrameInProgress;
void Reset();
bool BeginFrame();
void EndFrame();
void Present();
unsigned int GetBackBufferWidth();
@ -95,8 +92,6 @@ typedef HRESULT(WINAPI* D3D11CREATEDEVICE)(IDXGIAdapter*, D3D_DRIVER_TYPE, HMODU
CONST D3D_FEATURE_LEVEL*, UINT, UINT, ID3D11Device**,
D3D_FEATURE_LEVEL*, ID3D11DeviceContext**);
typedef HRESULT(WINAPI* D3DREFLECT)(LPCVOID, SIZE_T, REFIID, void**);
extern D3DREFLECT PD3DReflect;
extern pD3DCompile PD3DCompile;
} // namespace DX11

View File

@ -263,6 +263,21 @@ void StateManager::SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceVie
} // namespace D3D
StateCache::~StateCache()
{
for (auto& it : m_depth)
SAFE_RELEASE(it.second);
for (auto& it : m_raster)
SAFE_RELEASE(it.second);
for (auto& it : m_blend)
SAFE_RELEASE(it.second);
for (auto& it : m_sampler)
SAFE_RELEASE(it.second);
}
ID3D11SamplerState* StateCache::Get(SamplerState state)
{
auto it = m_sampler.find(state.hex);
@ -471,33 +486,6 @@ ID3D11DepthStencilState* StateCache::Get(DepthState state)
return res;
}
void StateCache::Clear()
{
for (auto it : m_depth)
{
SAFE_RELEASE(it.second);
}
m_depth.clear();
for (auto it : m_raster)
{
SAFE_RELEASE(it.second);
}
m_raster.clear();
for (auto it : m_blend)
{
SAFE_RELEASE(it.second);
}
m_blend.clear();
for (auto it : m_sampler)
{
SAFE_RELEASE(it.second);
}
m_sampler.clear();
}
D3D11_PRIMITIVE_TOPOLOGY StateCache::GetPrimitiveTopology(PrimitiveType primitive)
{
static constexpr std::array<D3D11_PRIMITIVE_TOPOLOGY, 4> primitives = {

View File

@ -23,6 +23,8 @@ namespace DX11
class StateCache
{
public:
~StateCache();
// Get existing or create new render state.
// Returned objects is owned by the cache and does not need to be released.
ID3D11SamplerState* Get(SamplerState state);
@ -30,9 +32,6 @@ public:
ID3D11RasterizerState* Get(RasterizationState state);
ID3D11DepthStencilState* Get(DepthState state);
// Release all cached states and clear hash tables.
void Clear();
// Convert RasterState primitive type to D3D11 primitive topology.
static D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology(PrimitiveType primitive);

View File

@ -40,7 +40,11 @@ DXGI_FORMAT GetDXGIFormatForHostFormat(AbstractTextureFormat format)
case AbstractTextureFormat::BPTC:
return DXGI_FORMAT_BC7_UNORM;
case AbstractTextureFormat::RGBA8:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case AbstractTextureFormat::BGRA8:
return DXGI_FORMAT_B8G8R8A8_UNORM;
default:
PanicAlert("Unhandled texture format.");
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}
@ -80,7 +84,6 @@ DXTexture::DXTexture(const TextureConfig& tex_config) : AbstractTexture(tex_conf
DXTexture::~DXTexture()
{
m_texture->Release();
SAFE_RELEASE(m_staging_texture);
}
D3DTexture2D* DXTexture::GetRawTexIdentifier() const
@ -93,97 +96,36 @@ void DXTexture::Bind(unsigned int stage)
D3D::stateman->SetTexture(stage, m_texture->GetSRV());
}
std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapFullImpl()
void DXTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, m_config.width,
m_config.height, 1, 1, 0, D3D11_USAGE_STAGING,
D3D11_CPU_ACCESS_READ);
const DXTexture* srcentry = static_cast<const DXTexture*>(src);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}
D3D11_BOX src_box;
src_box.left = src_rect.left;
src_box.top = src_rect.top;
src_box.right = src_rect.right;
src_box.bottom = src_rect.bottom;
src_box.front = 0;
src_box.back = 1;
// Copy the selected data to the staging texture
D3D::context->CopyResource(m_staging_texture, m_texture->GetTex());
// Map the staging texture to client memory, and encode it as a .png image.
D3D11_MAPPED_SUBRESOURCE map;
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
m_config.width, m_config.height};
D3D::context->CopySubresourceRegion(
m_texture->GetTex(), D3D11CalcSubresource(dst_level, dst_layer, m_config.levels),
dst_rect.left, dst_rect.top, 0, srcentry->m_texture->GetTex(),
D3D11CalcSubresource(src_level, src_layer, srcentry->m_config.levels), &src_box);
}
std::optional<AbstractTexture::RawTextureInfo> DXTexture::MapRegionImpl(u32 level, u32 x, u32 y,
u32 width, u32 height)
{
CD3D11_TEXTURE2D_DESC staging_texture_desc(DXGI_FORMAT_R8G8B8A8_UNORM, width, height, 1, 1, 0,
D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ);
HRESULT hr = D3D::device->CreateTexture2D(&staging_texture_desc, nullptr, &m_staging_texture);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to create texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}
// Copy the selected data to the staging texture
CD3D11_BOX src_box(x, y, 0, width, height, 1);
D3D::context->CopySubresourceRegion(m_staging_texture, 0, 0, 0, 0, m_texture->GetTex(),
D3D11CalcSubresource(level, 0, m_config.levels), &src_box);
// Map the staging texture to client memory, and encode it as a .png image.
D3D11_MAPPED_SUBRESOURCE map;
hr = D3D::context->Map(m_staging_texture, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr))
{
WARN_LOG(VIDEO, "Failed to map texture dumping readback texture: %X", static_cast<u32>(hr));
return {};
}
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(map.pData), map.RowPitch,
m_config.width, m_config.height};
}
void DXTexture::Unmap()
{
if (!m_staging_texture)
return;
D3D::context->Unmap(m_staging_texture, 0);
}
void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void DXTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
const DXTexture* srcentry = static_cast<const DXTexture*>(source);
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight())
{
D3D11_BOX srcbox;
srcbox.left = srcrect.left;
srcbox.top = srcrect.top;
srcbox.right = srcrect.right;
srcbox.bottom = srcrect.bottom;
srcbox.front = 0;
srcbox.back = srcentry->m_config.layers;
_assert_(m_config.rendertarget);
D3D::context->CopySubresourceRegion(m_texture->GetTex(), 0, dstrect.left, dstrect.top, 0,
srcentry->m_texture->GetTex(), 0, &srcbox);
return;
}
else if (!m_config.rendertarget)
{
return;
}
g_renderer->ResetAPIState(); // reset any game specific settings
const D3D11_VIEWPORT vp = CD3D11_VIEWPORT(float(dstrect.left), float(dstrect.top),
@ -213,8 +155,140 @@ void DXTexture::CopyRectangleFromTexture(const AbstractTexture* source,
void DXTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size)
{
size_t src_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length);
size_t src_pitch = CalculateStrideForFormat(m_config.format, row_length);
D3D::context->UpdateSubresource(m_texture->GetTex(), level, nullptr, buffer,
static_cast<UINT>(src_pitch), 0);
}
DXStagingTexture::DXStagingTexture(StagingTextureType type, const TextureConfig& config,
ID3D11Texture2D* tex)
: AbstractStagingTexture(type, config), m_tex(tex)
{
}
DXStagingTexture::~DXStagingTexture()
{
if (IsMapped())
DXStagingTexture::Unmap();
SAFE_RELEASE(m_tex);
}
std::unique_ptr<DXStagingTexture> DXStagingTexture::Create(StagingTextureType type,
const TextureConfig& config)
{
D3D11_USAGE usage;
UINT cpu_flags;
if (type == StagingTextureType::Readback)
{
usage = D3D11_USAGE_STAGING;
cpu_flags = D3D11_CPU_ACCESS_READ;
}
else if (type == StagingTextureType::Upload)
{
usage = D3D11_USAGE_DYNAMIC;
cpu_flags = D3D11_CPU_ACCESS_WRITE;
}
else
{
usage = D3D11_USAGE_STAGING;
cpu_flags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
}
CD3D11_TEXTURE2D_DESC desc(GetDXGIFormatForHostFormat(config.format), config.width, config.height,
1, 1, 0, usage, cpu_flags);
ID3D11Texture2D* texture;
HRESULT hr = D3D::device->CreateTexture2D(&desc, nullptr, &texture);
CHECK(SUCCEEDED(hr), "Create staging texture");
if (FAILED(hr))
return nullptr;
return std::unique_ptr<DXStagingTexture>(new DXStagingTexture(type, config, texture));
}
void DXStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(m_type == StagingTextureType::Readback);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height);
if (IsMapped())
DXStagingTexture::Unmap();
CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1);
D3D::context->CopySubresourceRegion(
m_tex, 0, static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0,
static_cast<const DXTexture*>(src)->GetRawTexIdentifier()->GetTex(),
D3D11CalcSubresource(src_level, src_layer, src->GetConfig().levels), &src_box);
m_needs_flush = true;
}
void DXStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(m_type == StagingTextureType::Upload);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);
if (IsMapped())
DXStagingTexture::Unmap();
CD3D11_BOX src_box(src_rect.left, src_rect.top, 0, src_rect.right, src_rect.bottom, 1);
D3D::context->CopySubresourceRegion(
static_cast<const DXTexture*>(dst)->GetRawTexIdentifier()->GetTex(),
D3D11CalcSubresource(dst_level, dst_layer, dst->GetConfig().levels),
static_cast<u32>(dst_rect.left), static_cast<u32>(dst_rect.top), 0, m_tex, 0, &src_box);
}
bool DXStagingTexture::Map()
{
if (m_map_pointer)
return true;
D3D11_MAP map_type;
if (m_type == StagingTextureType::Readback)
map_type = D3D11_MAP_READ;
else if (m_type == StagingTextureType::Upload)
map_type = D3D11_MAP_WRITE;
else
map_type = D3D11_MAP_READ_WRITE;
D3D11_MAPPED_SUBRESOURCE sr;
HRESULT hr = D3D::context->Map(m_tex, 0, map_type, 0, &sr);
CHECK(SUCCEEDED(hr), "Map readback texture");
if (FAILED(hr))
return false;
m_map_pointer = reinterpret_cast<char*>(sr.pData);
m_map_stride = sr.RowPitch;
return true;
}
void DXStagingTexture::Unmap()
{
if (!m_map_pointer)
return;
D3D::context->Unmap(m_tex, 0);
m_map_pointer = nullptr;
}
void DXStagingTexture::Flush()
{
// Flushing is handled by the API.
m_needs_flush = false;
}
} // namespace DX11

View File

@ -6,6 +6,7 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
class D3DTexture2D;
@ -19,23 +20,47 @@ public:
~DXTexture();
void Bind(unsigned int stage) override;
void Unmap() override;
void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
D3DTexture2D* GetRawTexIdentifier() const;
private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;
D3DTexture2D* m_texture;
ID3D11Texture2D* m_staging_texture = nullptr;
};
class DXStagingTexture final : public AbstractStagingTexture
{
public:
DXStagingTexture() = delete;
~DXStagingTexture();
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;
bool Map() override;
void Unmap() override;
void Flush() override;
static std::unique_ptr<DXStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);
private:
DXStagingTexture(StagingTextureType type, const TextureConfig& config, ID3D11Texture2D* tex);
ID3D11Texture2D* m_tex = nullptr;
};
} // namespace DX11

View File

@ -4,17 +4,21 @@
#include "VideoBackends/D3D/PSTextureEncoder.h"
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Core/HW/Memmap.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoBackends/D3D/D3DShader.h"
#include "VideoBackends/D3D/D3DState.h"
#include "VideoBackends/D3D/D3DUtil.h"
#include "VideoBackends/D3D/DXTexture.h"
#include "VideoBackends/D3D/FramebufferManager.h"
#include "VideoBackends/D3D/Render.h"
#include "VideoBackends/D3D/TextureCache.h"
#include "VideoBackends/D3D/VertexShaderCache.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/VideoCommon.h"
@ -31,76 +35,41 @@ struct EFBEncodeParams
};
PSTextureEncoder::PSTextureEncoder()
: m_ready(false), m_out(nullptr), m_outRTV(nullptr), m_outStage(nullptr),
m_encodeParams(nullptr)
{
}
PSTextureEncoder::~PSTextureEncoder() = default;
void PSTextureEncoder::Init()
{
m_ready = false;
HRESULT hr;
// Create output texture RGBA format
// TODO: This Texture is overly large and parts of it are unused
// EFB2RAM copies use max (EFB_WIDTH * 4) by (EFB_HEIGHT / 4)
// XFB2RAM copies use max (EFB_WIDTH / 2) by (EFB_HEIGHT)
D3D11_TEXTURE2D_DESC t2dd = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, EFB_WIDTH * 4, 1024,
1, 1, D3D11_BIND_RENDER_TARGET);
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_out);
CHECK(SUCCEEDED(hr), "create efb encode output texture");
D3D::SetDebugObjectName(m_out, "efb encoder output texture");
// Create output render target view
D3D11_RENDER_TARGET_VIEW_DESC rtvd = CD3D11_RENDER_TARGET_VIEW_DESC(
m_out, D3D11_RTV_DIMENSION_TEXTURE2D, DXGI_FORMAT_B8G8R8A8_UNORM);
hr = D3D::device->CreateRenderTargetView(m_out, &rtvd, &m_outRTV);
CHECK(SUCCEEDED(hr), "create efb encode output render target view");
D3D::SetDebugObjectName(m_outRTV, "efb encoder output rtv");
// Create output staging buffer
t2dd.Usage = D3D11_USAGE_STAGING;
t2dd.BindFlags = 0;
t2dd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
hr = D3D::device->CreateTexture2D(&t2dd, nullptr, &m_outStage);
CHECK(SUCCEEDED(hr), "create efb encode output staging buffer");
D3D::SetDebugObjectName(m_outStage, "efb encoder output staging buffer");
// TODO: Move this to a constant somewhere in common.
TextureConfig encoding_texture_config(EFB_WIDTH * 4, 1024, 1, 1, AbstractTextureFormat::BGRA8,
true);
m_encoding_render_texture = g_renderer->CreateTexture(encoding_texture_config);
m_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, encoding_texture_config);
_assert_(m_encoding_render_texture && m_encoding_readback_texture);
// Create constant buffer for uploading data to shaders
D3D11_BUFFER_DESC bd = CD3D11_BUFFER_DESC(sizeof(EFBEncodeParams), D3D11_BIND_CONSTANT_BUFFER);
hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encodeParams);
HRESULT hr = D3D::device->CreateBuffer(&bd, nullptr, &m_encode_params);
CHECK(SUCCEEDED(hr), "create efb encode params buffer");
D3D::SetDebugObjectName(m_encodeParams, "efb encoder params buffer");
m_ready = true;
D3D::SetDebugObjectName(m_encode_params, "efb encoder params buffer");
}
void PSTextureEncoder::Shutdown()
{
m_ready = false;
for (auto& it : m_encoding_shaders)
{
SAFE_RELEASE(it.second);
}
m_encoding_shaders.clear();
SAFE_RELEASE(m_encodeParams);
SAFE_RELEASE(m_outStage);
SAFE_RELEASE(m_outRTV);
SAFE_RELEASE(m_out);
SAFE_RELEASE(m_encode_params);
}
void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const EFBRectangle& src_rect, bool scale_by_half)
{
if (!m_ready) // Make sure we initialized OK
return;
HRESULT hr;
// Resolve MSAA targets before copying.
// FIXME: Instead of resolving EFB, it would be better to pick out a
// single sample from each pixel. The game may break if it isn't
@ -122,7 +91,10 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
constexpr EFBRectangle fullSrcRect(0, 0, EFB_WIDTH, EFB_HEIGHT);
TargetRectangle targetRect = g_renderer->ConvertEFBRectangle(fullSrcRect);
D3D::context->OMSetRenderTargets(1, &m_outRTV, nullptr);
D3D::context->OMSetRenderTargets(
1,
&static_cast<DXTexture*>(m_encoding_render_texture.get())->GetRawTexIdentifier()->GetRTV(),
nullptr);
EFBEncodeParams encode_params;
encode_params.SrcLeft = src_rect.left;
@ -130,8 +102,8 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
encode_params.DestWidth = native_width;
encode_params.ScaleFactor = scale_by_half ? 2 : 1;
encode_params.y_scale = params.y_scale;
D3D::context->UpdateSubresource(m_encodeParams, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encodeParams);
D3D::context->UpdateSubresource(m_encode_params, 0, nullptr, &encode_params, 0, 0);
D3D::stateman->SetPixelConstants(m_encode_params);
// We also linear filtering for both box filtering and downsampling higher resolutions to 1x
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
@ -148,24 +120,15 @@ void PSTextureEncoder::Encode(u8* dst, const EFBCopyParams& params, u32 native_w
VertexShaderCache::GetSimpleInputLayout());
// Copy to staging buffer
D3D11_BOX srcBox = CD3D11_BOX(0, 0, 0, words_per_row, num_blocks_y, 1);
D3D::context->CopySubresourceRegion(m_outStage, 0, 0, 0, 0, m_out, 0, &srcBox);
// Transfer staging buffer to GameCube/Wii RAM
D3D11_MAPPED_SUBRESOURCE map = {0};
hr = D3D::context->Map(m_outStage, 0, D3D11_MAP_READ, 0, &map);
CHECK(SUCCEEDED(hr), "map staging buffer (0x%x)", hr);
u8* src = (u8*)map.pData;
u32 readStride = std::min(bytes_per_row, map.RowPitch);
for (unsigned int y = 0; y < num_blocks_y; ++y)
MathUtil::Rectangle<int> copy_rect(0, 0, words_per_row, num_blocks_y);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->Flush();
if (m_encoding_readback_texture->Map())
{
memcpy(dst, src, readStride);
dst += memory_stride;
src += map.RowPitch;
m_encoding_readback_texture->ReadTexels(copy_rect, dst, memory_stride);
m_encoding_readback_texture->Unmap();
}
D3D::context->Unmap(m_outStage, 0);
}
// Restore API
@ -180,7 +143,7 @@ ID3D11PixelShader* PSTextureEncoder::GetEncodingPixelShader(const EFBCopyParams&
return iter->second;
D3DBlob* bytecode = nullptr;
const char* shader = TextureConversionShader::GenerateEncodingShader(params, APIType::D3D);
const char* shader = TextureConversionShaderTiled::GenerateEncodingShader(params, APIType::D3D);
if (!D3D::CompilePixelShader(shader, &bytecode))
{
PanicAlert("Failed to compile texture encoding shader.");

View File

@ -5,11 +5,15 @@
#pragma once
#include <map>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/VideoCommon.h"
class AbstractTexture;
class AbstractStagingTexture;
struct ID3D11Texture2D;
struct ID3D11RenderTargetView;
struct ID3D11Buffer;
@ -29,6 +33,7 @@ class PSTextureEncoder final
{
public:
PSTextureEncoder();
~PSTextureEncoder();
void Init();
void Shutdown();
@ -39,12 +44,9 @@ public:
private:
ID3D11PixelShader* GetEncodingPixelShader(const EFBCopyParams& params);
bool m_ready;
ID3D11Texture2D* m_out;
ID3D11RenderTargetView* m_outRTV;
ID3D11Texture2D* m_outStage;
ID3D11Buffer* m_encodeParams;
ID3D11Buffer* m_encode_params = nullptr;
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
std::map<EFBCopyParams, ID3D11PixelShader*> m_encoding_shaders;
};
}

View File

@ -53,31 +53,42 @@ typedef struct _Nv_Stereo_Image_Header
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e
struct GXPipelineState
Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight())
{
std::array<SamplerState, 8> samplers;
BlendingState blend;
DepthState zmode;
RasterizationState raster;
};
m_last_multisamples = g_ActiveConfig.iMultisamples;
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
m_last_fullscreen_mode = D3D::GetFullscreenState();
static u32 s_last_multisamples = 1;
static bool s_last_stereo_mode = false;
static bool s_last_fullscreen_mode = false;
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
SetupDeviceObjects();
static std::array<ID3D11BlendState*, 4> s_clear_blend_states{};
static std::array<ID3D11DepthStencilState*, 3> s_clear_depth_states{};
static ID3D11BlendState* s_reset_blend_state = nullptr;
static ID3D11DepthStencilState* s_reset_depth_state = nullptr;
static ID3D11RasterizerState* s_reset_rast_state = nullptr;
// Setup GX pipeline state
for (auto& sampler : m_gx_state.samplers)
sampler.hex = RenderState::GetPointSamplerState().hex;
static ID3D11Texture2D* s_screenshot_texture = nullptr;
static D3DTexture2D* s_3d_vision_texture = nullptr;
m_gx_state.zmode.testenable = false;
m_gx_state.zmode.updateenable = false;
m_gx_state.zmode.func = ZMode::NEVER;
m_gx_state.raster.cullmode = GenMode::CULL_NONE;
static GXPipelineState s_gx_state;
static StateCache s_gx_state_cache;
// 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);
static void SetupDeviceObjects()
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.f, 0.f, (float)m_target_width, (float)m_target_height);
D3D::context->RSSetViewports(1, &vp);
FramebufferManager::BindEFBRenderTarget();
}
Renderer::~Renderer()
{
TeardownDeviceObjects();
}
void Renderer::SetupDeviceObjects()
{
HRESULT hr;
@ -88,22 +99,22 @@ static void SetupDeviceObjects()
ddesc.StencilEnable = FALSE;
ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[0]);
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[0]);
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
ddesc.DepthEnable = TRUE;
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[1]);
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[1]);
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_clear_depth_states[2]);
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_clear_depth_states[2]);
CHECK(hr == S_OK, "Create depth state for Renderer::ClearScreen");
D3D::SetDebugObjectName(s_clear_depth_states[0],
D3D::SetDebugObjectName(m_clear_depth_states[0],
"depth state for Renderer::ClearScreen (depth buffer disabled)");
D3D::SetDebugObjectName(
s_clear_depth_states[1],
m_clear_depth_states[1],
"depth state for Renderer::ClearScreen (depth buffer enabled, writing enabled)");
D3D::SetDebugObjectName(
s_clear_depth_states[2],
m_clear_depth_states[2],
"depth state for Renderer::ClearScreen (depth buffer enabled, writing disabled)");
D3D11_BLEND_DESC blenddesc;
@ -117,24 +128,24 @@ static void SetupDeviceObjects()
blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
hr = D3D::device->CreateBlendState(&blenddesc, &s_reset_blend_state);
hr = D3D::device->CreateBlendState(&blenddesc, &m_reset_blend_state);
CHECK(hr == S_OK, "Create blend state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(s_reset_blend_state, "blend state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(m_reset_blend_state, "blend state for Renderer::ResetAPIState");
s_clear_blend_states[0] = s_reset_blend_state;
s_reset_blend_state->AddRef();
m_clear_blend_states[0] = m_reset_blend_state;
m_reset_blend_state->AddRef();
blenddesc.RenderTarget[0].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN | D3D11_COLOR_WRITE_ENABLE_BLUE;
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[1]);
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[1]);
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA;
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[2]);
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[2]);
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
blenddesc.RenderTarget[0].RenderTargetWriteMask = 0;
hr = D3D::device->CreateBlendState(&blenddesc, &s_clear_blend_states[3]);
hr = D3D::device->CreateBlendState(&blenddesc, &m_clear_blend_states[3]);
CHECK(hr == S_OK, "Create blend state for Renderer::ClearScreen");
ddesc.DepthEnable = FALSE;
@ -143,41 +154,39 @@ static void SetupDeviceObjects()
ddesc.StencilEnable = FALSE;
ddesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
ddesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
hr = D3D::device->CreateDepthStencilState(&ddesc, &s_reset_depth_state);
hr = D3D::device->CreateDepthStencilState(&ddesc, &m_reset_depth_state);
CHECK(hr == S_OK, "Create depth state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(s_reset_depth_state, "depth stencil state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(m_reset_depth_state, "depth stencil state for Renderer::ResetAPIState");
D3D11_RASTERIZER_DESC rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false,
0, 0.f, 0.f, false, false, false, false);
hr = D3D::device->CreateRasterizerState(&rastdesc, &s_reset_rast_state);
hr = D3D::device->CreateRasterizerState(&rastdesc, &m_reset_rast_state);
CHECK(hr == S_OK, "Create rasterizer state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(s_reset_rast_state, "rasterizer state for Renderer::ResetAPIState");
D3D::SetDebugObjectName(m_reset_rast_state, "rasterizer state for Renderer::ResetAPIState");
s_screenshot_texture = nullptr;
m_screenshot_texture = nullptr;
}
// Kill off all device objects
static void TeardownDeviceObjects()
void Renderer::TeardownDeviceObjects()
{
g_framebuffer_manager.reset();
SAFE_RELEASE(s_clear_blend_states[0]);
SAFE_RELEASE(s_clear_blend_states[1]);
SAFE_RELEASE(s_clear_blend_states[2]);
SAFE_RELEASE(s_clear_blend_states[3]);
SAFE_RELEASE(s_clear_depth_states[0]);
SAFE_RELEASE(s_clear_depth_states[1]);
SAFE_RELEASE(s_clear_depth_states[2]);
SAFE_RELEASE(s_reset_blend_state);
SAFE_RELEASE(s_reset_depth_state);
SAFE_RELEASE(s_reset_rast_state);
SAFE_RELEASE(s_screenshot_texture);
SAFE_RELEASE(s_3d_vision_texture);
s_gx_state_cache.Clear();
SAFE_RELEASE(m_clear_blend_states[0]);
SAFE_RELEASE(m_clear_blend_states[1]);
SAFE_RELEASE(m_clear_blend_states[2]);
SAFE_RELEASE(m_clear_blend_states[3]);
SAFE_RELEASE(m_clear_depth_states[0]);
SAFE_RELEASE(m_clear_depth_states[1]);
SAFE_RELEASE(m_clear_depth_states[2]);
SAFE_RELEASE(m_reset_blend_state);
SAFE_RELEASE(m_reset_depth_state);
SAFE_RELEASE(m_reset_rast_state);
SAFE_RELEASE(m_screenshot_texture);
SAFE_RELEASE(m_3d_vision_texture);
}
static void Create3DVisionTexture(int width, int height)
void Renderer::Create3DVisionTexture(int width, int height)
{
// Create a staging texture for 3D vision with signature information in the last row.
// Nvidia 3D Vision supports full SBS, so there is no loss in resolution during this process.
@ -197,48 +206,20 @@ static void Create3DVisionTexture(int width, int height)
sys_data.SysMemPitch = pitch;
sys_data.pSysMem = memory.get();
s_3d_vision_texture =
m_3d_vision_texture =
D3DTexture2D::Create(width * 2, height + 1, D3D11_BIND_RENDER_TARGET, D3D11_USAGE_DEFAULT,
DXGI_FORMAT_R8G8B8A8_UNORM, 1, 1, &sys_data);
}
Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight())
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
s_last_fullscreen_mode = D3D::GetFullscreenState();
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
SetupDeviceObjects();
// Setup GX pipeline state
for (auto& sampler : s_gx_state.samplers)
sampler.hex = RenderState::GetPointSamplerState().hex;
s_gx_state.zmode.testenable = false;
s_gx_state.zmode.updateenable = false;
s_gx_state.zmode.func = ZMode::NEVER;
s_gx_state.raster.cullmode = GenMode::CULL_NONE;
// 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();
D3D::BeginFrame();
return std::make_unique<DXTexture>(config);
}
Renderer::~Renderer()
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
TeardownDeviceObjects();
D3D::EndFrame();
D3D::Present();
D3D::Close();
return DXStagingTexture::Create(type, config);
}
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
@ -443,8 +424,8 @@ void Renderer::PokeEFB(EFBAccessType type, const EfbPokeData* points, size_t num
}
else // if (type == EFBAccessType::PokeZ)
{
D3D::stateman->PushBlendState(s_clear_blend_states[3]);
D3D::stateman->PushDepthState(s_clear_depth_states[1]);
D3D::stateman->PushBlendState(m_clear_blend_states[3]);
D3D::stateman->PushDepthState(m_clear_depth_states[1]);
D3D11_VIEWPORT vp =
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetTargetWidth(), (float)GetTargetHeight());
@ -526,21 +507,21 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool colorEnable, bool alphaE
ResetAPIState();
if (colorEnable && alphaEnable)
D3D::stateman->PushBlendState(s_clear_blend_states[0]);
D3D::stateman->PushBlendState(m_clear_blend_states[0]);
else if (colorEnable)
D3D::stateman->PushBlendState(s_clear_blend_states[1]);
D3D::stateman->PushBlendState(m_clear_blend_states[1]);
else if (alphaEnable)
D3D::stateman->PushBlendState(s_clear_blend_states[2]);
D3D::stateman->PushBlendState(m_clear_blend_states[2]);
else
D3D::stateman->PushBlendState(s_clear_blend_states[3]);
D3D::stateman->PushBlendState(m_clear_blend_states[3]);
// TODO: Should we enable Z testing here?
// if (!bpmem.zmode.testenable) D3D::stateman->PushDepthState(s_clear_depth_states[0]);
// else
if (zEnable)
D3D::stateman->PushDepthState(s_clear_depth_states[1]);
D3D::stateman->PushDepthState(m_clear_depth_states[1]);
else /*if (!zEnable)*/
D3D::stateman->PushDepthState(s_clear_depth_states[2]);
D3D::stateman->PushDepthState(m_clear_depth_states[2]);
// Update the view port for clearing the picture
TargetRectangle targetRc = Renderer::ConvertEFBRectangle(rc);
@ -600,7 +581,7 @@ void Renderer::ReinterpretPixelData(unsigned int convtype)
void Renderer::SetBlendingState(const BlendingState& state)
{
s_gx_state.blend.hex = state.hex;
m_gx_state.blend.hex = state.hex;
}
// This function has the final picture. We adjust the aspect ratio here.
@ -633,7 +614,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
Renderer::DrawDebugText();
OSD::DrawMessages();
D3D::EndFrame();
g_texture_cache->Cleanup(frameCount);
@ -646,33 +626,33 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
const bool window_resized = CheckForResize();
const bool fullscreen = D3D::GetFullscreenState();
const bool fs_changed = s_last_fullscreen_mode != fullscreen;
const bool fs_changed = m_last_fullscreen_mode != fullscreen;
// Flip/present backbuffer to frontbuffer here
D3D::Present();
// Resize the back buffers NOW to avoid flickering
if (CalculateTargetSize() || window_resized || fs_changed ||
s_last_multisamples != g_ActiveConfig.iMultisamples ||
s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
m_last_multisamples != g_ActiveConfig.iMultisamples ||
m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
{
s_last_multisamples = g_ActiveConfig.iMultisamples;
s_last_fullscreen_mode = fullscreen;
m_last_multisamples = g_ActiveConfig.iMultisamples;
m_last_fullscreen_mode = fullscreen;
PixelShaderCache::InvalidateMSAAShaders();
if (window_resized || fs_changed)
{
// TODO: Aren't we still holding a reference to the back buffer right now?
D3D::Reset();
SAFE_RELEASE(s_screenshot_texture);
SAFE_RELEASE(s_3d_vision_texture);
SAFE_RELEASE(m_screenshot_texture);
SAFE_RELEASE(m_3d_vision_texture);
m_backbuffer_width = D3D::GetBackBufferWidth();
m_backbuffer_height = D3D::GetBackBufferHeight();
}
UpdateDrawRectangle();
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
@ -694,7 +674,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
// begin next frame
RestoreAPIState();
D3D::BeginFrame();
FramebufferManager::BindEFBRenderTarget();
SetViewport();
}
@ -702,9 +681,9 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
void Renderer::ResetAPIState()
{
D3D::stateman->PushBlendState(s_reset_blend_state);
D3D::stateman->PushDepthState(s_reset_depth_state);
D3D::stateman->PushRasterizerState(s_reset_rast_state);
D3D::stateman->PushBlendState(m_reset_blend_state);
D3D::stateman->PushDepthState(m_reset_depth_state);
D3D::stateman->PushRasterizerState(m_reset_rast_state);
}
void Renderer::RestoreAPIState()
@ -719,15 +698,15 @@ void Renderer::RestoreAPIState()
void Renderer::ApplyState()
{
D3D::stateman->PushBlendState(s_gx_state_cache.Get(s_gx_state.blend));
D3D::stateman->PushDepthState(s_gx_state_cache.Get(s_gx_state.zmode));
D3D::stateman->PushRasterizerState(s_gx_state_cache.Get(s_gx_state.raster));
D3D::stateman->PushBlendState(m_state_cache.Get(m_gx_state.blend));
D3D::stateman->PushDepthState(m_state_cache.Get(m_gx_state.zmode));
D3D::stateman->PushRasterizerState(m_state_cache.Get(m_gx_state.raster));
D3D::stateman->SetPrimitiveTopology(
StateCache::GetPrimitiveTopology(s_gx_state.raster.primitive));
FramebufferManager::SetIntegerEFBRenderTarget(s_gx_state.blend.logicopenable);
StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive));
FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable);
for (u32 stage = 0; stage < static_cast<u32>(s_gx_state.samplers.size()); stage++)
D3D::stateman->SetSampler(stage, s_gx_state_cache.Get(s_gx_state.samplers[stage]));
for (u32 stage = 0; stage < static_cast<u32>(m_gx_state.samplers.size()); stage++)
D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage]));
ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer();
@ -746,17 +725,17 @@ void Renderer::RestoreState()
void Renderer::SetRasterizationState(const RasterizationState& state)
{
s_gx_state.raster.hex = state.hex;
m_gx_state.raster.hex = state.hex;
}
void Renderer::SetDepthState(const DepthState& state)
{
s_gx_state.zmode.hex = state.hex;
m_gx_state.zmode.hex = state.hex;
}
void Renderer::SetSamplerState(u32 index, const SamplerState& state)
{
s_gx_state.samplers[index].hex = state.hex;
m_gx_state.samplers[index].hex = state.hex;
}
void Renderer::SetInterlacingMode()
@ -831,7 +810,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
}
else if (g_ActiveConfig.stereo_mode == StereoMode::Nvidia3DVision)
{
if (!s_3d_vision_texture)
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(),
@ -840,7 +819,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
(float)dst.GetWidth(), (float)dst.GetHeight());
// Render to staging texture which is double the width of the backbuffer
D3D::context->OMSetRenderTargets(1, &s_3d_vision_texture->GetRTV(), nullptr);
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,
@ -858,7 +837,7 @@ void Renderer::BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D
// 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);
D3D::context->CopySubresourceRegion(D3D::GetBackBuffer()->GetTex(), 0, 0, 0, 0,
s_3d_vision_texture->GetTex(), 0, &box);
m_3d_vision_texture->GetTex(), 0, &box);
// Restore render target to backbuffer
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);

View File

@ -4,7 +4,9 @@
#pragma once
#include <array>
#include <string>
#include "VideoBackends/D3D/D3DState.h"
#include "VideoCommon/RenderBase.h"
enum class EFBAccessType;
@ -19,6 +21,11 @@ public:
Renderer();
~Renderer() override;
StateCache& GetStateCache() { return m_state_cache; }
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const EFBRectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;
@ -56,7 +63,35 @@ public:
bool CheckForResize();
private:
struct GXPipelineState
{
std::array<SamplerState, 8> samplers;
BlendingState blend;
DepthState zmode;
RasterizationState raster;
};
void SetupDeviceObjects();
void TeardownDeviceObjects();
void Create3DVisionTexture(int width, int height);
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
u32 src_width, u32 src_height, float Gamma);
StateCache m_state_cache;
GXPipelineState m_gx_state;
std::array<ID3D11BlendState*, 4> m_clear_blend_states{};
std::array<ID3D11DepthStencilState*, 3> m_clear_depth_states{};
ID3D11BlendState* m_reset_blend_state = nullptr;
ID3D11DepthStencilState* m_reset_depth_state = nullptr;
ID3D11RasterizerState* m_reset_rast_state = nullptr;
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_mode = false;
};
}

View File

@ -33,11 +33,6 @@ static const size_t MAX_COPY_BUFFERS = 32;
static ID3D11Buffer* s_efbcopycbuf[MAX_COPY_BUFFERS] = {0};
static std::unique_ptr<PSTextureEncoder> g_encoder;
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return std::make_unique<DXTexture>(config);
}
void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
const EFBRectangle& src_rect, bool scale_by_half)
@ -227,7 +222,8 @@ TextureCache::~TextureCache()
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
unsigned int cbuf_id, const float* colmat)
unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity)
{
auto* destination_texture = static_cast<DXTexture*>(entry->texture.get());

View File

@ -19,8 +19,6 @@ public:
~TextureCache();
private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
u64 EncodeToRamFromTexture(u32 address, void* source_texture, u32 SourceW, u32 SourceH,
bool bFromZBuffer, bool bIsIntensityFmt, u32 copyfmt, int bScaleByHalf,
const EFBRectangle& source)
@ -36,7 +34,8 @@ private:
bool scale_by_half) override;
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override;
bool scale_by_half, unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) override;
bool CompileShaders() override { return true; }
void DeleteShaders() override {}

View File

@ -181,6 +181,8 @@ void VideoBackend::Shutdown()
g_texture_cache.reset();
g_renderer.reset();
D3D::Close();
ShutdownShared();
}

View File

@ -14,9 +14,15 @@ void NullTexture::Bind(unsigned int stage)
{
}
void NullTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void NullTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
}
void NullTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
}
@ -25,4 +31,43 @@ void NullTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u
{
}
NullStagingTexture::NullStagingTexture(StagingTextureType type, const TextureConfig& config)
: AbstractStagingTexture(type, config)
{
m_texture_buf.resize(m_texel_size * config.width * config.height);
m_map_pointer = reinterpret_cast<char*>(m_texture_buf.data());
m_map_stride = m_texel_size * config.width;
}
NullStagingTexture::~NullStagingTexture() = default;
void NullStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
m_needs_flush = true;
}
void NullStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
m_needs_flush = true;
}
bool NullStagingTexture::Map()
{
return true;
}
void NullStagingTexture::Unmap()
{
}
void NullStagingTexture::Flush()
{
m_needs_flush = false;
}
} // namespace Null

View File

@ -4,8 +4,11 @@
#pragma once
#include <vector>
#include "Common/CommonTypes.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
namespace Null
@ -18,11 +21,36 @@ public:
void Bind(unsigned int stage) override;
void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
};
class NullStagingTexture final : public AbstractStagingTexture
{
public:
explicit NullStagingTexture(StagingTextureType type, const TextureConfig& config);
~NullStagingTexture();
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;
bool Map() override;
void Unmap() override;
void Flush() override;
private:
std::vector<u8> m_texture_buf;
};
} // namespace Null

View File

@ -4,6 +4,7 @@
#include "Common/Logging/Log.h"
#include "VideoBackends/Null/NullTexture.h"
#include "VideoBackends/Null/Render.h"
#include "VideoCommon/VideoConfig.h"
@ -21,6 +22,17 @@ Renderer::~Renderer()
UpdateActiveConfig();
}
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<NullTexture>(config);
}
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return std::make_unique<NullStagingTexture>(type, config);
}
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
NOTICE_LOG(VIDEO, "RenderText: %s", text.c_str());

View File

@ -14,6 +14,10 @@ public:
Renderer();
~Renderer() override;
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& 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 {}

View File

@ -32,15 +32,10 @@ public:
}
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
bool scale_by_half, unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) override
{
}
private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
{
return std::make_unique<NullTexture>(config);
}
};
} // Null name space

View File

@ -9,7 +9,6 @@
#include "VideoBackends/OGL/FramebufferManager.h"
#include "VideoBackends/OGL/OGLTexture.h"
#include "VideoBackends/OGL/Render.h"
#include "VideoBackends/OGL/SamplerCache.h"
#include "VideoBackends/OGL/TextureCache.h"
@ -36,7 +35,11 @@ GLenum GetGLInternalFormatForTextureFormat(AbstractTextureFormat format, bool st
case AbstractTextureFormat::BPTC:
return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
case AbstractTextureFormat::RGBA8:
return storage ? GL_RGBA8 : GL_RGBA;
case AbstractTextureFormat::BGRA8:
return storage ? GL_RGBA8 : GL_BGRA;
default:
PanicAlert("Unhandled texture format.");
return storage ? GL_RGBA8 : GL_RGBA;
}
}
@ -47,6 +50,8 @@ GLenum GetGLFormatForTextureFormat(AbstractTextureFormat format)
{
case AbstractTextureFormat::RGBA8:
return GL_RGBA;
case AbstractTextureFormat::BGRA8:
return GL_BGRA;
// Compressed texture formats don't use this parameter.
default:
return GL_UNSIGNED_BYTE;
@ -58,12 +63,22 @@ GLenum GetGLTypeForTextureFormat(AbstractTextureFormat format)
switch (format)
{
case AbstractTextureFormat::RGBA8:
case AbstractTextureFormat::BGRA8:
return GL_UNSIGNED_BYTE;
// Compressed texture formats don't use this parameter.
default:
return GL_UNSIGNED_BYTE;
}
}
bool UsePersistentStagingBuffers()
{
// We require ARB_buffer_storage to create the persistent mapped buffer,
// ARB_shader_image_load_store for glMemoryBarrier, and ARB_sync to ensure
// the GPU has finished the copy before reading the buffer from the CPU.
return g_ogl_config.bSupportsGLBufferStorage && g_ogl_config.bSupportsImageLoadStore &&
g_ogl_config.bSupportsGLSync;
}
} // Anonymous namespace
OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
@ -85,7 +100,7 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
if (m_config.rendertarget)
{
// We can't render to compressed formats.
_assert_(!IsCompressedHostTextureFormat(m_config.format));
_assert_(!IsCompressedFormat(m_config.format));
if (!g_ogl_config.bSupportsTextureStorage)
{
@ -100,6 +115,10 @@ OGLTexture::OGLTexture(const TextureConfig& tex_config) : AbstractTexture(tex_co
FramebufferManager::SetFramebuffer(m_framebuffer);
FramebufferManager::FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D_ARRAY, m_texId, 0);
// We broke the framebuffer binding here, and need to restore it, as the CreateTexture
// method is in the base renderer class and can be called by VideoCommon.
FramebufferManager::SetFramebuffer(0);
}
SetStage();
@ -148,89 +167,70 @@ void OGLTexture::Bind(unsigned int stage)
}
}
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapFullImpl()
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
return {};
m_staging_data.reserve(m_config.width * m_config.height * 4);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
OGLTexture::SetStage();
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_data.data()),
m_config.width * 4, m_config.width, m_config.height};
}
std::optional<AbstractTexture::RawTextureInfo> OGLTexture::MapRegionImpl(u32 level, u32 x, u32 y,
u32 width, u32 height)
{
if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL)
return {};
m_staging_data.reserve(m_config.width * m_config.height * 4);
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
if (g_ogl_config.bSupportTextureSubImage)
const OGLTexture* srcentry = static_cast<const OGLTexture*>(src);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
if (g_ogl_config.bSupportsCopySubImage)
{
glGetTextureSubImage(GL_TEXTURE_2D_ARRAY, level, GLint(x), GLint(y), 0, GLsizei(width),
GLsizei(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, GLsizei(width * height * 4),
m_staging_data.data());
glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, src_level, src_rect.left,
src_rect.top, src_layer, m_texId, GL_TEXTURE_2D_ARRAY, dst_level,
dst_rect.left, dst_rect.top, dst_layer, dst_rect.GetWidth(),
dst_rect.GetHeight(), 1);
}
else
{
MapRegionSlow(level, x, y, width, height);
// If it isn't a single leveled/layered texture, we need to update the framebuffer.
bool update_src_framebuffer =
srcentry->m_framebuffer == 0 || srcentry->m_config.layers != 0 || src_level != 0;
bool update_dst_framebuffer = m_framebuffer == 0 || m_config.layers != 0 || dst_level != 0;
if (!m_framebuffer)
glGenFramebuffers(1, &m_framebuffer);
if (!srcentry->m_framebuffer)
glGenFramebuffers(1, &const_cast<OGLTexture*>(srcentry)->m_framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcentry->m_framebuffer);
if (update_src_framebuffer)
{
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, srcentry->m_texId,
src_level, src_layer);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebuffer);
if (update_dst_framebuffer)
{
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texId, dst_level,
dst_layer);
}
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
if (update_src_framebuffer)
{
FramebufferManager::FramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D_ARRAY, srcentry->m_texId, 0);
}
if (update_dst_framebuffer)
{
FramebufferManager::FramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D_ARRAY, m_texId, 0);
}
FramebufferManager::SetFramebuffer(0);
}
OGLTexture::SetStage();
return AbstractTexture::RawTextureInfo{m_staging_data.data(), width * 4, width, height};
}
void OGLTexture::MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height)
{
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texId);
glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_staging_data.data());
// Now copy the region out of the staging data
const u32 partial_stride = width * 4;
std::vector<u8> partial_data;
partial_data.resize(partial_stride * height);
const u32 staging_stride = m_config.width * 4;
const u32 x_offset = x * 4;
auto staging_location = m_staging_data.begin() + staging_stride * y;
auto partial_location = partial_data.begin();
for (size_t i = 0; i < height; ++i)
{
auto starting_location = staging_location + x_offset;
std::copy(starting_location, starting_location + partial_stride, partial_location);
staging_location += staging_stride;
partial_location += partial_stride;
}
// Now swap the region back in for the staging data
m_staging_data.swap(partial_data);
}
void OGLTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void OGLTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
const OGLTexture* srcentry = static_cast<const OGLTexture*>(source);
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight() &&
g_ogl_config.bSupportsCopySubImage)
{
glCopyImageSubData(srcentry->m_texId, GL_TEXTURE_2D_ARRAY, 0, srcrect.left, srcrect.top, 0,
m_texId, GL_TEXTURE_2D_ARRAY, 0, dstrect.left, dstrect.top, 0,
dstrect.GetWidth(), dstrect.GetHeight(), srcentry->m_config.layers);
return;
}
else if (!m_framebuffer)
if (!m_framebuffer)
{
glGenFramebuffers(1, &m_framebuffer);
FramebufferManager::SetFramebuffer(m_framebuffer);
@ -269,7 +269,7 @@ void OGLTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
GLenum gl_internal_format = GetGLInternalFormatForTextureFormat(m_config.format, false);
if (IsCompressedHostTextureFormat(m_config.format))
if (IsCompressedFormat(m_config.format))
{
if (g_ogl_config.bSupportsTextureStorage)
{
@ -315,4 +315,262 @@ void OGLTexture::SetStage()
glActiveTexture(GL_TEXTURE0 + s_ActiveTexture);
}
OGLStagingTexture::OGLStagingTexture(StagingTextureType type, const TextureConfig& config,
GLenum target, GLuint buffer_name, size_t buffer_size,
char* map_ptr, size_t map_stride)
: AbstractStagingTexture(type, config), m_target(target), m_buffer_name(buffer_name),
m_buffer_size(buffer_size)
{
m_map_pointer = map_ptr;
m_map_stride = map_stride;
}
OGLStagingTexture::~OGLStagingTexture()
{
if (m_fence != 0)
glDeleteSync(m_fence);
if (m_map_pointer)
{
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
if (m_buffer_name != 0)
glDeleteBuffers(1, &m_buffer_name);
}
std::unique_ptr<OGLStagingTexture> OGLStagingTexture::Create(StagingTextureType type,
const TextureConfig& config)
{
size_t stride = config.GetStride();
size_t buffer_size = stride * config.height;
GLenum target =
type == StagingTextureType::Readback ? GL_PIXEL_PACK_BUFFER : GL_PIXEL_UNPACK_BUFFER;
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(target, buffer);
// Prefer using buffer_storage where possible. This allows us to skip the map/unmap steps.
char* buffer_ptr;
if (UsePersistentStagingBuffers())
{
GLenum buffer_flags;
GLenum map_flags;
if (type == StagingTextureType::Readback)
{
buffer_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
map_flags = GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT;
}
else if (type == StagingTextureType::Upload)
{
buffer_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT;
}
else
{
buffer_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
map_flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT;
}
glBufferStorage(target, buffer_size, nullptr, buffer_flags);
buffer_ptr =
reinterpret_cast<char*>(glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, buffer_size, map_flags));
_assert_(buffer_ptr != nullptr);
}
else
{
// Otherwise, fallback to mapping the buffer each time.
glBufferData(target, buffer_size, nullptr,
type == StagingTextureType::Readback ? GL_STREAM_READ : GL_STREAM_DRAW);
buffer_ptr = nullptr;
}
glBindBuffer(target, 0);
return std::unique_ptr<OGLStagingTexture>(
new OGLStagingTexture(type, config, target, buffer, buffer_size, buffer_ptr, stride));
}
void OGLStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(m_type == StagingTextureType::Readback);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height);
// Unmap the buffer before writing when not using persistent mappings.
if (!UsePersistentStagingBuffers())
OGLStagingTexture::Unmap();
// Copy from the texture object to the staging buffer.
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_buffer_name);
glPixelStorei(GL_PACK_ROW_LENGTH, m_config.width);
const OGLTexture* gltex = static_cast<const OGLTexture*>(src);
size_t dst_offset = dst_rect.top * m_config.GetStride() + dst_rect.left * m_texel_size;
// If we don't have a FBO associated with this texture, we need to use a slow path.
if (gltex->GetFramebuffer() != 0 && src_layer == 0 && src_level == 0)
{
// This texture has a framebuffer, so we can use glReadPixels().
glBindFramebuffer(GL_READ_FRAMEBUFFER, gltex->GetFramebuffer());
glReadPixels(src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(),
GetGLFormatForTextureFormat(m_config.format),
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(dst_offset));
// Reset both read/draw framebuffers.
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferManager::GetEFBFramebuffer());
}
else
{
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, gltex->GetRawTexIdentifier());
if (g_ogl_config.bSupportsTextureSubImage)
{
glGetTextureSubImage(
GL_TEXTURE_2D_ARRAY, src_level, src_rect.left, src_rect.top, src_layer,
src_rect.GetWidth(), src_rect.GetHeight(), 1,
GetGLFormatForTextureFormat(m_config.format), GetGLTypeForTextureFormat(m_config.format),
static_cast<GLsizei>(m_buffer_size - dst_offset), reinterpret_cast<void*>(dst_offset));
}
else
{
// TODO: Investigate whether it's faster to use glReadPixels() with a framebuffer, since we're
// copying the whole texture, which may waste bandwidth. So we're trading CPU work in creating
// the framebuffer for GPU work in copying potentially redundant texels.
glGetTexImage(GL_TEXTURE_2D_ARRAY, src_level, GetGLFormatForTextureFormat(m_config.format),
GetGLTypeForTextureFormat(m_config.format), nullptr);
}
OGLTexture::SetStage();
}
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
// If we support buffer storage, create a fence for synchronization.
if (UsePersistentStagingBuffers())
{
if (m_fence != 0)
glDeleteSync(m_fence);
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
m_needs_flush = true;
}
void OGLStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect,
AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(m_type == StagingTextureType::Upload);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);
size_t src_offset = src_rect.top * m_config.GetStride() + src_rect.left * m_texel_size;
size_t copy_size = src_rect.GetHeight() * m_config.GetStride();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_buffer_name);
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_config.width);
if (!UsePersistentStagingBuffers())
{
// Unmap the buffer before writing when not using persistent mappings.
if (m_map_pointer)
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
m_map_pointer = nullptr;
}
}
else
{
// Since we're not using coherent mapping, we must flush the range explicitly.
if (m_type == StagingTextureType::Upload)
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, src_offset, copy_size);
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
}
// Copy from the staging buffer to the texture object.
glActiveTexture(GL_TEXTURE9);
glBindTexture(GL_TEXTURE_2D_ARRAY, static_cast<const OGLTexture*>(dst)->GetRawTexIdentifier());
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, dst_rect.left, dst_rect.top, dst_layer,
dst_rect.GetWidth(), dst_rect.GetHeight(), 1,
GetGLFormatForTextureFormat(m_config.format),
GetGLTypeForTextureFormat(m_config.format), reinterpret_cast<void*>(src_offset));
OGLTexture::SetStage();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
// If we support buffer storage, create a fence for synchronization.
if (UsePersistentStagingBuffers())
{
if (m_fence != 0)
glDeleteSync(m_fence);
m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
m_needs_flush = true;
}
void OGLStagingTexture::Flush()
{
// No-op when not using buffer storage, as the transfers happen on Map().
// m_fence will always be zero in this case.
if (m_fence == 0)
{
m_needs_flush = false;
return;
}
glClientWaitSync(m_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(m_fence);
m_fence = 0;
m_needs_flush = false;
}
bool OGLStagingTexture::Map()
{
if (m_map_pointer)
return true;
// Slow path, map the texture, unmap it later.
GLenum flags;
if (m_type == StagingTextureType::Readback)
flags = GL_MAP_READ_BIT;
else if (m_type == StagingTextureType::Upload)
flags = GL_MAP_WRITE_BIT;
else
flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT;
glBindBuffer(m_target, m_buffer_name);
m_map_pointer = reinterpret_cast<char*>(glMapBufferRange(m_target, 0, m_buffer_size, flags));
if (!m_map_pointer)
return false;
return true;
}
void OGLStagingTexture::Unmap()
{
// No-op with persistent mapped buffers.
if (!m_map_pointer || UsePersistentStagingBuffers())
return;
glBindBuffer(m_target, m_buffer_name);
glUnmapBuffer(m_target);
glBindBuffer(m_target, 0);
m_map_pointer = nullptr;
}
} // namespace OGL

View File

@ -8,6 +8,7 @@
#include "Common/GL/GLUtil.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
namespace OGL
@ -20,9 +21,13 @@ public:
void Bind(unsigned int stage) override;
void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
@ -33,14 +38,39 @@ public:
static void SetStage();
private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;
void MapRegionSlow(u32 level, u32 x, u32 y, u32 width, u32 height);
GLuint m_texId;
GLuint m_framebuffer = 0;
std::vector<u8> m_staging_data;
};
class OGLStagingTexture final : public AbstractStagingTexture
{
public:
OGLStagingTexture() = delete;
~OGLStagingTexture();
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;
bool Map() override;
void Unmap() override;
void Flush() override;
static std::unique_ptr<OGLStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);
private:
OGLStagingTexture(StagingTextureType type, const TextureConfig& config, GLenum target,
GLuint buffer_name, size_t buffer_size, char* map_ptr, size_t map_stride);
private:
GLenum m_target;
GLuint m_buffer_name;
size_t m_buffer_size;
GLsync m_fence = 0;
};
} // namespace OGL

View File

@ -458,7 +458,7 @@ Renderer::Renderer()
GLExtensions::Supports("GL_EXT_copy_image") ||
GLExtensions::Supports("GL_OES_copy_image")) &&
!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_COPYIMAGE);
g_ogl_config.bSupportTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image");
g_ogl_config.bSupportsTextureSubImage = GLExtensions::Supports("ARB_get_texture_sub_image");
// Desktop OpenGL supports the binding layout if it supports 420pack
// OpenGL ES 3.1 supports it implicitly without an extension
@ -623,6 +623,8 @@ Renderer::Renderer()
// Compute shaders are core in GL4.3.
g_Config.backend_info.bSupportsComputeShaders = true;
if (GLExtensions::Version() >= 450)
g_ogl_config.bSupportsTextureSubImage = true;
}
else
{
@ -814,6 +816,17 @@ void Renderer::Init()
OpenGL_CreateAttributelessVAO();
}
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<OGLTexture>(config);
}
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return OGLStagingTexture::Create(type, config);
}
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u);

View File

@ -58,7 +58,7 @@ struct VideoConfig
bool bSupportsImageLoadStore;
bool bSupportsAniso;
bool bSupportsBitfield;
bool bSupportTextureSubImage;
bool bSupportsTextureSubImage;
const char* gl_vendor;
const char* gl_renderer;
@ -77,6 +77,10 @@ public:
void Init();
void Shutdown();
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
void SetBlendingState(const BlendingState& state) override;
void SetScissorRect(const EFBRectangle& rc) override;
void SetRasterizationState(const RasterizationState& state) override;

View File

@ -26,17 +26,45 @@
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/TextureConverterShaderGen.h"
#include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h"
namespace OGL
{
//#define TIME_TEXTURE_DECODING 1
constexpr const char* vertex_program =
"out vec3 %c_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 copy_position;\n" // left, top, right, bottom
"void main()\n"
"{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" %c_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, "
"0).xy), 0.0);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
"}\n";
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return std::make_unique<OGLTexture>(config);
}
constexpr const char* geometry_program = "layout(triangles) in;\n"
"layout(triangle_strip, max_vertices = 6) out;\n"
"in vec3 v_uv0[3];\n"
"out vec3 f_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"void main()\n"
"{\n"
" int layers = textureSize(samp9, 0).z;\n"
" for (int layer = 0; layer < layers; ++layer) {\n"
" for (int i = 0; i < 3; ++i) {\n"
" f_uv0 = vec3(v_uv0[i].xy, layer);\n"
" gl_Position = gl_in[i].gl_Position;\n"
" gl_Layer = layer;\n"
" EmitVertex();\n"
" }\n"
" EndPrimitive();\n"
" }\n"
"}\n";
//#define TIME_TEXTURE_DECODING 1
void TextureCache::CopyEFB(u8* dst, const EFBCopyParams& params, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
@ -129,101 +157,22 @@ bool TextureCache::CompileShaders()
" ocol0 = texcol;\n"
"}\n";
constexpr const char* color_matrix_program =
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 colmat[7];\n"
"in vec3 f_uv0;\n"
"out vec4 ocol0;\n"
"\n"
"void main(){\n"
" vec4 texcol = texture(samp9, f_uv0);\n"
" texcol = floor(texcol * colmat[5]) * colmat[6];\n"
" ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
"}\n";
constexpr const char* depth_matrix_program =
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 colmat[5];\n"
"in vec3 f_uv0;\n"
"out vec4 ocol0;\n"
"\n"
"void main(){\n"
" vec4 texcol = texture(samp9, vec3(f_uv0.xy, %s));\n"
" int depth = int(texcol.x * 16777216.0);\n"
// Convert to Z24 format
" ivec4 workspace;\n"
" workspace.r = (depth >> 16) & 255;\n"
" workspace.g = (depth >> 8) & 255;\n"
" workspace.b = depth & 255;\n"
// Convert to Z4 format
" workspace.a = (depth >> 16) & 0xF0;\n"
// Normalize components to [0.0..1.0]
" texcol = vec4(workspace) / 255.0;\n"
" ocol0 = texcol * mat4(colmat[0], colmat[1], colmat[2], colmat[3]) + colmat[4];\n"
"}\n";
constexpr const char* vertex_program =
"out vec3 %s_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"uniform vec4 copy_position;\n" // left, top, right, bottom
"void main()\n"
"{\n"
" vec2 rawpos = vec2(gl_VertexID&1, gl_VertexID&2);\n"
" %s_uv0 = vec3(mix(copy_position.xy, copy_position.zw, rawpos) / vec2(textureSize(samp9, "
"0).xy), 0.0);\n"
" gl_Position = vec4(rawpos*2.0-1.0, 0.0, 1.0);\n"
"}\n";
const std::string geo_program = g_ActiveConfig.stereo_mode != StereoMode::Off ?
"layout(triangles) in;\n"
"layout(triangle_strip, max_vertices = 6) out;\n"
"in vec3 v_uv0[3];\n"
"out vec3 f_uv0;\n"
"SAMPLER_BINDING(9) uniform sampler2DArray samp9;\n"
"void main()\n"
"{\n"
" int layers = textureSize(samp9, 0).z;\n"
" for (int layer = 0; layer < layers; ++layer) {\n"
" for (int i = 0; i < 3; ++i) {\n"
" f_uv0 = vec3(v_uv0[i].xy, layer);\n"
" gl_Position = gl_in[i].gl_Position;\n"
" gl_Layer = layer;\n"
" EmitVertex();\n"
" }\n"
" EndPrimitive();\n"
" }\n"
"}\n" :
"";
const char* prefix = geo_program.empty() ? "f" : "v";
const char* depth_layer = g_ActiveConfig.bStereoEFBMonoDepth ? "0.0" : "f_uv0.z";
std::string geo_program = "";
char prefix = 'f';
if (g_ActiveConfig.stereo_mode != StereoMode::Off)
{
geo_program = geometry_program;
prefix = 'v';
}
if (!ProgramShaderCache::CompileShader(m_colorCopyProgram,
StringFromFormat(vertex_program, prefix, prefix),
color_copy_program, geo_program) ||
!ProgramShaderCache::CompileShader(m_colorMatrixProgram,
StringFromFormat(vertex_program, prefix, prefix),
color_matrix_program, geo_program) ||
!ProgramShaderCache::CompileShader(
m_depthMatrixProgram, StringFromFormat(vertex_program, prefix, prefix),
StringFromFormat(depth_matrix_program, depth_layer), geo_program))
color_copy_program, geo_program))
{
return false;
}
m_colorMatrixUniform = glGetUniformLocation(m_colorMatrixProgram.glprogid, "colmat");
m_depthMatrixUniform = glGetUniformLocation(m_depthMatrixProgram.glprogid, "colmat");
m_color_cbuf_id = UINT_MAX;
m_depth_cbuf_id = UINT_MAX;
m_colorCopyPositionUniform = glGetUniformLocation(m_colorCopyProgram.glprogid, "copy_position");
m_colorMatrixPositionUniform =
glGetUniformLocation(m_colorMatrixProgram.glprogid, "copy_position");
m_depthCopyPositionUniform = glGetUniformLocation(m_depthMatrixProgram.glprogid, "copy_position");
std::string palette_shader =
R"GLSL(
@ -325,8 +274,9 @@ bool TextureCache::CompileShaders()
void TextureCache::DeleteShaders()
{
m_colorMatrixProgram.Destroy();
m_depthMatrixProgram.Destroy();
for (auto& it : m_efb_copy_programs)
it.second.shader.Destroy();
m_efb_copy_programs.clear();
if (g_ActiveConfig.backend_info.bSupportsPaletteConversion)
for (auto& shader : m_palette_shaders)
@ -387,16 +337,16 @@ void main()
void TextureCache::CreateTextureDecodingResources()
{
static const GLenum gl_view_types[TextureConversionShader::BUFFER_FORMAT_COUNT] = {
static const GLenum gl_view_types[TextureConversionShaderTiled::BUFFER_FORMAT_COUNT] = {
GL_R8UI, // BUFFER_FORMAT_R8_UINT
GL_R16UI, // BUFFER_FORMAT_R16_UINT
GL_RG32UI, // BUFFER_FORMAT_R32G32_UINT
GL_RGBA8UI, // BUFFER_FORMAT_RGBA8_UINT
};
glGenTextures(TextureConversionShader::BUFFER_FORMAT_COUNT,
glGenTextures(TextureConversionShaderTiled::BUFFER_FORMAT_COUNT,
m_texture_decoding_buffer_views.data());
for (size_t i = 0; i < TextureConversionShader::BUFFER_FORMAT_COUNT; i++)
for (size_t i = 0; i < TextureConversionShaderTiled::BUFFER_FORMAT_COUNT; i++)
{
glBindTexture(GL_TEXTURE_BUFFER, m_texture_decoding_buffer_views[i]);
glTexBuffer(GL_TEXTURE_BUFFER, gl_view_types[i], m_palette_stream_buffer->m_buffer);
@ -405,7 +355,7 @@ void TextureCache::CreateTextureDecodingResources()
void TextureCache::DestroyTextureDecodingResources()
{
glDeleteTextures(TextureConversionShader::BUFFER_FORMAT_COUNT,
glDeleteTextures(TextureConversionShaderTiled::BUFFER_FORMAT_COUNT,
m_texture_decoding_buffer_views.data());
m_texture_decoding_buffer_views.fill(0);
m_texture_decoding_program_info.clear();
@ -419,7 +369,7 @@ bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TLUTFormat pal
return iter->second.valid;
TextureDecodingProgramInfo info;
info.base_info = TextureConversionShader::GetDecodingShaderInfo(format);
info.base_info = TextureConversionShaderTiled::GetDecodingShaderInfo(format);
if (!info.base_info)
{
m_texture_decoding_program_info.emplace(key, info);
@ -427,7 +377,7 @@ bool TextureCache::SupportsGPUTextureDecode(TextureFormat format, TLUTFormat pal
}
std::string shader_source =
TextureConversionShader::GenerateDecodingShader(format, palette_format, APIType::OpenGL);
TextureConversionShaderTiled::GenerateDecodingShader(format, palette_format, APIType::OpenGL);
if (shader_source.empty())
{
m_texture_decoding_program_info.emplace(key, info);
@ -467,7 +417,7 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
// Copy to GPU-visible buffer, aligned to the data type.
auto info = iter->second;
u32 bytes_per_buffer_elem =
TextureConversionShader::GetBytesPerBufferElement(info.base_info->buffer_format);
TextureConversionShaderTiled::GetBytesPerBufferElement(info.base_info->buffer_format);
// Only copy palette if it is required.
bool has_palette = info.base_info->palette_size > 0;
@ -520,7 +470,7 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
}
auto dispatch_groups =
TextureConversionShader::GetDispatchCount(info.base_info, aligned_width, aligned_height);
TextureConversionShaderTiled::GetDispatchCount(info.base_info, aligned_width, aligned_height);
glBindImageTexture(0, static_cast<OGLTexture*>(entry->texture.get())->GetRawTexIdentifier(),
dst_level, GL_TRUE, 0, GL_WRITE_ONLY, GL_RGBA8);
glDispatchCompute(dispatch_groups.first, dispatch_groups.second, 1);
@ -536,7 +486,8 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
unsigned int cbuf_id, const float* colmat)
unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity)
{
auto* destination_texture = static_cast<OGLTexture*>(entry->texture.get());
g_renderer->ResetAPIState(); // reset any game specific settings
@ -559,26 +510,36 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
glViewport(0, 0, destination_texture->GetConfig().width, destination_texture->GetConfig().height);
GLuint uniform_location;
if (is_depth_copy)
auto uid = TextureConversionShaderGen::GetShaderUid(dst_format, is_depth_copy, is_intensity,
scale_by_half);
auto it = m_efb_copy_programs.emplace(uid, EFBCopyShader());
EFBCopyShader& shader = it.first->second;
bool created = it.second;
if (created)
{
m_depthMatrixProgram.Bind();
if (m_depth_cbuf_id != cbuf_id)
glUniform4fv(m_depthMatrixUniform, 5, colmat);
m_depth_cbuf_id = cbuf_id;
uniform_location = m_depthCopyPositionUniform;
}
else
{
m_colorMatrixProgram.Bind();
if (m_color_cbuf_id != cbuf_id)
glUniform4fv(m_colorMatrixUniform, 7, colmat);
m_color_cbuf_id = cbuf_id;
uniform_location = m_colorMatrixPositionUniform;
ShaderCode code = TextureConversionShaderGen::GenerateShader(APIType::OpenGL, uid.GetUidData());
std::string geo_program = "";
char prefix = 'f';
if (g_ActiveConfig.stereo_mode != StereoMode::Off)
{
geo_program = geometry_program;
prefix = 'v';
}
ProgramShaderCache::CompileShader(shader.shader,
StringFromFormat(vertex_program, prefix, prefix),
code.GetBuffer(), geo_program);
shader.position_uniform = glGetUniformLocation(shader.shader.glprogid, "copy_position");
}
shader.shader.Bind();
TargetRectangle R = g_renderer->ConvertEFBRectangle(src_rect);
glUniform4f(uniform_location, static_cast<float>(R.left), static_cast<float>(R.top),
glUniform4f(shader.position_uniform, static_cast<float>(R.left), static_cast<float>(R.top),
static_cast<float>(R.right), static_cast<float>(R.bottom));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

View File

@ -13,6 +13,7 @@
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/TextureConversionShader.h"
#include "VideoCommon/TextureConverterShaderGen.h"
#include "VideoCommon/VideoCommon.h"
class AbstractTexture;
@ -49,7 +50,7 @@ private:
struct TextureDecodingProgramInfo
{
const TextureConversionShader::DecodingShaderInfo* base_info = nullptr;
const TextureConversionShaderTiled::DecodingShaderInfo* base_info = nullptr;
SHADER program;
GLint uniform_dst_size = -1;
GLint uniform_src_size = -1;
@ -59,7 +60,6 @@ private:
bool valid = false;
};
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;
@ -68,7 +68,8 @@ private:
bool scale_by_half) override;
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override;
bool scale_by_half, unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) override;
bool CompileShaders() override;
void DeleteShaders() override;
@ -79,26 +80,23 @@ private:
void CreateTextureDecodingResources();
void DestroyTextureDecodingResources();
SHADER m_colorCopyProgram;
SHADER m_colorMatrixProgram;
SHADER m_depthMatrixProgram;
GLuint m_colorMatrixUniform;
GLuint m_depthMatrixUniform;
GLuint m_colorCopyPositionUniform;
GLuint m_colorMatrixPositionUniform;
GLuint m_depthCopyPositionUniform;
struct EFBCopyShader
{
SHADER shader;
GLuint position_uniform;
};
u32 m_color_cbuf_id;
u32 m_depth_cbuf_id;
std::map<TextureConversionShaderGen::TCShaderUid, EFBCopyShader> m_efb_copy_programs;
SHADER m_colorCopyProgram;
GLuint m_colorCopyPositionUniform;
std::array<PaletteShader, 3> m_palette_shaders;
std::unique_ptr<StreamBuffer> m_palette_stream_buffer;
GLuint m_palette_resolv_texture = 0;
std::map<std::pair<u32, u32>, TextureDecodingProgramInfo> m_texture_decoding_program_info;
std::array<GLuint, TextureConversionShader::BUFFER_FORMAT_COUNT> m_texture_decoding_buffer_views;
std::array<GLuint, TextureConversionShaderTiled::BUFFER_FORMAT_COUNT>
m_texture_decoding_buffer_views;
};
bool SaveTexture(const std::string& filename, u32 textarget, u32 tex, int virtual_width,
int virtual_height, unsigned int level);
}

View File

@ -34,9 +34,8 @@ namespace TextureConverter
{
using OGL::TextureCache;
static GLuint s_texConvFrameBuffer[2] = {0, 0};
static GLuint s_srcTexture = 0; // for decoding from RAM
static GLuint s_dstTexture = 0; // for encoding to RAM
std::unique_ptr<AbstractTexture> s_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> s_encoding_readback_texture;
const int renderBufferWidth = EFB_WIDTH * 4;
const int renderBufferHeight = 1024;
@ -49,15 +48,14 @@ struct EncodingProgram
};
static std::map<EFBCopyParams, EncodingProgram> s_encoding_programs;
static GLuint s_PBO = 0; // for readback with different strides
static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
{
auto iter = s_encoding_programs.find(params);
if (iter != s_encoding_programs.end())
return iter->second;
const char* shader = TextureConversionShader::GenerateEncodingShader(params, APIType::OpenGL);
const char* shader =
TextureConversionShaderTiled::GenerateEncodingShader(params, APIType::OpenGL);
#if defined(_DEBUG) || defined(DEBUGFAST)
if (g_ActiveConfig.iLog & CONF_SAVESHADERS && shader)
@ -87,42 +85,21 @@ static EncodingProgram& GetOrCreateEncodingShader(const EFBCopyParams& params)
void Init()
{
glGenFramebuffers(2, s_texConvFrameBuffer);
glActiveTexture(GL_TEXTURE9);
glGenTextures(1, &s_srcTexture);
glBindTexture(GL_TEXTURE_2D, s_srcTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glGenTextures(1, &s_dstTexture);
glBindTexture(GL_TEXTURE_2D, s_dstTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderBufferWidth, renderBufferHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, s_dstTexture, 0);
FramebufferManager::SetFramebuffer(0);
glGenBuffers(1, &s_PBO);
TextureConfig config(renderBufferWidth, renderBufferHeight, 1, 1, AbstractTextureFormat::BGRA8,
true);
s_encoding_render_texture = g_renderer->CreateTexture(config);
s_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
}
void Shutdown()
{
glDeleteTextures(1, &s_srcTexture);
glDeleteTextures(1, &s_dstTexture);
glDeleteBuffers(1, &s_PBO);
glDeleteFramebuffers(2, s_texConvFrameBuffer);
s_encoding_readback_texture.reset();
s_encoding_render_texture.reset();
for (auto& program : s_encoding_programs)
program.second.program.Destroy();
s_encoding_programs.clear();
s_srcTexture = 0;
s_dstTexture = 0;
s_PBO = 0;
s_texConvFrameBuffer[0] = 0;
s_texConvFrameBuffer[1] = 0;
}
// dst_line_size, writeStride in bytes
@ -130,9 +107,8 @@ void Shutdown()
static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line_size,
u32 dstHeight, u32 writeStride, bool linearFilter, float y_scale)
{
// switch to texture converter frame buffer
// attach render buffer as color destination
FramebufferManager::SetFramebuffer(s_texConvFrameBuffer[0]);
FramebufferManager::SetFramebuffer(
static_cast<OGLTexture*>(s_encoding_render_texture.get())->GetFramebuffer());
OpenGL_BindAttributelessVAO();
@ -153,33 +129,13 @@ static void EncodeToRamUsingShader(GLuint srcTexture, u8* destAddr, u32 dst_line
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
int dstSize = dst_line_size * dstHeight;
MathUtil::Rectangle<int> copy_rect(0, 0, dst_line_size / 4, dstHeight);
s_encoding_readback_texture->CopyFromTexture(s_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
s_encoding_readback_texture->ReadTexels(copy_rect, destAddr, writeStride);
// When the dst_line_size and writeStride are the same, we could use glReadPixels directly to RAM.
// But instead we always copy the data via a PBO, because macOS inexplicably prefers this (most
// noticeably in the Super Mario Sunshine transition).
glBindBuffer(GL_PIXEL_PACK_BUFFER, s_PBO);
glBufferData(GL_PIXEL_PACK_BUFFER, dstSize, nullptr, GL_STREAM_READ);
glReadPixels(0, 0, (GLsizei)(dst_line_size / 4), (GLsizei)dstHeight, GL_BGRA, GL_UNSIGNED_BYTE,
nullptr);
u8* pbo = (u8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, dstSize, GL_MAP_READ_BIT);
if (dst_line_size == writeStride)
{
memcpy(destAddr, pbo, dst_line_size * dstHeight);
}
else
{
for (size_t i = 0; i < dstHeight; ++i)
{
memcpy(destAddr, pbo, dst_line_size);
pbo += dst_line_size;
destAddr += writeStride;
}
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
FramebufferManager::SetFramebuffer(0);
OGLTexture::SetStage();
}
void EncodeToRamFromTexture(u8* dest_ptr, const EFBCopyParams& params, u32 native_width,

View File

@ -14,6 +14,7 @@
#include "VideoBackends/Software/EfbCopy.h"
#include "VideoBackends/Software/EfbInterface.h"
#include "VideoBackends/Software/SWOGLWindow.h"
#include "VideoBackends/Software/SWTexture.h"
#include "VideoCommon/BoundingBox.h"
#include "VideoCommon/OnScreenDisplay.h"
@ -34,6 +35,17 @@ void SWRenderer::Shutdown()
UpdateActiveConfig();
}
std::unique_ptr<AbstractTexture> SWRenderer::CreateTexture(const TextureConfig& config)
{
return std::make_unique<SW::SWTexture>(config);
}
std::unique_ptr<AbstractStagingTexture>
SWRenderer::CreateStagingTexture(StagingTextureType type, const TextureConfig& config)
{
return std::make_unique<SW::SWStagingTexture>(type, config);
}
void SWRenderer::RenderText(const std::string& pstr, int left, int top, u32 color)
{
SWOGLWindow::s_instance->PrintText(pstr, left, top, color);

View File

@ -16,6 +16,10 @@ public:
static void Init();
static void Shutdown();
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& 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 {}

View File

@ -5,6 +5,7 @@
#include "VideoBackends/Software/SWTexture.h"
#include <cstring>
#include "Common/Assert.h"
#include "VideoBackends/Software/CopyRegion.h"
@ -21,7 +22,31 @@ struct Pixel
u8 a;
};
#pragma pack(pop)
void CopyTextureData(const TextureConfig& src_config, const u8* src_ptr, u32 src_x, u32 src_y,
u32 width, u32 height, const TextureConfig& dst_config, u8* dst_ptr, u32 dst_x,
u32 dst_y)
{
size_t texel_size = AbstractTexture::GetTexelSizeForFormat(src_config.format);
size_t src_stride = src_config.GetStride();
size_t src_offset =
static_cast<size_t>(src_y) * src_stride + static_cast<size_t>(src_x) * texel_size;
size_t dst_stride = dst_config.GetStride();
size_t dst_offset =
static_cast<size_t>(dst_y) * dst_stride + static_cast<size_t>(dst_x) * texel_size;
size_t copy_len = static_cast<size_t>(width) * texel_size;
src_ptr += src_offset;
dst_ptr += dst_offset;
for (u32 i = 0; i < height; i++)
{
std::memcpy(dst_ptr, src_ptr, copy_len);
src_ptr += src_stride;
dst_ptr += dst_stride;
}
}
}
SWTexture::SWTexture(const TextureConfig& tex_config) : AbstractTexture(tex_config)
{
m_data.resize(tex_config.width * tex_config.height * 4);
@ -31,9 +56,19 @@ void SWTexture::Bind(unsigned int stage)
{
}
void SWTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
void SWTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
_assert_(src_level == 0 && src_layer == 0 && dst_layer == 0 && dst_level == 0);
CopyTextureData(src->GetConfig(), static_cast<const SWTexture*>(src)->m_data.data(),
src_rect.left, src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config,
m_data.data(), dst_rect.left, dst_rect.top);
}
void SWTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
const SWTexture* software_source_texture = static_cast<const SWTexture*>(source);
@ -72,10 +107,49 @@ u8* SWTexture::GetData()
return m_data.data();
}
std::optional<AbstractTexture::RawTextureInfo> SWTexture::MapFullImpl()
SWStagingTexture::SWStagingTexture(StagingTextureType type, const TextureConfig& config)
: AbstractStagingTexture(type, config)
{
return AbstractTexture::RawTextureInfo{GetData(), m_config.width * 4, m_config.width,
m_config.height};
m_data.resize(m_texel_size * config.width * config.height);
m_map_pointer = reinterpret_cast<char*>(m_data.data());
m_map_stride = m_texel_size * config.width;
}
SWStagingTexture::~SWStagingTexture() = default;
void SWStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(src_level == 0 && src_layer == 0);
CopyTextureData(src->GetConfig(), static_cast<const SWTexture*>(src)->GetData(), src_rect.left,
src_rect.top, src_rect.GetWidth(), src_rect.GetHeight(), m_config, m_data.data(),
dst_rect.left, dst_rect.top);
m_needs_flush = true;
}
void SWStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(dst_level == 0 && dst_layer == 0);
CopyTextureData(m_config, m_data.data(), src_rect.left, src_rect.top, src_rect.GetWidth(),
src_rect.GetHeight(), dst->GetConfig(), static_cast<SWTexture*>(dst)->GetData(),
dst_rect.left, dst_rect.top);
m_needs_flush = true;
}
bool SWStagingTexture::Map()
{
return true;
}
void SWStagingTexture::Unmap()
{
}
void SWStagingTexture::Flush()
{
m_needs_flush = false;
}
} // namespace SW

View File

@ -8,6 +8,7 @@
#include "Common/CommonTypes.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
namespace SW
@ -20,9 +21,13 @@ public:
void Bind(unsigned int stage) override;
void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
@ -30,8 +35,27 @@ public:
u8* GetData();
private:
std::optional<RawTextureInfo> MapFullImpl() override;
std::vector<u8> m_data;
};
class SWStagingTexture final : public AbstractStagingTexture
{
public:
explicit SWStagingTexture(StagingTextureType type, const TextureConfig& config);
~SWStagingTexture();
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;
bool Map() override;
void Unmap() override;
void Flush() override;
private:
std::vector<u8> m_data;
};

View File

@ -25,13 +25,9 @@ public:
}
private:
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override
{
return std::make_unique<SWTexture>(config);
}
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override
bool scale_by_half, unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) override
{
// TODO: If we ever want to "fake" vram textures, we would need to implement this
}

View File

@ -1441,6 +1441,8 @@ void EncodeEfbCopy(u8* dst, const EFBCopyParams& params, u32 native_width, u32 b
case PEControl::Z24:
EncodeZ24halfscale(dst, src, params.copy_format);
break;
default:
break;
}
}
else
@ -1459,6 +1461,8 @@ void EncodeEfbCopy(u8* dst, const EFBCopyParams& params, u32 native_width, u32 b
case PEControl::Z24:
EncodeZ24(dst, src, params.copy_format);
break;
default:
break;
}
}
}

View File

@ -11,7 +11,6 @@ set(SRCS
ShaderCompiler.cpp
StateTracker.cpp
StagingBuffer.cpp
StagingTexture2D.cpp
StreamBuffer.cpp
SwapChain.cpp
Texture2D.cpp

View File

@ -371,6 +371,12 @@ void CommandBufferManager::OnCommandBufferExecuted(size_t index)
FrameResources& resources = m_frame_resources[index];
// Fire fence tracking callbacks.
for (auto iter = m_fence_point_callbacks.begin(); iter != m_fence_point_callbacks.end();)
{
auto backup_iter = iter++;
backup_iter->second.second(resources.fence);
}
for (const auto& iter : m_fence_point_callbacks)
iter.second.second(resources.fence);

View File

@ -16,7 +16,6 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
@ -698,7 +697,7 @@ u32 FramebufferManager::PeekEFBColor(u32 x, u32 y)
return 0;
u32 value;
m_color_readback_texture->ReadTexel(x, y, &value, sizeof(value));
m_color_readback_texture->ReadTexel(x, y, &value);
return value;
}
@ -711,7 +710,6 @@ bool FramebufferManager::PopulateColorReadbackTexture()
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
Texture2D* src_texture = m_efb_color_texture.get();
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
if (GetEFBSamples() > 1)
src_texture = ResolveEFBColorTexture(src_region);
@ -750,9 +748,9 @@ bool FramebufferManager::PopulateColorReadbackTexture()
// Copy from EFB or copy texture to staging texture.
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_color_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
EFB_HEIGHT, 0, 0);
static_cast<VKStagingTexture*>(m_color_readback_texture.get())
->CopyFromTexture(src_texture, m_color_readback_texture->GetConfig().GetRect(), 0, 0,
m_color_readback_texture->GetConfig().GetRect());
// Restore original layout if we used the EFB as a source.
if (src_texture == m_efb_color_texture.get())
@ -762,12 +760,7 @@ bool FramebufferManager::PopulateColorReadbackTexture()
}
// Wait until the copy is complete.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Map to host memory.
if (!m_color_readback_texture->IsMapped() && !m_color_readback_texture->Map())
return false;
m_color_readback_texture->Flush();
m_color_readback_texture_valid = true;
return true;
}
@ -778,7 +771,7 @@ float FramebufferManager::PeekEFBDepth(u32 x, u32 y)
return 0.0f;
float value;
m_depth_readback_texture->ReadTexel(x, y, &value, sizeof(value));
m_depth_readback_texture->ReadTexel(x, y, &value);
return value;
}
@ -791,12 +784,10 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
// Issue a copy from framebuffer -> copy texture if we have >1xIR or MSAA on.
VkRect2D src_region = {{0, 0}, {GetEFBWidth(), GetEFBHeight()}};
Texture2D* src_texture = m_efb_depth_texture.get();
VkImageAspectFlags src_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
if (GetEFBSamples() > 1)
{
// EFB depth resolves are written out as color textures
src_texture = ResolveEFBDepthTexture(src_region);
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
if (GetEFBWidth() != EFB_WIDTH || GetEFBHeight() != EFB_HEIGHT)
{
@ -828,15 +819,14 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
// Use this as a source texture now.
src_texture = m_depth_copy_texture.get();
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
// Copy from EFB or copy texture to staging texture.
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_depth_readback_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
src_texture->GetImage(), src_aspect, 0, 0, EFB_WIDTH,
EFB_HEIGHT, 0, 0);
static_cast<VKStagingTexture*>(m_depth_readback_texture.get())
->CopyFromTexture(src_texture, m_depth_readback_texture->GetConfig().GetRect(), 0, 0,
m_depth_readback_texture->GetConfig().GetRect());
// Restore original layout if we used the EFB as a source.
if (src_texture == m_efb_depth_texture.get())
@ -846,12 +836,7 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
}
// Wait until the copy is complete.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Map to host memory.
if (!m_depth_readback_texture->IsMapped() && !m_depth_readback_texture->Map())
return false;
m_depth_readback_texture->Flush();
m_depth_readback_texture_valid = true;
return true;
}
@ -1011,32 +996,27 @@ bool FramebufferManager::CreateReadbackTextures()
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
m_color_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_COLOR_TEXTURE_FORMAT);
if (!m_color_copy_texture || !m_color_readback_texture)
{
ERROR_LOG(VIDEO, "Failed to create EFB color readback texture");
return false;
}
m_depth_copy_texture =
Texture2D::Create(EFB_WIDTH, EFB_HEIGHT, 1, 1, EFB_DEPTH_AS_COLOR_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
if (!m_depth_copy_texture || !m_depth_readback_texture)
if (!m_color_copy_texture || !m_depth_copy_texture)
{
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
ERROR_LOG(VIDEO, "Failed to create EFB copy textures");
return false;
}
// With Vulkan, we can leave these textures mapped and use invalidate/flush calls instead.
if (!m_color_readback_texture->Map() || !m_depth_readback_texture->Map())
TextureConfig readback_texture_config(EFB_WIDTH, EFB_HEIGHT, 1, 1, AbstractTextureFormat::RGBA8,
false);
m_color_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
m_depth_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Mutable, readback_texture_config);
if (!m_color_readback_texture || !m_depth_readback_texture)
{
ERROR_LOG(VIDEO, "Failed to map EFB readback textures");
ERROR_LOG(VIDEO, "Failed to create EFB readback textures");
return false;
}
@ -1113,7 +1093,7 @@ void FramebufferManager::PokeEFBColor(u32 x, u32 y, u32 color)
// Update the peek cache if it's valid, since we know the color of the pixel now.
if (m_color_readback_texture_valid)
m_color_readback_texture->WriteTexel(x, y, &color, sizeof(color));
m_color_readback_texture->WriteTexel(x, y, &color);
}
void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
@ -1126,7 +1106,7 @@ void FramebufferManager::PokeEFBDepth(u32 x, u32 y, float depth)
// Update the peek cache if it's valid, since we know the color of the pixel now.
if (m_depth_readback_texture_valid)
m_depth_readback_texture->WriteTexel(x, y, &depth, sizeof(depth));
m_depth_readback_texture->WriteTexel(x, y, &depth);
}
void FramebufferManager::CreatePokeVertices(std::vector<EFBPokeVertex>* destination_list, u32 x,

View File

@ -13,9 +13,10 @@
#include "VideoCommon/FramebufferManagerBase.h"
#include "VideoCommon/RenderState.h"
class AbstractStagingTexture;
namespace Vulkan
{
class StagingTexture2D;
class StateTracker;
class StreamBuffer;
class Texture2D;
@ -138,8 +139,8 @@ private:
VkFramebuffer m_depth_copy_framebuffer = VK_NULL_HANDLE;
// CPU-side EFB readback texture
std::unique_ptr<StagingTexture2D> m_color_readback_texture;
std::unique_ptr<StagingTexture2D> m_depth_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_color_readback_texture;
std::unique_ptr<AbstractStagingTexture> m_depth_readback_texture;
bool m_color_readback_texture_valid = false;
bool m_depth_readback_texture_valid = false;

View File

@ -159,6 +159,17 @@ void Renderer::DestroySemaphores()
}
}
std::unique_ptr<AbstractTexture> Renderer::CreateTexture(const TextureConfig& config)
{
return VKTexture::Create(config);
}
std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTextureType type,
const TextureConfig& config)
{
return VKStagingTexture::Create(type, config);
}
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
{
u32 backbuffer_width = m_swap_chain->GetWidth();

View File

@ -32,6 +32,10 @@ public:
static Renderer* GetInstance();
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) override;
SwapChain* GetSwapChain() const { return m_swap_chain.get(); }
BoundingBox* GetBoundingBox() const { return m_bounding_box.get(); }
bool Initialize();

View File

@ -59,11 +59,11 @@ public:
static std::unique_ptr<StagingBuffer> Create(STAGING_BUFFER_TYPE type, VkDeviceSize size,
VkBufferUsageFlags usage);
protected:
// Allocates the resources needed to create a staging buffer.
static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage,
VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent);
protected:
STAGING_BUFFER_TYPE m_type;
VkBuffer m_buffer;
VkDeviceMemory m_memory;

View File

@ -1,164 +0,0 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "Common/Assert.h"
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
namespace Vulkan
{
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
VkDeviceSize size, bool coherent, u32 width, u32 height,
VkFormat format, u32 stride)
: StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height),
m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride)
{
}
StagingTexture2D::~StagingTexture2D()
{
}
void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const
{
_assert_(data_size >= m_texel_size);
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
VkDeviceSize map_offset = offset - m_map_offset;
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
const char* ptr = m_map_pointer + map_offset;
memcpy(data, ptr, data_size);
}
void StagingTexture2D::WriteTexel(u32 x, u32 y, const void* data, size_t data_size)
{
_assert_(data_size >= m_texel_size);
VkDeviceSize offset = y * m_row_stride + x * m_texel_size;
VkDeviceSize map_offset = offset - m_map_offset;
_assert_(offset >= m_map_offset && (map_offset + m_texel_size) <= (m_map_offset + m_map_size));
char* ptr = m_map_pointer + map_offset;
memcpy(ptr, data, data_size);
}
void StagingTexture2D::ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data,
u32 data_stride) const
{
const char* src_ptr = GetRowPointer(y);
// Optimal path: same dimensions, same stride.
_assert_((x + width) <= m_width && (y + height) <= m_height);
if (x == 0 && width == m_width && m_row_stride == data_stride)
{
memcpy(data, src_ptr, m_row_stride * height);
return;
}
u32 copy_size = std::min(width * m_texel_size, data_stride);
char* dst_ptr = reinterpret_cast<char*>(data);
for (u32 row = 0; row < height; row++)
{
memcpy(dst_ptr, src_ptr + (x * m_texel_size), copy_size);
src_ptr += m_row_stride;
dst_ptr += data_stride;
}
}
void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data,
u32 data_stride)
{
char* dst_ptr = GetRowPointer(y);
// Optimal path: same dimensions, same stride.
_assert_((x + width) <= m_width && (y + height) <= m_height);
if (x == 0 && width == m_width && m_row_stride == data_stride)
{
memcpy(dst_ptr, data, m_row_stride * height);
return;
}
u32 copy_size = std::min(width * m_texel_size, data_stride);
const char* src_ptr = reinterpret_cast<const char*>(data);
for (u32 row = 0; row < height; row++)
{
memcpy(dst_ptr + (x * m_texel_size), src_ptr, copy_size);
dst_ptr += m_row_stride;
src_ptr += data_stride;
}
}
void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// Issue the image->buffer copy.
VkBufferImageCopy image_copy = {
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
m_width, // uint32_t bufferRowLength
0, // uint32_t bufferImageHeight
{src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
{width, height, 1} // VkExtent3D imageExtent
};
vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1,
&image_copy);
// Flush CPU and GPU caches if not coherent mapping.
VkDeviceSize buffer_flush_offset = y * m_row_stride;
VkDeviceSize buffer_flush_size = height * m_row_stride;
FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
buffer_flush_offset, buffer_flush_size);
InvalidateCPUCache(buffer_flush_offset, buffer_flush_size);
}
void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// Flush CPU and GPU caches if not coherent mapping.
VkDeviceSize buffer_flush_offset = y * m_row_stride;
VkDeviceSize buffer_flush_size = height * m_row_stride;
FlushCPUCache(buffer_flush_offset, buffer_flush_size);
InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
buffer_flush_offset, buffer_flush_size);
// Issue the buffer->image copy.
VkBufferImageCopy image_copy = {
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
m_width, // uint32_t bufferRowLength
0, // uint32_t bufferImageHeight
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D imageOffset
{width, height, 1} // VkExtent3D imageExtent
};
vkCmdCopyBufferToImage(command_buffer, m_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&image_copy);
}
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
u32 height, VkFormat format)
{
// Assume tight packing.
u32 stride = Util::GetTexelSize(format) * width;
u32 size = stride * height;
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkBuffer buffer;
VkDeviceMemory memory;
bool coherent;
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
return nullptr;
return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height,
format, stride);
}
} // namespace Vulkan

View File

@ -1,58 +0,0 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
#include "VideoBackends/Vulkan/StagingBuffer.h"
namespace Vulkan
{
class StagingTexture2D final : public StagingBuffer
{
public:
StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format,
u32 stride);
~StagingTexture2D();
u32 GetWidth() const { return m_width; }
u32 GetHeight() const { return m_height; }
VkFormat GetFormat() const { return m_format; }
u32 GetRowStride() const { return m_row_stride; }
u32 GetTexelSize() const { return m_texel_size; }
// Requires Map() to be called first.
const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; }
char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; }
void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const;
void WriteTexel(u32 x, u32 y, const void* data, size_t data_size);
void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const;
void WriteTexels(u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
// Assumes that image is in TRANSFER_SRC layout.
// Results are not ready until command_buffer has been executed.
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
// Assumes that image is in TRANSFER_DST layout.
// Buffer is not safe for re-use until after command_buffer has been executed.
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
// Creates the optimal format of image copy.
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format);
protected:
u32 m_width;
u32 m_height;
VkFormat m_format;
u32 m_texel_size;
u32 m_row_stride;
};
}

View File

@ -178,11 +178,6 @@ void TextureCache::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u
}
}
std::unique_ptr<AbstractTexture> TextureCache::CreateTexture(const TextureConfig& config)
{
return VKTexture::Create(config);
}
bool TextureCache::CreateRenderPasses()
{
static constexpr VkAttachmentDescription update_attachment = {
@ -242,80 +237,12 @@ bool TextureCache::CompileShaders()
}
)";
static const char EFB_COLOR_TO_TEX_SOURCE[] = R"(
SAMPLER_BINDING(0) uniform sampler2DArray samp0;
layout(std140, push_constant) uniform PSBlock
{
vec4 colmat[7];
} C;
layout(location = 0) in vec3 uv0;
layout(location = 1) in vec4 col0;
layout(location = 0) out vec4 ocol0;
void main()
{
float4 texcol = texture(samp0, uv0);
texcol = floor(texcol * C.colmat[5]) * C.colmat[6];
ocol0 = texcol * mat4(C.colmat[0], C.colmat[1], C.colmat[2], C.colmat[3]) + C.colmat[4];
}
)";
static const char EFB_DEPTH_TO_TEX_SOURCE[] = R"(
SAMPLER_BINDING(0) uniform sampler2DArray samp0;
layout(std140, push_constant) uniform PSBlock
{
vec4 colmat[5];
} C;
layout(location = 0) in vec3 uv0;
layout(location = 1) in vec4 col0;
layout(location = 0) out vec4 ocol0;
void main()
{
#if MONO_DEPTH
vec4 texcol = texture(samp0, vec3(uv0.xy, 0.0f));
#else
vec4 texcol = texture(samp0, uv0);
#endif
int depth = int((1.0 - texcol.x) * 16777216.0);
// Convert to Z24 format
ivec4 workspace;
workspace.r = (depth >> 16) & 255;
workspace.g = (depth >> 8) & 255;
workspace.b = depth & 255;
// Convert to Z4 format
workspace.a = (depth >> 16) & 0xF0;
// Normalize components to [0.0..1.0]
texcol = vec4(workspace) / 255.0;
ocol0 = texcol * mat4(C.colmat[0], C.colmat[1], C.colmat[2], C.colmat[3]) + C.colmat[4];
}
)";
std::string header = g_shader_cache->GetUtilityShaderHeader();
std::string source;
std::string source = header + COPY_SHADER_SOURCE;
source = header + COPY_SHADER_SOURCE;
m_copy_shader = Util::CompileAndCreateFragmentShader(source);
source = header + EFB_COLOR_TO_TEX_SOURCE;
m_efb_color_to_tex_shader = Util::CompileAndCreateFragmentShader(source);
if (g_ActiveConfig.bStereoEFBMonoDepth)
source = header + "#define MONO_DEPTH 1\n" + EFB_DEPTH_TO_TEX_SOURCE;
else
source = header + EFB_DEPTH_TO_TEX_SOURCE;
m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source);
return m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE &&
m_efb_depth_to_tex_shader != VK_NULL_HANDLE;
return m_copy_shader != VK_NULL_HANDLE;
}
void TextureCache::DeleteShaders()
@ -329,21 +256,18 @@ void TextureCache::DeleteShaders()
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_copy_shader, nullptr);
m_copy_shader = VK_NULL_HANDLE;
}
if (m_efb_color_to_tex_shader != VK_NULL_HANDLE)
for (auto& shader : m_efb_copy_to_tex_shaders)
{
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_color_to_tex_shader, nullptr);
m_efb_color_to_tex_shader = VK_NULL_HANDLE;
}
if (m_efb_depth_to_tex_shader != VK_NULL_HANDLE)
{
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr);
m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader.second, nullptr);
}
m_efb_copy_to_tex_shaders.clear();
}
void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
unsigned int cbuf_id, const float* colmat)
unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity)
{
VKTexture* texture = static_cast<VKTexture*>(entry->texture.get());
@ -383,13 +307,27 @@ void TextureCache::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
texture->GetRawTexIdentifier()->TransitionToLayout(command_buffer,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
UtilityShaderDraw draw(command_buffer,
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
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);
auto uid = TextureConversionShaderGen::GetShaderUid(dst_format, is_depth_copy, is_intensity,
scale_by_half);
auto it = m_efb_copy_to_tex_shaders.emplace(uid, VkShaderModule(VK_NULL_HANDLE));
VkShaderModule& shader = it.first->second;
bool created = it.second;
if (created)
{
std::string source = g_shader_cache->GetUtilityShaderHeader();
source +=
TextureConversionShaderGen::GenerateShader(APIType::Vulkan, uid.GetUidData()).GetBuffer();
shader = Util::CompileAndCreateFragmentShader(source);
}
UtilityShaderDraw draw(command_buffer,
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_render_pass,
g_shader_cache->GetPassthroughVertexShader(),
g_shader_cache->GetPassthroughGeometryShader(), shader);
draw.SetPushConstants(colmat, (is_depth_copy ? sizeof(float) * 20 : sizeof(float) * 28));
draw.SetPSSampler(0, src_texture->GetView(), src_sampler);
VkRect2D dest_region = {{0, 0}, {texture->GetConfig().width, texture->GetConfig().height}};

View File

@ -4,11 +4,13 @@
#pragma once
#include <map>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoCommon/TextureCacheBase.h"
#include "VideoCommon/TextureConverterShaderGen.h"
namespace Vulkan
{
@ -31,8 +33,6 @@ public:
bool CompileShaders() override;
void DeleteShaders() override;
std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) override;
void ConvertTexture(TCacheEntry* destination, TCacheEntry* source, const void* palette,
TLUTFormat format) override;
@ -55,7 +55,8 @@ private:
bool CreateRenderPasses();
void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, const EFBRectangle& src_rect,
bool scale_by_half, unsigned int cbuf_id, const float* colmat) override;
bool scale_by_half, unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) override;
VkRenderPass m_render_pass = VK_NULL_HANDLE;
@ -64,8 +65,7 @@ private:
std::unique_ptr<TextureConverter> m_texture_converter;
VkShaderModule m_copy_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_color_to_tex_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
std::map<TextureConversionShaderGen::TCShaderUid, VkShaderModule> m_efb_copy_to_tex_shaders;
};
} // namespace Vulkan

View File

@ -19,7 +19,6 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/FramebufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
@ -67,9 +66,6 @@ TextureConverter::~TextureConverter()
if (m_encoding_render_pass != VK_NULL_HANDLE)
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr);
if (m_encoding_render_framebuffer != VK_NULL_HANDLE)
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr);
for (auto& it : m_encoding_shaders)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second, nullptr);
@ -111,12 +107,6 @@ bool TextureConverter::Initialize()
return false;
}
if (!CreateEncodingDownloadTexture())
{
PanicAlert("Failed to create download texture");
return false;
}
if (!CreateDecodingTexture())
{
PanicAlert("Failed to create decoding texture");
@ -245,8 +235,10 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
// Can't do our own draw within a render pass.
StateTracker::GetInstance()->EndRenderPass();
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<VKTexture*>(m_encoding_render_texture.get())
->GetRawTexIdentifier()
->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
@ -276,23 +268,15 @@ void TextureConverter::EncodeTextureToMemory(VkImageView src_texture, u8* dest_p
render_height);
VkRect2D render_region = {{0, 0}, {render_width, render_height}};
draw.BeginRenderPass(m_encoding_render_framebuffer, render_region);
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
render_region);
draw.DrawWithoutVertexBuffer(4);
draw.EndRenderPass();
// Transition the image before copying
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_encoding_download_texture->CopyFromImage(
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(),
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0);
// Block until the GPU has finished copying to the staging texture.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Copy from staging texture to the final destination, adjusting pitch if necessary.
m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr,
memory_stride);
MathUtil::Rectangle<int> copy_rect(0, 0, render_width, render_height);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->ReadTexels(copy_rect, dest_ptr, memory_stride);
}
void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride,
@ -304,8 +288,9 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
// Borrow framebuffer from EFB2RAM encoder.
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_encoding_render_texture->TransitionToLayout(command_buffer,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<VKTexture*>(m_encoding_render_texture.get())
->GetRawTexIdentifier()
->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// Use fragment shader to convert RGBA to YUYV.
// Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
@ -317,7 +302,8 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
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);
draw.BeginRenderPass(static_cast<VKTexture*>(m_encoding_render_texture.get())->GetFramebuffer(),
region);
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
@ -325,18 +311,11 @@ void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u
static_cast<int>(src_texture->GetHeight()));
draw.EndRenderPass();
// Render pass transitions to TRANSFER_SRC.
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// Copy from encoding texture to download buffer.
m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(),
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width,
dst_height, 0, 0);
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Finally, copy to guest memory. This may have a different stride.
m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
MathUtil::Rectangle<int> copy_rect(0, 0, output_width, dst_height);
m_encoding_readback_texture->CopyFromTexture(m_encoding_render_texture.get(), copy_rect, 0, 0,
copy_rect);
m_encoding_readback_texture->ReadTexels(copy_rect, dst_ptr, dst_stride);
}
void TextureConverter::DecodeYUYVTextureFromMemory(VKTexture* dst_texture, const void* src_ptr,
@ -410,7 +389,7 @@ bool TextureConverter::SupportsTextureDecoding(TextureFormat format, TLUTFormat
return iter->second.valid;
TextureDecodingPipeline pipeline;
pipeline.base_info = TextureConversionShader::GetDecodingShaderInfo(format);
pipeline.base_info = TextureConversionShaderTiled::GetDecodingShaderInfo(format);
pipeline.compute_shader = VK_NULL_HANDLE;
pipeline.valid = false;
@ -421,7 +400,7 @@ bool TextureConverter::SupportsTextureDecoding(TextureFormat format, TLUTFormat
}
std::string shader_source =
TextureConversionShader::GenerateDecodingShader(format, palette_format, APIType::Vulkan);
TextureConversionShaderTiled::GenerateDecodingShader(format, palette_format, APIType::Vulkan);
pipeline.compute_shader = Util::CompileAndCreateComputeShader(shader_source);
if (pipeline.compute_shader == VK_NULL_HANDLE)
@ -459,7 +438,7 @@ void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer,
// Copy to GPU-visible buffer, aligned to the data type
auto info = iter->second;
u32 bytes_per_buffer_elem =
TextureConversionShader::GetBytesPerBufferElement(info.base_info->buffer_format);
TextureConversionShaderTiled::GetBytesPerBufferElement(info.base_info->buffer_format);
// Calculate total data size, including palette.
// Only copy palette if it is required.
@ -517,16 +496,16 @@ void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer,
VkBufferView data_view = VK_NULL_HANDLE;
switch (iter->second.base_info->buffer_format)
{
case TextureConversionShader::BUFFER_FORMAT_R8_UINT:
case TextureConversionShaderTiled::BUFFER_FORMAT_R8_UINT:
data_view = m_texel_buffer_view_r8_uint;
break;
case TextureConversionShader::BUFFER_FORMAT_R16_UINT:
case TextureConversionShaderTiled::BUFFER_FORMAT_R16_UINT:
data_view = m_texel_buffer_view_r16_uint;
break;
case TextureConversionShader::BUFFER_FORMAT_R32G32_UINT:
case TextureConversionShaderTiled::BUFFER_FORMAT_R32G32_UINT:
data_view = m_texel_buffer_view_r32g32_uint;
break;
case TextureConversionShader::BUFFER_FORMAT_RGBA8_UINT:
case TextureConversionShaderTiled::BUFFER_FORMAT_RGBA8_UINT:
data_view = m_texel_buffer_view_rgba8_uint;
break;
default:
@ -543,8 +522,8 @@ void TextureConverter::DecodeTexture(VkCommandBuffer command_buffer,
dispatcher.SetTexelBuffer(0, data_view);
if (has_palette)
dispatcher.SetTexelBuffer(1, m_texel_buffer_view_r16_uint);
auto groups = TextureConversionShader::GetDispatchCount(iter->second.base_info, aligned_width,
aligned_height);
auto groups = TextureConversionShaderTiled::GetDispatchCount(iter->second.base_info,
aligned_width, aligned_height);
dispatcher.Dispatch(groups.first, groups.second, 1);
// Copy from temporary texture to final destination.
@ -712,7 +691,8 @@ bool TextureConverter::CompilePaletteConversionShaders()
VkShaderModule TextureConverter::CompileEncodingShader(const EFBCopyParams& params)
{
const char* shader = TextureConversionShader::GenerateEncodingShader(params, APIType::Vulkan);
const char* shader =
TextureConversionShaderTiled::GenerateEncodingShader(params, APIType::Vulkan);
VkShaderModule module = Util::CompileAndCreateFragmentShader(shader);
if (module == VK_NULL_HANDLE)
PanicAlert("Failed to compile texture encoding shader.");
@ -734,10 +714,10 @@ VkShaderModule TextureConverter::GetEncodingShader(const EFBCopyParams& params)
bool TextureConverter::CreateEncodingRenderPass()
{
VkAttachmentDescription attachments[] = {
{0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
{0, Util::GetVkFormatForHostTextureFormat(ENCODING_TEXTURE_FORMAT), VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
VkAttachmentReference color_attachment_references[] = {
{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
@ -769,43 +749,14 @@ bool TextureConverter::CreateEncodingRenderPass()
bool TextureConverter::CreateEncodingTexture()
{
m_encoding_render_texture = Texture2D::Create(
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
if (!m_encoding_render_texture)
return false;
TextureConfig config(ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1,
ENCODING_TEXTURE_FORMAT, true);
VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()};
VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr,
0,
m_encoding_render_pass,
static_cast<u32>(ArraySize(framebuffer_attachments)),
framebuffer_attachments,
m_encoding_render_texture->GetWidth(),
m_encoding_render_texture->GetHeight(),
m_encoding_render_texture->GetLayers()};
m_encoding_render_texture = g_renderer->CreateTexture(config);
m_encoding_readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, config);
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr,
&m_encoding_render_framebuffer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: ");
return false;
}
return true;
}
bool TextureConverter::CreateEncodingDownloadTexture()
{
m_encoding_download_texture =
StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH,
ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT);
return m_encoding_download_texture && m_encoding_download_texture->Map();
return m_encoding_render_texture && m_encoding_readback_texture;
}
bool TextureConverter::CreateDecodingTexture()

View File

@ -16,6 +16,9 @@
#include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/VideoCommon.h"
class AbstractTexture;
class AbstractStagingTexture;
namespace Vulkan
{
class StagingTexture2D;
@ -58,7 +61,7 @@ public:
private:
static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4;
static const u32 ENCODING_TEXTURE_HEIGHT = 1024;
static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM;
static const AbstractTextureFormat ENCODING_TEXTURE_FORMAT = AbstractTextureFormat::BGRA8;
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
// Maximum size of a texture based on BP registers.
@ -75,8 +78,6 @@ private:
bool CreateEncodingRenderPass();
bool CreateEncodingTexture();
bool CreateEncodingDownloadTexture();
bool CreateDecodingTexture();
bool CompileYUYVConversionShaders();
@ -106,15 +107,14 @@ private:
// Texture encoding - RGBA8->GX format in memory
std::map<EFBCopyParams, VkShaderModule> m_encoding_shaders;
std::unique_ptr<AbstractTexture> m_encoding_render_texture;
std::unique_ptr<AbstractStagingTexture> m_encoding_readback_texture;
VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE;
std::unique_ptr<Texture2D> m_encoding_render_texture;
VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE;
std::unique_ptr<StagingTexture2D> m_encoding_download_texture;
// Texture decoding - GX format in memory->RGBA8
struct TextureDecodingPipeline
{
const TextureConversionShader::DecodingShaderInfo* base_info;
const TextureConversionShaderTiled::DecodingShaderInfo* base_info;
VkShaderModule compute_shader;
bool valid;
};

View File

@ -107,7 +107,13 @@ VkFormat GetVkFormatForHostTextureFormat(AbstractTextureFormat format)
return VK_FORMAT_BC7_UNORM_BLOCK;
case AbstractTextureFormat::RGBA8:
return VK_FORMAT_R8G8B8A8_UNORM;
case AbstractTextureFormat::BGRA8:
return VK_FORMAT_B8G8R8A8_UNORM;
default:
PanicAlert("Unhandled texture format.");
return VK_FORMAT_R8G8B8A8_UNORM;
}
}

View File

@ -14,7 +14,7 @@
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/FramebufferManager.h"
#include "VideoBackends/Vulkan/StagingTexture2D.h"
#include "VideoBackends/Vulkan/StagingBuffer.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/Texture2D.h"
#include "VideoBackends/Vulkan/Util.h"
@ -113,60 +113,13 @@ void VKTexture::Bind(unsigned int stage)
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
}
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapFullImpl()
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level)
{
// No support for optimization of full copy
return MapRegionImpl(0, 0, 0, m_config.width, m_config.height);
}
Texture2D* src_texture = static_cast<const VKTexture*>(src)->GetRawTexIdentifier();
std::optional<AbstractTexture::RawTextureInfo> VKTexture::MapRegionImpl(u32 level, u32 x, u32 y,
u32 width, u32 height)
{
m_staging_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, width, height,
TEXTURECACHE_TEXTURE_FORMAT);
// Transition image to transfer source, and invalidate the current state,
// since we'll be executing the command buffer.
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
StateTracker::GetInstance()->EndRenderPass();
// Copy to download buffer.
m_staging_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, x, y, width,
height, level, 0);
// Restore original state of texture.
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// Block until the GPU has finished copying to the staging texture.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
// Map the staging texture so we can copy the contents out.
if (!m_staging_texture->Map())
{
PanicAlert("Failed to map staging texture");
return {};
}
return AbstractTexture::RawTextureInfo{reinterpret_cast<u8*>(m_staging_texture->GetMapPointer()),
static_cast<u32>(m_staging_texture->GetRowStride()), width,
height};
}
void VKTexture::Unmap()
{
if (!m_staging_texture)
return;
m_staging_texture->Unmap();
}
void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect)
{
_assert_msg_(VIDEO, static_cast<u32>(src_rect.GetWidth()) <= src_texture->GetWidth() &&
static_cast<u32>(src_rect.GetHeight()) <= src_texture->GetHeight(),
"Source rect is too large for CopyRectangleFromTexture");
@ -176,15 +129,11 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
"Dest rect is too large for CopyRectangleFromTexture");
VkImageCopy image_copy = {
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
src_texture->GetLayers()}, // VkImageSubresourceLayers srcSubresource
{src_rect.left, src_rect.top, 0}, // VkOffset3D srcOffset
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, // VkImageSubresourceLayers dstSubresource
m_config.layers},
{dst_rect.left, dst_rect.top, 0}, // VkOffset3D dstOffset
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()),
1} // VkExtent3D extent
};
{VK_IMAGE_ASPECT_COLOR_BIT, src_level, src_layer, src_texture->GetLayers()},
{src_rect.left, src_rect.top, 0},
{VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, m_config.layers},
{dst_rect.left, dst_rect.top, 0},
{static_cast<uint32_t>(src_rect.GetWidth()), static_cast<uint32_t>(src_rect.GetHeight()), 1}};
// Must be called outside of a render pass.
StateTracker::GetInstance()->EndRenderPass();
@ -197,12 +146,20 @@ void VKTexture::CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
vkCmdCopyImage(g_command_buffer_mgr->GetCurrentCommandBuffer(), src_texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
// Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound.
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect)
void VKTexture::ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect)
{
Texture2D* src_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
// Can't do this within a game render pass.
StateTracker::GetInstance()->EndRenderPass();
StateTracker::GetInstance()->SetPendingRebind();
@ -235,27 +192,10 @@ void VKTexture::ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect,
static_cast<int>(src_texture->GetWidth()),
static_cast<int>(src_texture->GetHeight()));
draw.EndRenderPass();
}
void VKTexture::CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
auto* raw_source_texture = static_cast<const VKTexture*>(source)->GetRawTexIdentifier();
CopyRectangleFromTexture(raw_source_texture, srcrect, dstrect);
}
void VKTexture::CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect)
{
if (srcrect.GetWidth() == dstrect.GetWidth() && srcrect.GetHeight() == dstrect.GetHeight())
CopyTextureRectangle(dstrect, source, srcrect);
else
ScaleTextureRectangle(dstrect, source, srcrect);
// Ensure both textures remain in the SHADER_READ_ONLY layout so they can be bound.
source->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
@ -291,7 +231,7 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity());
u32 block_size = Util::GetBlockSize(m_texture->GetFormat());
u32 num_rows = Common::AlignUp(height, block_size) / block_size;
size_t source_pitch = CalculateHostTextureLevelPitch(m_config.format, row_length);
size_t source_pitch = CalculateStrideForFormat(m_config.format, row_length);
size_t upload_size = source_pitch * num_rows;
std::unique_ptr<StagingBuffer> temp_buffer;
VkBuffer upload_buffer;
@ -356,4 +296,224 @@ void VKTexture::Load(u32 level, u32 width, u32 height, u32 row_length, const u8*
}
}
VKStagingTexture::VKStagingTexture(StagingTextureType type, const TextureConfig& config,
std::unique_ptr<StagingBuffer> buffer)
: AbstractStagingTexture(type, config), m_staging_buffer(std::move(buffer))
{
}
VKStagingTexture::~VKStagingTexture()
{
if (m_needs_flush)
VKStagingTexture::Flush();
}
std::unique_ptr<VKStagingTexture> VKStagingTexture::Create(StagingTextureType type,
const TextureConfig& config)
{
size_t stride = config.GetStride();
size_t buffer_size = stride * static_cast<size_t>(config.height);
STAGING_BUFFER_TYPE buffer_type;
VkImageUsageFlags buffer_usage;
if (type == StagingTextureType::Readback)
{
buffer_type = STAGING_BUFFER_TYPE_READBACK;
buffer_usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
else if (type == StagingTextureType::Upload)
{
buffer_type = STAGING_BUFFER_TYPE_UPLOAD;
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
else
{
buffer_type = STAGING_BUFFER_TYPE_READBACK;
buffer_usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
VkBuffer buffer;
VkDeviceMemory memory;
bool coherent;
if (!StagingBuffer::AllocateBuffer(buffer_type, buffer_size, buffer_usage, &buffer, &memory,
&coherent))
{
return nullptr;
}
std::unique_ptr<StagingBuffer> staging_buffer =
std::make_unique<StagingBuffer>(buffer_type, buffer, memory, buffer_size, coherent);
std::unique_ptr<VKStagingTexture> staging_tex = std::unique_ptr<VKStagingTexture>(
new VKStagingTexture(type, config, std::move(staging_buffer)));
// Use persistent mapping.
if (!staging_tex->m_staging_buffer->Map())
return nullptr;
staging_tex->m_map_pointer = staging_tex->m_staging_buffer->GetMapPointer();
staging_tex->m_map_stride = stride;
return staging_tex;
}
void VKStagingTexture::CopyFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect)
{
_assert_(m_type == StagingTextureType::Readback);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= src->GetConfig().width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= src->GetConfig().height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= m_config.width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= m_config.height);
Texture2D* src_tex = static_cast<const VKTexture*>(src)->GetRawTexIdentifier();
CopyFromTexture(src_tex, src_rect, src_layer, src_level, dst_rect);
}
void VKStagingTexture::CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect)
{
if (m_needs_flush)
{
// Drop copy before reusing it.
g_command_buffer_mgr->RemoveFencePointCallback(this);
m_flush_fence = VK_NULL_HANDLE;
m_needs_flush = false;
}
VkImageLayout old_layout = src->GetLayout();
src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// Issue the image->buffer copy, but delay it for now.
VkBufferImageCopy image_copy = {};
VkImageAspectFlags aspect =
Util::IsDepthFormat(src->GetFormat()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
image_copy.bufferOffset =
static_cast<VkDeviceSize>(static_cast<size_t>(dst_rect.top) * m_config.GetStride() +
static_cast<size_t>(dst_rect.left) * m_texel_size);
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
image_copy.bufferImageHeight = 0;
image_copy.imageSubresource = {aspect, src_level, src_layer, 1};
image_copy.imageOffset = {src_rect.left, src_rect.top, 0};
image_copy.imageExtent = {static_cast<u32>(src_rect.GetWidth()),
static_cast<u32>(src_rect.GetHeight()), 1u};
vkCmdCopyImageToBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), src->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_staging_buffer->GetBuffer(), 1,
&image_copy);
// Restore old source texture layout.
src->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout);
m_needs_flush = true;
g_command_buffer_mgr->AddFencePointCallback(this,
[this](VkCommandBuffer buf, VkFence fence) {
_assert_(m_needs_flush);
m_flush_fence = fence;
},
[this](VkFence fence) {
m_flush_fence = VK_NULL_HANDLE;
m_needs_flush = false;
g_command_buffer_mgr->RemoveFencePointCallback(
this);
});
}
void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level)
{
_assert_(m_type == StagingTextureType::Upload);
_assert_(src_rect.GetWidth() == dst_rect.GetWidth() &&
src_rect.GetHeight() == dst_rect.GetHeight());
_assert_(src_rect.left >= 0 && static_cast<u32>(src_rect.right) <= m_config.width &&
src_rect.top >= 0 && static_cast<u32>(src_rect.bottom) <= m_config.height);
_assert_(dst_rect.left >= 0 && static_cast<u32>(dst_rect.right) <= dst->GetConfig().width &&
dst_rect.top >= 0 && static_cast<u32>(dst_rect.bottom) <= dst->GetConfig().height);
if (m_needs_flush)
{
// Drop copy before reusing it.
g_command_buffer_mgr->RemoveFencePointCallback(this);
m_flush_fence = VK_NULL_HANDLE;
m_needs_flush = false;
}
// Flush caches before copying.
m_staging_buffer->FlushCPUCache();
Texture2D* dst_tex = static_cast<const VKTexture*>(dst)->GetRawTexIdentifier();
VkImageLayout old_layout = dst_tex->GetLayout();
dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
// Issue the image->buffer copy, but delay it for now.
VkBufferImageCopy image_copy = {};
image_copy.bufferOffset =
static_cast<VkDeviceSize>(static_cast<size_t>(src_rect.top) * m_config.GetStride() +
static_cast<size_t>(src_rect.left) * m_texel_size);
image_copy.bufferRowLength = static_cast<u32>(m_config.width);
image_copy.bufferImageHeight = 0;
image_copy.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, dst_level, dst_layer, 1};
image_copy.imageOffset = {dst_rect.left, dst_rect.top, 0};
image_copy.imageExtent = {static_cast<u32>(dst_rect.GetWidth()),
static_cast<u32>(dst_rect.GetHeight()), 1u};
vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
m_staging_buffer->GetBuffer(), dst_tex->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy);
// Restore old source texture layout.
dst_tex->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_layout);
m_needs_flush = true;
g_command_buffer_mgr->AddFencePointCallback(this,
[this](VkCommandBuffer buf, VkFence fence) {
_assert_(m_needs_flush);
m_flush_fence = fence;
},
[this](VkFence fence) {
m_flush_fence = VK_NULL_HANDLE;
m_needs_flush = false;
g_command_buffer_mgr->RemoveFencePointCallback(
this);
});
}
bool VKStagingTexture::Map()
{
// Always mapped.
return true;
}
void VKStagingTexture::Unmap()
{
// Always mapped.
}
void VKStagingTexture::Flush()
{
if (!m_needs_flush)
return;
// Either of the below two calls will cause the callback to fire.
g_command_buffer_mgr->RemoveFencePointCallback(this);
if (m_flush_fence != VK_NULL_HANDLE)
{
// WaitForFence should fire the callback.
g_command_buffer_mgr->WaitForFence(m_flush_fence);
}
else
{
// We don't have a fence, and are pending. That means the readback is in the current
// command buffer, and must execute it to populate the staging texture.
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
}
m_needs_flush = false;
// For readback textures, invalidate the CPU cache as there is new data there.
if (m_type == StagingTextureType::Readback)
m_staging_buffer->InvalidateCPUCache();
}
} // namespace Vulkan

View File

@ -7,10 +7,12 @@
#include <memory>
#include <vulkan/vulkan.h>
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
namespace Vulkan
{
class StagingBuffer;
class Texture2D;
class VKTexture final : public AbstractTexture
@ -20,13 +22,15 @@ public:
~VKTexture();
void Bind(unsigned int stage) override;
void Unmap() override;
void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) override;
void CopyRectangleFromTexture(Texture2D* source, const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect);
void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) override;
void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect) override;
void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) override;
@ -39,21 +43,41 @@ private:
VKTexture(const TextureConfig& tex_config, std::unique_ptr<Texture2D> texture,
VkFramebuffer framebuffer);
// Copies the contents of a texture using vkCmdCopyImage
void CopyTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect);
// Copies (and optionally scales) the contents of a texture using a framgent shader.
void ScaleTextureRectangle(const MathUtil::Rectangle<int>& dst_rect, Texture2D* src_texture,
const MathUtil::Rectangle<int>& src_rect);
std::optional<RawTextureInfo> MapFullImpl() override;
std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height) override;
std::unique_ptr<Texture2D> m_texture;
std::unique_ptr<StagingTexture2D> m_staging_texture;
VkFramebuffer m_framebuffer;
};
class VKStagingTexture final : public AbstractStagingTexture
{
public:
VKStagingTexture() = delete;
~VKStagingTexture();
void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) override;
void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) override;
bool Map() override;
void Unmap() override;
void Flush() override;
// This overload is provided for compatibility as we dropped StagingTexture2D.
// For now, FramebufferManager relies on them. But we can drop it once we move that to common.
void CopyFromTexture(Texture2D* src, const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect);
static std::unique_ptr<VKStagingTexture> Create(StagingTextureType type,
const TextureConfig& config);
private:
VKStagingTexture(StagingTextureType type, const TextureConfig& config,
std::unique_ptr<StagingBuffer> buffer);
std::unique_ptr<StagingBuffer> m_staging_buffer;
VkFence m_flush_fence = VK_NULL_HANDLE;
};
} // namespace Vulkan

View File

@ -46,7 +46,6 @@
<ClCompile Include="PerfQuery.cpp" />
<ClCompile Include="RasterFont.cpp" />
<ClCompile Include="StagingBuffer.cpp" />
<ClCompile Include="StagingTexture2D.cpp" />
<ClCompile Include="Util.cpp" />
<ClCompile Include="VertexFormat.cpp" />
<ClCompile Include="ObjectCache.cpp" />
@ -72,7 +71,6 @@
<ClInclude Include="TextureConverter.h" />
<ClInclude Include="RasterFont.h" />
<ClInclude Include="StagingBuffer.h" />
<ClInclude Include="StagingTexture2D.h" />
<ClInclude Include="Util.h" />
<ClInclude Include="VertexFormat.h" />
<ClInclude Include="PerfQuery.h" />

View File

@ -0,0 +1,133 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "Common/Assert.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
AbstractStagingTexture::AbstractStagingTexture(StagingTextureType type, const TextureConfig& c)
: m_type(type), m_config(c), m_texel_size(AbstractTexture::GetTexelSizeForFormat(c.format))
{
}
AbstractStagingTexture::~AbstractStagingTexture() = default;
void AbstractStagingTexture::CopyFromTexture(const AbstractTexture* src, u32 src_layer,
u32 src_level)
{
MathUtil::Rectangle<int> src_rect = src->GetConfig().GetMipRect(src_level);
MathUtil::Rectangle<int> dst_rect = m_config.GetRect();
CopyFromTexture(src, src_rect, src_layer, src_level, dst_rect);
}
void AbstractStagingTexture::CopyToTexture(AbstractTexture* dst, u32 dst_layer, u32 dst_level)
{
MathUtil::Rectangle<int> src_rect = m_config.GetRect();
MathUtil::Rectangle<int> dst_rect = dst->GetConfig().GetMipRect(dst_level);
CopyToTexture(src_rect, dst, dst_rect, dst_layer, dst_level);
}
void AbstractStagingTexture::ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr,
u32 out_stride)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);
// Offset pointer to point to start of region being copied out.
const char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;
// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width &&
m_map_stride == out_stride)
{
std::memcpy(out_ptr, current_ptr, m_map_stride * rect.GetHeight());
return;
}
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
char* dst_ptr = reinterpret_cast<char*>(out_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(dst_ptr, current_ptr, copy_size);
current_ptr += m_map_stride;
dst_ptr += out_stride;
}
}
void AbstractStagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;
_assert_(x < m_config.width && y < m_config.height);
const char* src_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(out_ptr, src_ptr, m_texel_size);
}
void AbstractStagingTexture::WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr,
u32 in_stride)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);
// Offset pointer to point to start of region being copied to.
char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;
// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width && m_map_stride == in_stride)
{
std::memcpy(current_ptr, in_ptr, m_map_stride * rect.GetHeight());
return;
}
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
const char* src_ptr = reinterpret_cast<const char*>(in_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(current_ptr, src_ptr, copy_size);
current_ptr += m_map_stride;
src_ptr += in_stride;
}
}
void AbstractStagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;
_assert_(x < m_config.width && y < m_config.height);
char* dest_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(dest_ptr, in_ptr, m_texel_size);
}
bool AbstractStagingTexture::PrepareForAccess()
{
if (m_needs_flush)
{
if (IsMapped())
Unmap();
Flush();
}
return IsMapped() || Map();
}

View File

@ -0,0 +1,86 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "VideoCommon/TextureConfig.h"
class AbstractTexture;
class AbstractStagingTexture
{
public:
explicit AbstractStagingTexture(StagingTextureType type, const TextureConfig& c);
virtual ~AbstractStagingTexture();
const TextureConfig& GetConfig() const { return m_config; }
StagingTextureType GetType() const { return m_type; }
size_t GetTexelSize() const { return m_texel_size; }
bool IsMapped() const { return m_map_pointer != nullptr; }
char* GetMappedPointer() const { return m_map_pointer; }
size_t GetMappedStride() const { return m_map_stride; }
// Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) = 0;
// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyFromTexture(const AbstractTexture* src, u32 src_layer = 0, u32 src_level = 0);
// Copies from this staging texture to a GPU texture.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) = 0;
// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyToTexture(AbstractTexture* dst, u32 dst_layer = 0, u32 dst_level = 0);
// Maps the texture into the CPU address space, enabling it to read the contents.
// The Map call may not perform synchronization. If the contents of the staging texture
// has been updated by a CopyFromTexture call, you must call Flush() first.
// If persistent mapping is supported in the backend, this may be a no-op.
virtual bool Map() = 0;
// Unmaps the CPU-readable copy of the texture. May be a no-op on backends which
// support persistent-mapped buffers.
virtual void Unmap() = 0;
// Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU.
// This may cause a command buffer flush depending on if one has occurred between the last
// call to CopyFromTexture()/CopyToTexture() and the Flush() call.
virtual void Flush() = 0;
// Reads the specified rectangle from the staging texture to out_ptr, with the specified stride
// (length in bytes of each row). CopyFromTexture must be called first. The contents of any
// texels outside of the rectangle used for CopyFromTexture is undefined.
void ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr, u32 out_stride);
void ReadTexel(u32 x, u32 y, void* out_ptr);
// Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the
// specified stride (length in bytes of each row). After updating the staging texture with all
// changes, call CopyToTexture() to update the GPU copy.
void WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr, u32 in_stride);
void WriteTexel(u32 x, u32 y, const void* in_ptr);
protected:
bool PrepareForAccess();
const StagingTextureType m_type;
const TextureConfig m_config;
const size_t m_texel_size;
char* m_map_pointer = nullptr;
size_t m_map_stride = 0;
bool m_needs_flush = false;
};

View File

@ -5,9 +5,11 @@
#include <algorithm>
#include "Common/Assert.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/ImageWrite.h"
#include "VideoCommon/RenderBase.h"
AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c)
{
@ -20,93 +22,51 @@ bool AbstractTexture::Save(const std::string& filename, unsigned int level)
// We can't dump compressed textures currently (it would mean drawing them to a RGBA8
// framebuffer, and saving that). TextureCache does not call Save for custom textures
// anyway, so this is fine for now.
_assert_(m_config.format == AbstractTextureFormat::RGBA8);
_assert_(!IsCompressedFormat(m_config.format));
_assert_(level < m_config.levels);
auto result = level == 0 ? Map() : Map(level);
// Determine dimensions of image we want to save.
u32 level_width = std::max(1u, m_config.width >> level);
u32 level_height = std::max(1u, m_config.height >> level);
if (!result.has_value())
// Use a temporary staging texture for the download. Certainly not optimal,
// but this is not a frequently-executed code path..
TextureConfig readback_texture_config(level_width, level_height, 1, 1,
AbstractTextureFormat::RGBA8, false);
auto readback_texture =
g_renderer->CreateStagingTexture(StagingTextureType::Readback, readback_texture_config);
if (!readback_texture)
return false;
// Copy to the readback texture's buffer.
readback_texture->CopyFromTexture(this, 0, level);
readback_texture->Flush();
// Map it so we can encode it to the file.
if (!readback_texture->Map())
return false;
return TextureToPng(reinterpret_cast<const u8*>(readback_texture->GetMappedPointer()),
static_cast<int>(readback_texture->GetMappedStride()), filename, level_width,
level_height);
}
bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format)
{
switch (format)
{
case AbstractTextureFormat::DXT1:
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return true;
default:
return false;
}
auto raw_data = result.value();
return TextureToPng(raw_data.data, raw_data.stride, filename, raw_data.width, raw_data.height);
}
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map()
{
if (m_currently_mapped)
{
Unmap();
m_currently_mapped = false;
}
auto result = MapFullImpl();
if (!result.has_value())
{
m_currently_mapped = false;
return {};
}
m_currently_mapped = true;
return result;
}
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level, u32 x, u32 y,
u32 width, u32 height)
{
_assert_(level < m_config.levels);
u32 max_level_width = std::max(m_config.width >> level, 1u);
u32 max_level_height = std::max(m_config.height >> level, 1u);
_assert_(width < max_level_width);
_assert_(height < max_level_height);
auto result = MapRegionImpl(level, x, y, width, height);
if (!result.has_value())
{
m_currently_mapped = false;
return {};
}
m_currently_mapped = true;
return result;
}
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::Map(u32 level)
{
_assert_(level < m_config.levels);
u32 level_width = std::max(m_config.width >> level, 1u);
u32 level_height = std::max(m_config.height >> level, 1u);
return Map(level, 0, 0, level_width, level_height);
}
void AbstractTexture::Unmap()
{
}
std::optional<AbstractTexture::RawTextureInfo> AbstractTexture::MapFullImpl()
{
return {};
}
std::optional<AbstractTexture::RawTextureInfo>
AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height)
{
return {};
}
bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
{
// This will need to be changed if we add any other uncompressed formats.
return format != AbstractTextureFormat::RGBA8;
}
size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length)
size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length)
{
switch (format)
{
@ -117,8 +77,30 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for
case AbstractTextureFormat::BPTC:
return static_cast<size_t>(std::max(1u, row_length / 4)) * 16;
case AbstractTextureFormat::RGBA8:
default:
case AbstractTextureFormat::BGRA8:
return static_cast<size_t>(row_length) * 4;
default:
PanicAlert("Unhandled texture format.");
return 0;
}
}
size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format)
{
switch (format)
{
case AbstractTextureFormat::DXT1:
return 8;
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return 16;
case AbstractTextureFormat::RGBA8:
case AbstractTextureFormat::BGRA8:
return 4;
default:
PanicAlert("Unhandled texture format.");
return 0;
}
}

View File

@ -5,7 +5,6 @@
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
@ -17,38 +16,27 @@ class AbstractTexture
public:
explicit AbstractTexture(const TextureConfig& c);
virtual ~AbstractTexture();
virtual void Bind(unsigned int stage) = 0;
bool Save(const std::string& filename, unsigned int level);
struct RawTextureInfo
{
const u8* data;
u32 stride;
u32 width;
u32 height;
};
std::optional<RawTextureInfo> Map();
std::optional<RawTextureInfo> Map(u32 level, u32 x, u32 y, u32 width, u32 height);
std::optional<RawTextureInfo> Map(u32 level);
virtual void Unmap();
virtual void CopyRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) = 0;
virtual void CopyRectangleFromTexture(const AbstractTexture* src,
const MathUtil::Rectangle<int>& src_rect, u32 src_layer,
u32 src_level, const MathUtil::Rectangle<int>& dst_rect,
u32 dst_layer, u32 dst_level) = 0;
virtual void ScaleRectangleFromTexture(const AbstractTexture* source,
const MathUtil::Rectangle<int>& srcrect,
const MathUtil::Rectangle<int>& dstrect) = 0;
virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) = 0;
static bool IsCompressedHostTextureFormat(AbstractTextureFormat format);
static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length);
bool Save(const std::string& filename, unsigned int level);
static bool IsCompressedFormat(AbstractTextureFormat format);
static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length);
static size_t GetTexelSizeForFormat(AbstractTextureFormat format);
const TextureConfig& GetConfig() const;
protected:
virtual std::optional<RawTextureInfo> MapFullImpl();
virtual std::optional<RawTextureInfo> MapRegionImpl(u32 level, u32 x, u32 y, u32 width,
u32 height);
bool m_currently_mapped = false;
const TextureConfig m_config;
};

View File

@ -1017,7 +1017,7 @@ void GetBPRegInfo(const u8* data, std::string* name, std::string* desc)
(copy.clamp0 && copy.clamp1) ? "Top and Bottom" : (copy.clamp0) ?
"Top only" :
(copy.clamp1) ? "Bottom only" : "None",
no_yes[copy.yuv], copy.tp_realFormat(),
no_yes[copy.yuv], static_cast<int>(copy.tp_realFormat()),
(copy.gamma == 0) ? "1.0" : (copy.gamma == 1) ?
"1.7" :
(copy.gamma == 2) ? "2.2" : "Invalid value 0x3?",

View File

@ -1,4 +1,5 @@
set(SRCS
AbstractStagingTexture.cpp
AbstractTexture.cpp
AsyncRequests.cpp
AsyncShaderCompiler.cpp
@ -38,6 +39,7 @@ set(SRCS
TextureCacheBase.cpp
TextureConfig.cpp
TextureConversionShader.cpp
TextureConverterShaderGen.cpp
TextureDecoder_Common.cpp
VertexLoader.cpp
VertexLoaderBase.cpp

View File

@ -284,7 +284,7 @@ std::string HiresTexture::GenBaseName(const u8* texture, size_t texture_size, co
std::string basename = s_format_prefix + StringFromFormat("%dx%d%s_%016" PRIx64, width, height,
has_mipmaps ? "_m" : "", tex_hash);
std::string tlutname = tlut_size ? StringFromFormat("_%016" PRIx64, tlut_hash) : "";
std::string formatname = StringFromFormat("_%d", format);
std::string formatname = StringFromFormat("_%d", static_cast<int>(format));
std::string fullname = basename + tlutname + formatname;
for (int level = 0; level < 10 && convert; level++)

View File

@ -49,7 +49,7 @@ void VideoBackendBase::Video_CleanupShared()
{
// First stop any framedumping, which might need to dump the last xfb frame. This process
// can require additional graphics sub-systems so it needs to be done first
g_renderer->ExitFramedumping();
g_renderer->ShutdownFrameDumping();
Video_Cleanup();
}

View File

@ -43,6 +43,7 @@
#include "Core/Movie.h"
#include "VideoCommon/AVIDump.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/CPMemory.h"
@ -100,15 +101,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height)
Renderer::~Renderer() = default;
void Renderer::ExitFramedumping()
{
ShutdownFrameDumping();
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
m_dump_texture.reset();
}
void Renderer::RenderToXFB(u32 xfbAddr, const EFBRectangle& sourceRc, u32 fbStride, u32 fbHeight,
float Gamma)
{
@ -635,14 +627,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
m_aspect_wide = flush_count_anamorphic > 0.75 * flush_total;
}
if (IsFrameDumping() && m_last_xfb_texture)
{
FinishFrameData();
}
else
{
ShutdownFrameDumping();
}
// Ensure the last frame was written to the dump.
// This is required even if frame dumping has stopped, since the frame dump is one frame
// behind the renderer.
FlushFrameDump();
bool update_frame_count = false;
if (xfbAddr && fbWidth && fbStride && fbHeight)
@ -668,10 +656,9 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
m_fps_counter.Update();
update_frame_count = true;
if (IsFrameDumping())
{
DoDumpFrame();
}
DumpCurrentFrame();
}
// Update our last xfb values
@ -701,20 +688,16 @@ bool Renderer::IsFrameDumping()
return false;
}
void Renderer::DoDumpFrame()
void Renderer::DumpCurrentFrame()
{
UpdateFrameDumpTexture();
// Scale/render to frame dump texture.
RenderFrameDump();
auto result = m_dump_texture->Map();
if (result.has_value())
{
auto raw_data = result.value();
DumpFrameData(raw_data.data, raw_data.width, raw_data.height, raw_data.stride,
AVIDump::FetchState(m_last_xfb_ticks));
}
// Queue a readback for the next frame.
QueueFrameDumpReadback();
}
void Renderer::UpdateFrameDumpTexture()
void Renderer::RenderFrameDump()
{
int target_width, target_height;
if (!g_ActiveConfig.bInternalResolutionFrameDumps && !IsHeadless())
@ -729,33 +712,99 @@ void Renderer::UpdateFrameDumpTexture()
m_last_xfb_texture->GetConfig().width, m_last_xfb_texture->GetConfig().height);
}
if (m_dump_texture == nullptr ||
m_dump_texture->GetConfig().width != static_cast<u32>(target_width) ||
m_dump_texture->GetConfig().height != static_cast<u32>(target_height))
// Ensure framebuffer exists (we lazily allocate it in case frame dumping isn't used).
// Or, resize texture if it isn't large enough to accommodate the current frame.
if (!m_frame_dump_render_texture ||
m_frame_dump_render_texture->GetConfig().width != static_cast<u32>(target_width) ||
m_frame_dump_render_texture->GetConfig().height == static_cast<u32>(target_height))
{
TextureConfig config;
config.width = target_width;
config.height = target_height;
config.rendertarget = true;
m_dump_texture = g_texture_cache->CreateTexture(config);
// Recreate texture objects. Release before creating so we don't temporarily use twice the RAM.
TextureConfig config(target_width, target_height, 1, 1, AbstractTextureFormat::RGBA8, true);
m_frame_dump_render_texture.reset();
m_frame_dump_render_texture = CreateTexture(config);
_assert_(m_frame_dump_render_texture);
}
m_dump_texture->CopyRectangleFromTexture(m_last_xfb_texture, m_last_xfb_region,
EFBRectangle{0, 0, target_width, target_height});
// Scaling is likely to occur here, but if possible, do a bit-for-bit copy.
if (m_last_xfb_region.GetWidth() != target_width ||
m_last_xfb_region.GetHeight() != target_height)
{
m_frame_dump_render_texture->ScaleRectangleFromTexture(
m_last_xfb_texture, m_last_xfb_region, EFBRectangle{0, 0, target_width, target_height});
}
else
{
m_frame_dump_render_texture->CopyRectangleFromTexture(
m_last_xfb_texture, m_last_xfb_region, 0, 0,
EFBRectangle{0, 0, target_width, target_height}, 0, 0);
}
}
void Renderer::QueueFrameDumpReadback()
{
// Index 0 was just sent to AVI dump. Swap with the second texture.
if (m_frame_dump_readback_textures[0])
std::swap(m_frame_dump_readback_textures[0], m_frame_dump_readback_textures[1]);
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
if (!rbtex || rbtex->GetConfig() != m_frame_dump_render_texture->GetConfig())
{
rbtex = CreateStagingTexture(StagingTextureType::Readback,
m_frame_dump_render_texture->GetConfig());
}
m_last_frame_state = AVIDump::FetchState(m_last_xfb_ticks);
m_last_frame_exported = true;
rbtex->CopyFromTexture(m_frame_dump_render_texture.get(), 0, 0);
}
void Renderer::FlushFrameDump()
{
if (!m_last_frame_exported)
return;
// Ensure the previously-queued frame was encoded.
FinishFrameData();
// Queue encoding of the last frame dumped.
std::unique_ptr<AbstractStagingTexture>& rbtex = m_frame_dump_readback_textures[0];
rbtex->Flush();
if (rbtex->Map())
{
DumpFrameData(reinterpret_cast<u8*>(rbtex->GetMappedPointer()), rbtex->GetConfig().width,
rbtex->GetConfig().height, static_cast<int>(rbtex->GetMappedStride()),
m_last_frame_state);
rbtex->Unmap();
}
m_last_frame_exported = false;
// Shutdown frame dumping if it is no longer active.
if (!IsFrameDumping())
ShutdownFrameDumping();
}
void Renderer::ShutdownFrameDumping()
{
// Ensure the last queued readback has been sent to the encoder.
FlushFrameDump();
if (!m_frame_dump_thread_running.IsSet())
return;
// Ensure previous frame has been encoded.
FinishFrameData();
// Wake thread up, and wait for it to exit.
m_frame_dump_thread_running.Clear();
m_frame_dump_start.Set();
if (m_frame_dump_thread.joinable())
m_frame_dump_thread.join();
}
void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state)
{
m_frame_dump_config = FrameDumpConfig{m_last_xfb_texture, data, w, h, stride, state};
m_frame_dump_config = FrameDumpConfig{data, w, h, stride, state};
if (!m_frame_dump_thread_running.IsSet())
{
@ -765,6 +814,7 @@ void Renderer::DumpFrameData(const u8* data, int w, int h, int stride, const AVI
m_frame_dump_thread = std::thread(&Renderer::RunFrameDumps, this);
}
// Wake worker thread up.
m_frame_dump_start.Set();
m_frame_dump_frame_running = true;
}
@ -776,7 +826,6 @@ void Renderer::FinishFrameData()
m_frame_dump_done.Wait();
m_frame_dump_frame_running = false;
m_frame_dump_config.texture->Unmap();
}
void Renderer::RunFrameDumps()

View File

@ -34,8 +34,11 @@
class AbstractRawTexture;
class AbstractTexture;
class AbstractStagingTexture;
class PostProcessingShaderImplementation;
struct TextureConfig;
enum class EFBAccessType;
enum class StagingTextureType;
struct EfbPokeData
{
@ -79,6 +82,10 @@ public:
virtual void RestoreState() {}
virtual void ResetAPIState() {}
virtual void RestoreAPIState() {}
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
virtual std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;
// Ideal internal resolution - multiple of the native EFB resolution
int GetTargetWidth() const { return m_target_width; }
int GetTargetHeight() const { return m_target_height; }
@ -145,7 +152,7 @@ public:
virtual void ChangeSurface(void* new_surface_handle) {}
bool UseVertexDepthRange() const;
void ExitFramedumping();
void ShutdownFrameDumping();
protected:
std::tuple<int, int> CalculateTargetScale(int x, int y) const;
@ -185,11 +192,8 @@ protected:
u32 m_last_host_config_bits = 0;
private:
void DoDumpFrame();
void RunFrameDumps();
void ShutdownFrameDumping();
std::tuple<int, int> CalculateOutputDimensions(int width, int height);
void UpdateFrameDumpTexture();
PEControl::PixelFormat m_prev_efb_format = PEControl::INVALID_FMT;
unsigned int m_efb_scale = 1;
@ -207,7 +211,6 @@ private:
bool m_frame_dump_frame_running = false;
struct FrameDumpConfig
{
AbstractTexture* texture;
const u8* data;
int width;
int height;
@ -215,13 +218,18 @@ private:
AVIDump::Frame state;
} m_frame_dump_config;
// Texture used for screenshot/frame dumping
std::unique_ptr<AbstractTexture> m_frame_dump_render_texture;
std::array<std::unique_ptr<AbstractStagingTexture>, 2> m_frame_dump_readback_textures;
AVIDump::Frame m_last_frame_state;
bool m_last_frame_exported = false;
// Tracking of XFB textures so we don't render duplicate frames.
AbstractTexture* m_last_xfb_texture = nullptr;
u64 m_last_xfb_id = std::numeric_limits<u64>::max();
u64 m_last_xfb_ticks = 0;
EFBRectangle m_last_xfb_region;
std::unique_ptr<AbstractTexture> m_dump_texture;
// Note: Only used for auto-ir
u32 m_last_xfb_width = MAX_XFB_WIDTH;
u32 m_last_xfb_height = MAX_XFB_HEIGHT;
@ -235,7 +243,23 @@ private:
void DumpFrameToImage(const FrameDumpConfig& config);
bool IsFrameDumping();
// Asynchronously encodes the current staging texture to the frame dump.
void DumpCurrentFrame();
// Fills the frame dump render texture with the current XFB texture.
void RenderFrameDump();
// Queues the current frame for readback, which will be written to AVI next frame.
void QueueFrameDumpReadback();
// Asynchronously encodes the specified pointer of frame data to the frame dump.
void DumpFrameData(const u8* data, int w, int h, int stride, const AVIDump::Frame& state);
// Ensures all rendered frames are queued for encoding.
void FlushFrameDump();
// Ensures all encoded frames have been written to the output file.
void FinishFrameData();
};

View File

@ -277,9 +277,9 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* e
std::unique_ptr<AbstractTexture> new_texture = AllocateTexture(newconfig);
if (new_texture)
{
new_texture->CopyRectangleFromTexture(entry->texture.get(),
entry->texture->GetConfig().GetRect(),
new_texture->GetConfig().GetRect());
new_texture->ScaleRectangleFromTexture(entry->texture.get(),
entry->texture->GetConfig().GetRect(),
new_texture->GetConfig().GetRect());
entry->texture.swap(new_texture);
auto config = new_texture->GetConfig();
@ -406,7 +406,11 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale
dstrect.top = dst_y;
dstrect.right = (dst_x + copy_width);
dstrect.bottom = (dst_y + copy_height);
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect);
for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
{
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer,
0, dstrect, layer, 0);
}
if (isPaletteTexture)
{
@ -1366,33 +1370,16 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_
srcrect.right = (src_x + copy_width);
srcrect.bottom = (src_y + copy_height);
if (static_cast<int>(entry->GetWidth()) == srcrect.GetWidth())
{
srcrect.right -= 1;
}
if (static_cast<int>(entry->GetHeight()) == srcrect.GetHeight())
{
srcrect.bottom -= 1;
}
dstrect.left = dst_x;
dstrect.top = dst_y;
dstrect.right = (dst_x + copy_width);
dstrect.bottom = (dst_y + copy_height);
if (static_cast<int>(entry_to_update->GetWidth()) == dstrect.GetWidth())
for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
{
dstrect.right -= 1;
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer,
0, dstrect, layer, 0);
}
if (static_cast<int>(entry_to_update->GetHeight()) == dstrect.GetHeight())
{
dstrect.bottom -= 1;
}
entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect);
updated_entry = true;
if (tex_info.is_palette_texture)
@ -2010,7 +1997,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, EFBCopyFormat dstF
entry->may_have_overlapping_textures = false;
entry->is_custom_tex = false;
CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, cbufid, colmat);
CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, cbufid, colmat, dstFormat,
isIntensity);
u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash);
@ -2090,7 +2078,7 @@ std::unique_ptr<AbstractTexture> TextureCacheBase::AllocateTexture(const Texture
}
else
{
entry = CreateTexture(config);
entry = g_renderer->CreateTexture(config);
if (!entry)
return nullptr;

View File

@ -273,8 +273,6 @@ public:
void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height);
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
protected:
TextureCacheBase();
@ -318,7 +316,8 @@ private:
virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
const EFBRectangle& src_rect, bool scale_by_half,
unsigned int cbuf_id, const float* colmat) = 0;
unsigned int cbuf_id, const float* colmat,
EFBCopyFormat dst_format, bool is_intensity) = 0;
// Removes and unlinks texture from texture cache and returns it to the pool
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter);

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/AbstractTexture.h"
#include <tuple>
@ -12,7 +13,28 @@ bool TextureConfig::operator==(const TextureConfig& o) const
std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget);
}
bool TextureConfig::operator!=(const TextureConfig& o) const
{
return !operator==(o);
}
MathUtil::Rectangle<int> TextureConfig::GetRect() const
{
return {0, 0, static_cast<int>(width), static_cast<int>(height)};
}
MathUtil::Rectangle<int> TextureConfig::GetMipRect(u32 level) const
{
return {0, 0, static_cast<int>(std::max(width >> level, 1u)),
static_cast<int>(std::max(height >> level, 1u))};
}
size_t TextureConfig::GetStride() const
{
return AbstractTexture::CalculateStrideForFormat(format, width);
}
size_t TextureConfig::GetMipStride(u32 level) const
{
return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u));
}

View File

@ -13,17 +13,37 @@
enum class AbstractTextureFormat : u32
{
RGBA8,
BGRA8,
DXT1,
DXT3,
DXT5,
BPTC
BPTC,
Undefined
};
enum class StagingTextureType
{
Readback, // Optimize for CPU reads, GPU writes, no CPU writes
Upload, // Optimize for CPU writes, GPU reads, no CPU reads
Mutable // Optimize for CPU reads, GPU writes, allow slow CPU reads
};
struct TextureConfig
{
constexpr TextureConfig() = default;
constexpr TextureConfig(u32 width_, u32 height_, u32 levels_, u32 layers_,
AbstractTextureFormat format_, bool rendertarget_)
: width(width_), height(height_), levels(levels_), layers(layers_), format(format_),
rendertarget(rendertarget_)
{
}
bool operator==(const TextureConfig& o) const;
bool operator!=(const TextureConfig& o) const;
MathUtil::Rectangle<int> GetRect() const;
MathUtil::Rectangle<int> GetMipRect(u32 level) const;
size_t GetStride() const;
size_t GetMipStride(u32 level) const;
u32 width = 0;
u32 height = 0;

Some files were not shown because too many files have changed in this diff Show More