mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
graphics: address perf issues with DX9 image resize logic (#712)
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change Move resize logic from `EndScene` to `Present`/`PresentEx` (resolves Live2D frame drops in some extreme settings) Plug small memory leak Remove unnecessary surface Lock/Unlock ## Testing Tested IIDX, SDVX, DDR.
This commit is contained in:
@@ -1458,6 +1458,15 @@ void graphics_d3d9_on_present(
|
||||
IDirect3DDevice9 *device,
|
||||
IDirect3DDevice9 *wrapped_device) {
|
||||
|
||||
// image resize / orientation swap. run here (the present path) rather than from `EndScene`,
|
||||
// which may fire several times per frame on multi-pass / render-to-texture games. this is the
|
||||
// single point guaranteed to be after the game's last `EndScene` and before the real `Present`,
|
||||
// so the back buffer is fully drawn and the expensive StretchRect work happens exactly once per
|
||||
// frame. it must run before the overlay is rendered so the overlay isn't scaled with the image.
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
SurfaceHook(device);
|
||||
}
|
||||
|
||||
// Do overlay init as many d3d9 hooks create a dummy instance to get vtable offsets and never
|
||||
// call `Present`. This avoids race conditions on `IDirect3D9::CreateDevice` like with
|
||||
// `dx9osd.dll` for pfreepanic.
|
||||
|
||||
@@ -760,18 +760,15 @@ HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::BeginScene() {
|
||||
|
||||
static IDirect3DSurface9 *topSurface = nullptr;
|
||||
static IDirect3DSurface9 *backbuffer = nullptr;
|
||||
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
|
||||
static IDirect3DTexture9* tex;
|
||||
static IDirect3DTexture9 *tex = nullptr;
|
||||
|
||||
static UINT topSurface_width = 0;
|
||||
static UINT topSurface_height = 0;
|
||||
static float topSurface_aspect_ratio = 1.f;
|
||||
static UINT backbuffer_width = 0;
|
||||
static UINT backbuffer_height = 0;
|
||||
|
||||
void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
D3DPRESENT_PARAMETERS param {};
|
||||
|
||||
pReal->GetSwapChain(0, &mSwapChain);
|
||||
mSwapChain->GetPresentParameters(¶m);
|
||||
|
||||
// phase 0 - create a surface that has the same aspect ratio as the original back buffer
|
||||
// but larger in size. this is needed to ensure that when the user zooms out, we have
|
||||
@@ -779,8 +776,20 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
//
|
||||
// (only done once)
|
||||
if (!topSurface) {
|
||||
topSurface_width = param.BackBufferWidth * 3;
|
||||
topSurface_height = param.BackBufferHeight * 3;
|
||||
// GetSwapChain/GetBackBuffer AddRef their out parameters; the swap chain is only
|
||||
// needed here (during one-time init), so release it after use to avoid a per-frame
|
||||
// ref leak. The backbuffer ref is intentionally retained for the lifetime of the
|
||||
// cached resources.
|
||||
LPDIRECT3DSWAPCHAIN9 swapChain = nullptr;
|
||||
D3DPRESENT_PARAMETERS param {};
|
||||
|
||||
pReal->GetSwapChain(0, &swapChain);
|
||||
swapChain->GetPresentParameters(¶m);
|
||||
|
||||
backbuffer_width = param.BackBufferWidth;
|
||||
backbuffer_height = param.BackBufferHeight;
|
||||
topSurface_width = backbuffer_width * 3;
|
||||
topSurface_height = backbuffer_height * 3;
|
||||
topSurface_aspect_ratio = (float)topSurface_width / (float)topSurface_height;
|
||||
if (pReal->CreateTexture(topSurface_width, topSurface_height, 1,
|
||||
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
|
||||
@@ -793,16 +802,18 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
|
||||
|
||||
tex->GetSurfaceLevel(0, &topSurface);
|
||||
if (mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) {
|
||||
if (swapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) {
|
||||
log_warning("graphics::d3d9", "SurfaceHook - GetBackBuffer failed");
|
||||
}
|
||||
|
||||
swapChain->Release();
|
||||
}
|
||||
|
||||
// pre-calculate dimensions used for phase 1
|
||||
const int rectLeft = param.BackBufferWidth;
|
||||
const int rectTop = param.BackBufferHeight;
|
||||
const int w = param.BackBufferWidth;
|
||||
const int h = param.BackBufferHeight;
|
||||
const int rectLeft = backbuffer_width;
|
||||
const int rectTop = backbuffer_height;
|
||||
const int w = backbuffer_width;
|
||||
const int h = backbuffer_height;
|
||||
|
||||
// this code used to clear the surface using ColorFill on every call, but
|
||||
// this turned out to be very expensive, leading to major frame drops in
|
||||
@@ -812,9 +823,7 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
cfg::SCREENRESIZE->need_surface_clean = false;
|
||||
}
|
||||
|
||||
D3DLOCKED_RECT rect;
|
||||
HRESULT hr;
|
||||
topSurface->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
|
||||
// phase 1 - copy the original back buffer onto the new surface.
|
||||
// we draw it 1:1 in the center of the surface.
|
||||
@@ -877,7 +886,6 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
originRect2.left, originRect2.top, originRect2.right, originRect2.bottom);
|
||||
}
|
||||
}
|
||||
topSurface->UnlockRect();
|
||||
|
||||
// phase 2 - viewport calculation - do the actual zoom / offset math
|
||||
// figure out what region of the surface to copy back to the back buffer.
|
||||
@@ -952,7 +960,6 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
// targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
|
||||
|
||||
// phase 3 - draw the surface to back buffer
|
||||
backbuffer->LockRect(&rect, NULL, D3DLOCK_DONOTWAIT);
|
||||
bool use_linear_filter = true;
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
||||
use_linear_filter = cfg::SCREENRESIZE->enable_linear_filter;
|
||||
@@ -961,7 +968,6 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
topSurface, &targetRect,
|
||||
backbuffer, nullptr,
|
||||
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
|
||||
backbuffer->UnlockRect();
|
||||
if (hr != D3D_OK) {
|
||||
log_warning(
|
||||
"graphics::d3d9",
|
||||
@@ -973,10 +979,6 @@ void SurfaceHook(IDirect3DDevice9 *pReal) {
|
||||
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndScene() {
|
||||
WRAP_DEBUG;
|
||||
|
||||
if (cfg::SCREENRESIZE->enable_screen_resize || GRAPHICS_FS_ORIENTATION_SWAP) {
|
||||
SurfaceHook(pReal);
|
||||
}
|
||||
|
||||
static std::once_flag printed;
|
||||
std::call_once(printed, []() {
|
||||
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::EndScene");
|
||||
|
||||
@@ -35,6 +35,10 @@ static const GUID IID_WrappedIDirect3DDevice9 = {
|
||||
0x6dec0d40, 0x1339, 0x4bda, { 0xa5, 0xf2, 0x22, 0x31, 0xd4, 0x1, 0xf, 0xd1 }
|
||||
};
|
||||
|
||||
// applies the image resize / orientation swap by copying the back buffer through an
|
||||
// oversized intermediate surface and back. expects the real (unwrapped) device.
|
||||
void SurfaceHook(IDirect3DDevice9 *pReal);
|
||||
|
||||
struct WrappedIDirect3DDevice9 : IDirect3DDevice9Ex {
|
||||
explicit WrappedIDirect3DDevice9(HWND hFocusWindow, IDirect3DDevice9 *orig)
|
||||
: hFocusWindow(hFocusWindow), pReal(orig), is_d3d9ex(false) {
|
||||
|
||||
@@ -103,19 +103,10 @@ namespace overlay::windows {
|
||||
|
||||
void ScreenResize::build_fullscreen_config() {
|
||||
|
||||
if (avs::game::is_model("KFC")) {
|
||||
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"Warning: Enabling Image Resize uses more GPU\n"
|
||||
"resources and may significantly lower framerate\n"
|
||||
"for songs with Live2D! Results may vary, use at\n"
|
||||
"your own risk.");
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"Warning: Enabling Image Resize uses more GPU\n"
|
||||
"resources and may significantly lower framerate\n"
|
||||
"in some situations! Results may vary, use at\n"
|
||||
"your own risk.");
|
||||
}
|
||||
ImGui::TextColored(ImVec4(1, 0.5f, 0.5f, 1.f),
|
||||
"Warning: Enabling Image Resize uses more GPU\n"
|
||||
"resources and may lower framerate on some\n"
|
||||
"lower-end PCs.");
|
||||
|
||||
// enable checkbox
|
||||
ImGui::Checkbox("Enable", &cfg::SCREENRESIZE->enable_screen_resize);
|
||||
|
||||
Reference in New Issue
Block a user