Files
spice2x.github.io/src/spice2x/hooks/graphics/backends/d3d9/d3d9_device.cpp
T
bicarus-dev 104a9cbffd graphics: rewrite image scaler (#278)
## Link to GitHub Issue, if one exists
Fixes #276 

## Description of change

Note: this change is not compatible with previous screen resize config;
i.e., if you had the zoom setting dialed into a certain view, after this
change it won't be the same area, so you'll have to set up a scene
again. I think this is a worthwhile cost since the old logic is just
broken in weird ways, and preserving old settings is really tedious math
(for a feature that not many people use). Besides, this change will only
use the new Scenes values from the JSON.

Rewrite the DX9 image scaler logic.

* Previously, the rendering surface was fixed at `4096*4096 px`. Now,
this is relative to the backbuffer dimensions, which depends on the game
& respects user provided `-forceres`.
* Remove "center" option, make it the default.

Above two changes fix the aspect ratio issue (scaling in portrait games
not respecting screen ratio) and things breaking at higher res (1080p
when zoomed out far enough, or when forced to run at 4k resolution for
example)

* Use `scenes` leaf of screen resize JSON config for Scene 1 as well,
completely removing ourselves from being compatible with older spice2x
versions for these values.
* Add additional bounds check before calling `StretchRect` to avoid
users getting stuck with a bad layout, which can kill performance.

## Compiling
🦾 

## Testing
Tested 32 bit (popn / ddr), 64 bit (ldj), portrait and landscape modes
(kfc)
2025-03-28 17:23:01 -07:00

1786 lines
53 KiB
C++

#include "d3d9_device.h"
#include <cassert>
#include <mutex>
#include "avs/game.h"
#include "hooks/graphics/graphics.h"
#include "overlay/overlay.h"
#include "util/flags_helper.h"
#include "util/utils.h"
#include "cfg/screen_resize.h"
#include "d3d9_backend.h"
#include "d3d9_texture.h"
#ifndef SPICE64
#include "shaders/vertex_shader.h"
#endif
#define CHECK_RESULT_FMT(x, fmt, ...) \
HRESULT __ret = (x); \
if (GRAPHICS_LOG_HRESULT && FAILED(__ret)) [[unlikely]] { \
log_warning("graphics::d3d9", "{} failed, hr={} " fmt, __FUNCTION__, FMT_HRESULT(__ret), ## __VA_ARGS__); \
} \
return __ret
#define CHECK_RESULT(x) \
HRESULT __ret = (x); \
if (GRAPHICS_LOG_HRESULT && FAILED(__ret)) [[unlikely]] { \
log_warning("graphics::d3d9", "{} failed, hr={}", __FUNCTION__, FMT_HRESULT(__ret)); \
} \
return __ret
// TODO: At some point, support a manual `Reset` call by reloading all textures
constexpr bool CUSTOM_RESET = false;
constexpr D3DFORMAT D3DFMT_DF24 = static_cast<D3DFORMAT>(MAKEFOURCC('D', 'F', '2', '4'));
std::string usage2s(DWORD dwUsage) {
FLAGS_START(dwUsage);
FLAG(dwUsage, D3DUSAGE_RENDERTARGET);
FLAG(dwUsage, D3DUSAGE_DEPTHSTENCIL);
FLAG(dwUsage, D3DUSAGE_WRITEONLY);
FLAG(dwUsage, D3DUSAGE_SOFTWAREPROCESSING);
FLAG(dwUsage, D3DUSAGE_DONOTCLIP);
FLAG(dwUsage, D3DUSAGE_POINTS);
FLAG(dwUsage, D3DUSAGE_RTPATCHES);
FLAG(dwUsage, D3DUSAGE_NPATCHES);
FLAG(dwUsage, D3DUSAGE_DYNAMIC);
FLAG(dwUsage, D3DUSAGE_AUTOGENMIPMAP);
FLAG(dwUsage, D3DUSAGE_DMAP);
FLAGS_END(dwUsage);
}
std::string pool2s(D3DPOOL Pool) {
switch (Pool) {
ENUM_VARIANT(D3DPOOL_DEFAULT);
ENUM_VARIANT(D3DPOOL_MANAGED);
ENUM_VARIANT(D3DPOOL_SYSTEMMEM);
ENUM_VARIANT(D3DPOOL_SCRATCH);
default:
return fmt::format("Pool(0x{:08x})", Pool);
}
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::QueryInterface(
REFIID riid,
void **ppvObj)
{
if (ppvObj == nullptr) {
return E_POINTER;
}
if (riid == IID_WrappedIDirect3DDevice9 ||
riid == IID_IDirect3DDevice9 ||
riid == IID_IDirect3DDevice9Ex)
{
// update to IDirect3DDevice9Ex interface
if (!is_d3d9ex && riid == IID_IDirect3DDevice9Ex) {
IDirect3DDevice9Ex *deviceex = nullptr;
HRESULT ret = pReal->QueryInterface(IID_PPV_ARGS(&deviceex));
if (FAILED(ret) || deviceex == nullptr) {
if (ret != E_NOINTERFACE) {
log_warning("graphics::d3d9",
"failed to upgrade to IDirect3DDevice9Ex, hr={}",
FMT_HRESULT(ret));
}
return E_NOINTERFACE;
}
pReal->Release();
pReal = deviceex;
is_d3d9ex = true;
}
this->AddRef();
*ppvObj = this;
return S_OK;
}
return pReal->QueryInterface(riid, ppvObj);
}
ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::AddRef() {
this->refs++;
return pReal->AddRef();
}
ULONG STDMETHODCALLTYPE WrappedIDirect3DDevice9::Release() {
// get what this thread thinks the reference count is
auto local_refs = --this->refs;
// release owned objects if there are no more references
if (local_refs == 0) {
if (this->main_swapchain) {
this->main_swapchain->Release();
this->main_swapchain = nullptr;
}
if (this->sub_swapchain) {
this->sub_swapchain->Release();
this->sub_swapchain = nullptr;
}
if (this->fake_sub_swapchain) {
this->fake_sub_swapchain->Release();
this->fake_sub_swapchain = nullptr;
}
if (overlay::ENABLED) {
const std::lock_guard<std::mutex> lock(overlay::OVERLAY_MUTEX);
// release overlay if we are the backing device which will release its owned references
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(this->pReal)) {
overlay::OVERLAY.reset();
}
}
}
// get reference count of underlying interface
ULONG refs = this->pReal != nullptr ? this->pReal->Release() : 0;
// the reference counts should be equivalent at this point, so log if they are not
if (local_refs == 0 && refs != 0) {
log_warning("graphics::d3d9",
"reference count for `IDirect3DDevice9` object {} wrapping {} "
"is inconsistent: {}, but expected 0",
fmt::ptr(this),
fmt::ptr(this->pReal),
refs);
refs = 0;
}
if (refs == 0) {
delete this;
}
return refs;
}
/*
* IDirect3DDevice9
*/
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::TestCooperativeLevel() {
WRAP_VERBOSE;
CHECK_RESULT(pReal->TestCooperativeLevel());
}
UINT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetAvailableTextureMem() {
WRAP_DEBUG;
return pReal->GetAvailableTextureMem();
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EvictManagedResources() {
WRAP_VERBOSE;
CHECK_RESULT(pReal->EvictManagedResources());
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDirect3D(
IDirect3D9 **ppD3D9)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->GetDirect3D(ppD3D9));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDeviceCaps(
D3DCAPS9 *pCaps)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->GetDeviceCaps(pCaps));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDisplayMode(
UINT iSwapChain,
D3DDISPLAYMODE *pMode)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->GetDisplayMode(iSwapChain, pMode));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetCreationParameters(
D3DDEVICE_CREATION_PARAMETERS *pParameters)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->GetCreationParameters(pParameters));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetCursorProperties(
UINT XHotSpot,
UINT YHotSpot,
IDirect3DSurface9 *pCursorBitmap)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->SetCursorProperties(XHotSpot, YHotSpot, pCursorBitmap));
}
void STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetCursorPosition(
int X,
int Y,
DWORD Flags)
{
WRAP_VERBOSE;
return pReal->SetCursorPosition(X, Y, Flags);
}
BOOL STDMETHODCALLTYPE WrappedIDirect3DDevice9::ShowCursor(
BOOL bShow)
{
WRAP_VERBOSE;
return pReal->ShowCursor(bShow);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateAdditionalSwapChain(
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DSwapChain9 **ppSwapChain)
{
WRAP_VERBOSE;
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
if (avs::game::is_model({"LDJ", "KFC"})) {
if (SUCCEEDED(hr) && !sub_swapchain) {
sub_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
sub_swapchain->should_run_hooks = false;
} else if (FAILED(hr) && !fake_sub_swapchain) {
log_warning("graphics::d3d9",
"failed to create sub swap chain, hr={}, using fake swap chain",
FMT_HRESULT(hr));
fake_sub_swapchain = new FakeIDirect3DSwapChain9(this, pPresentationParameters, false);
fake_sub_swapchain->AddRef();
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain);
return D3D_OK;
}
}
CHECK_RESULT(hr);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
UINT iSwapChain,
IDirect3DSwapChain9 **ppSwapChain)
{
WRAP_VERBOSE_FMT("GetSwapChain({})", iSwapChain);
if (iSwapChain == 0) {
if (!main_swapchain) {
HRESULT ret = pReal->GetSwapChain(iSwapChain, ppSwapChain);
if (FAILED(ret)) {
log_warning("graphics::d3d9", "failed to get swapchain {}, hr={}",
iSwapChain, FMT_HRESULT(ret));
return ret;
}
main_swapchain = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
}
main_swapchain->AddRef();
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(main_swapchain);
graphics_screens_register(iSwapChain);
return D3D_OK;
}
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC"})) {
if (sub_swapchain) {
sub_swapchain->AddRef();
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain);
graphics_screens_register(iSwapChain);
return D3D_OK;
} else if (fake_sub_swapchain) {
fake_sub_swapchain->AddRef();
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain);
graphics_screens_register(iSwapChain);
return D3D_OK;
}
}
HRESULT ret = pReal->GetSwapChain(iSwapChain, ppSwapChain);
if (GRAPHICS_LOG_HRESULT && FAILED(ret)) {
log_warning("graphics::d3d9", "{} failed, hr={}", __FUNCTION__, FMT_HRESULT(ret));
} else {
graphics_screens_register(iSwapChain);
}
return ret;
}
UINT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetNumberOfSwapChains() {
WRAP_VERBOSE;
UINT n = pReal->GetNumberOfSwapChains();
if (sub_swapchain && avs::game::is_model({"LDJ", "KFC"})) {
n += 1;
}
return n;
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::Reset(
D3DPRESENT_PARAMETERS *pPresentationParameters)
{
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::Reset");
if (pPresentationParameters) {
if (GRAPHICS_WINDOWED) {
pPresentationParameters->Windowed = true;
pPresentationParameters->FullScreen_RefreshRateInHz = 0;
} else if (GRAPHICS_FORCE_REFRESH > 0) {
pPresentationParameters->FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH;
}
}
// reset overlay
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(pReal)) {
overlay::OVERLAY->reset_invalidate();
}
HRESULT res = pReal->Reset(pPresentationParameters);
// recreate overlay
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(pReal) && SUCCEEDED(res)) {
overlay::OVERLAY->reset_recreate();
}
CHECK_RESULT(res);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::Present(
const RECT *pSourceRect,
const RECT *pDestRect,
HWND hDestWindowOverride,
const RGNDATA *pDirtyRegion)
{
WRAP_DEBUG;
static std::once_flag printed;
std::call_once(printed, []() {
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::Present");
});
graphics_d3d9_on_present(hFocusWindow, pReal, this);
CHECK_RESULT(pReal->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetBackBuffer(
UINT iSwapChain,
UINT iBackBuffer,
D3DBACKBUFFER_TYPE Type,
IDirect3DSurface9 **ppBackBuffer)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->GetBackBuffer(iSwapChain, iBackBuffer, Type, ppBackBuffer));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetRasterStatus(
UINT iSwapChain,
D3DRASTER_STATUS *pRasterStatus)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetRasterStatus(iSwapChain, pRasterStatus));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetDialogBoxMode(
BOOL bEnableDialogs)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetDialogBoxMode(bEnableDialogs));
}
void STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetGammaRamp(
UINT iSwapChain,
DWORD Flags,
const D3DGAMMARAMP *pRamp)
{
WRAP_DEBUG;
pReal->SetGammaRamp(iSwapChain, Flags, pRamp);
}
void STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetGammaRamp(
UINT iSwapChain,
D3DGAMMARAMP *pRamp)
{
WRAP_DEBUG;
pReal->GetGammaRamp(iSwapChain, pRamp);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateTexture(
UINT Width,
UINT Height,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DTexture9 **ppTexture,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateTexture({}, {}, {}, {}, {}, {})",
Width, Height, Levels, usage2s(Usage), Format, pool2s(Pool));
// fix nvidia proprietary depthstencil texture format
if (avs::game::is_model("I36") && (Usage & D3DUSAGE_DEPTHSTENCIL) && Format == D3DFMT_DF24) {
HRESULT ret = pReal->CreateTexture(Width, Height, Levels,
Usage, Format, Pool, ppTexture, pSharedHandle);
if (FAILED(ret)) {
Format = D3DFMT_D24S8;
} else {
return ret;
}
}
#ifndef SPICE64
// texture fix for iidx (iix18 resort anthem, iidx19 lincle)
if (avs::game::is_model({ "JDZ", "KDZ" })) {
// patch texture format type from 15-bit+alpha to 32-bit.
if (Width == 256 && Height == 256 && Levels == 1 && Usage == D3DUSAGE_RENDERTARGET
&& Format == D3DFMT_A1R5G5B5 && Pool == D3DPOOL_DEFAULT && !pSharedHandle) {
Format = D3DFMT_A8R8G8B8;
}
}
#endif
HRESULT res = pReal->CreateTexture(Width, Height, Levels,
Usage, Format, Pool, ppTexture, pSharedHandle);
if (CUSTOM_RESET && ppTexture && Pool == D3DPOOL_MANAGED && SUCCEEDED(res)) {
*ppTexture = new WrappedIDirect3DTexture9(this, *ppTexture);
}
CHECK_RESULT_FMT(res, "({}, {}, {}, {}, {}, {})",
Width, Height, Levels, usage2s(Usage), Format, pool2s(Pool));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateVolumeTexture(
UINT Width,
UINT Height,
UINT Depth,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DVolumeTexture9 **ppVolumeTexture,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateVolumeTexture({}, {}, {}, {}, {}, {}, {})",
Width, Height, Depth, Levels, usage2s(Usage), Format, pool2s(Pool));
CHECK_RESULT(pReal->CreateVolumeTexture(Width, Height, Depth, Levels,
Usage, Format, Pool, ppVolumeTexture, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateCubeTexture(
UINT EdgeLength,
UINT Levels,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DCubeTexture9 **ppCubeTexture,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateCubeTexture({}, {}, {}, {}, {})",
EdgeLength, Levels, usage2s(Usage), Format, pool2s(Pool));
CHECK_RESULT(pReal->CreateCubeTexture(
EdgeLength, Levels, Usage, Format, Pool, ppCubeTexture, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateVertexBuffer(
UINT Length,
DWORD Usage,
DWORD FVF,
D3DPOOL Pool,
IDirect3DVertexBuffer9 **ppVertexBuffer,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateVertexBuffer({}, {}, {}, {})", Length, usage2s(Usage), FVF, pool2s(Pool));
CHECK_RESULT(pReal->CreateVertexBuffer(Length, Usage, FVF, Pool, ppVertexBuffer, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateIndexBuffer(
UINT Length,
DWORD Usage,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DIndexBuffer9 **ppIndexBuffer,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateIndexBuffer({}, {}, {}, {})", Length, usage2s(Usage), Format, pool2s(Pool));
CHECK_RESULT(pReal->CreateIndexBuffer(Length, Usage, Format, Pool, ppIndexBuffer, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateRenderTarget(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
BOOL Lockable,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateRenderTarget({}, {}, {}, {}, {}, {})",
Width, Height, Format, MultiSample, MultisampleQuality, Lockable);
CHECK_RESULT(pReal->CreateRenderTarget(Width, Height, Format,
MultiSample, MultisampleQuality, Lockable, ppSurface, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateDepthStencilSurface(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
BOOL Discard,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->CreateDepthStencilSurface(Width, Height, Format,
MultiSample, MultisampleQuality, Discard, ppSurface, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::UpdateSurface(
IDirect3DSurface9 *pSourceSurface,
const RECT *pSourceRect,
IDirect3DSurface9 *pDestinationSurface,
const POINT *pDestPoint)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->UpdateSurface(pSourceSurface, pSourceRect, pDestinationSurface, pDestPoint));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::UpdateTexture(
IDirect3DBaseTexture9 *pSourceTexture,
IDirect3DBaseTexture9 *pDestinationTexture)
{
WRAP_DEBUG;
if (CUSTOM_RESET) {
#ifdef __GNUC__
// Do a pointer compare rather than `QueryInterface` because it is a lot cheaper than incrementing
// the reference count
if (pSourceTexture && (&pSourceTexture->QueryInterface) == WrappedIDirect3DTexture9::QueryInterface) {
pSourceTexture = static_cast<WrappedIDirect3DTexture9 *>(pSourceTexture)->pReal;
}
if (pDestinationTexture &&
(&pDestinationTexture->QueryInterface) == WrappedIDirect3DTexture9::QueryInterface)
{
pDestinationTexture = static_cast<WrappedIDirect3DTexture9 *>(pDestinationTexture)->pReal;
}
#else
void *dummy = nullptr;
if (pSourceTexture && SUCCEEDED(pSourceTexture->QueryInterface(
IID_WrappedIDirect3DTexture9, &dummy)))
{
pSourceTexture = static_cast<WrappedIDirect3DTexture9 *>(pSourceTexture)->pReal;
}
if (pDestinationTexture && SUCCEEDED(pDestinationTexture->QueryInterface(
IID_WrappedIDirect3DTexture9, &dummy)))
{
pDestinationTexture = static_cast<WrappedIDirect3DTexture9 *>(pDestinationTexture)->pReal;
}
#endif
}
CHECK_RESULT(pReal->UpdateTexture(pSourceTexture, pDestinationTexture));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetRenderTargetData(
IDirect3DSurface9 *pRenderTarget,
IDirect3DSurface9 *pDestSurface)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetRenderTargetData(pRenderTarget, pDestSurface));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetFrontBufferData(
UINT iSwapChain,
IDirect3DSurface9 *pDestSurface)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetFrontBufferData(iSwapChain, pDestSurface));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::StretchRect(
IDirect3DSurface9 *pSourceSurface,
const RECT *pSourceRect,
IDirect3DSurface9 *pDestSurface,
const RECT *pDestRect,
D3DTEXTUREFILTERTYPE Filter)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->StretchRect(pSourceSurface, pSourceRect, pDestSurface, pDestRect, Filter));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::ColorFill(
IDirect3DSurface9 *pSurface,
const RECT *pRect,
D3DCOLOR color)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->ColorFill(pSurface, pRect, color));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateOffscreenPlainSurface(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle)
{
WRAP_VERBOSE_FMT("CreateOffscreenPlainSurface({}, {}, {}, {})",
Width, Height, Format, pool2s(Pool));
CHECK_RESULT(pReal->CreateOffscreenPlainSurface(
Width, Height, Format, Pool, ppSurface, pSharedHandle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetRenderTarget(
DWORD RenderTargetIndex,
IDirect3DSurface9 *pRenderTarget)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetRenderTarget(RenderTargetIndex, pRenderTarget));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetRenderTarget(
DWORD RenderTargetIndex,
IDirect3DSurface9 **ppRenderTarget)
{
WRAP_DEBUG;
return pReal->GetRenderTarget(RenderTargetIndex, ppRenderTarget);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetDepthStencilSurface(
IDirect3DSurface9 *pNewZStencil)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetDepthStencilSurface(pNewZStencil));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDepthStencilSurface(
IDirect3DSurface9 **ppZStencilSurface)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetDepthStencilSurface(ppZStencilSurface));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::BeginScene() {
WRAP_DEBUG;
static std::once_flag printed;
std::call_once(printed, []() {
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::BeginScene");
});
return pReal->BeginScene();
}
static IDirect3DSurface9 *topSurface = nullptr;
static IDirect3DSurface9 *backbuffer = nullptr;
static LPDIRECT3DSWAPCHAIN9 mSwapChain = nullptr;
static IDirect3DTexture9* tex;
static UINT topSurface_width = 0;
static UINT topSurface_height = 0;
static float topSurface_aspect_ratio = 1.f;
void SurfaceHook(IDirect3DDevice9 *pReal) {
D3DPRESENT_PARAMETERS param {};
pReal->GetSwapChain(0, &mSwapChain);
mSwapChain->GetPresentParameters(&param);
// 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
// sufficient buffer space (black border) around the original image
//
// (only done once)
if (!topSurface) {
topSurface_width = param.BackBufferWidth * 3;
topSurface_height = param.BackBufferHeight * 3;
topSurface_aspect_ratio = (float)topSurface_width / (float)topSurface_height;
if (pReal->CreateTexture(topSurface_width, topSurface_height, 1,
D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, &tex, NULL) != D3D_OK) {
log_warning("graphics::d3d9", "SurfaceHook - create texture failed");
}
log_misc(
"graphics::d3d9", "SurfaceHook - Backbuffer: {} {} {}",
param.BackBufferWidth, param.BackBufferHeight, param.BackBufferCount);
tex->GetSurfaceLevel(0, &topSurface);
if (mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer) != D3D_OK) {
log_warning("graphics::d3d9", "SurfaceHook - GetBackBuffer failed");
}
}
// 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;
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.
// if orientation is swapped, fix up the rect and draw it in the center.
//
// for this phase we always render with the same rect so that it's always overwritten
// by a new frame on the same spot.
RECT originRect {
rectLeft,
rectTop,
(LONG)(rectLeft + w),
(LONG)(rectTop + h),
};
if (GRAPHICS_FS_ORIENTATION_SWAP) {
originRect.left = (topSurface_width / 2) - (GRAPHICS_FS_ORIGINAL_WIDTH / 2);
originRect.top = (topSurface_height / 2) - (GRAPHICS_FS_ORIGINAL_HEIGHT / 2);
originRect.right = originRect.left + GRAPHICS_FS_ORIGINAL_WIDTH;
originRect.bottom = originRect.top + GRAPHICS_FS_ORIGINAL_HEIGHT;
}
// log_misc(
// "graphics::d3d9", "originRect: {} {} {} {}",
// originRect.left, originRect.top, originRect.right, originRect.bottom);
hr = pReal->StretchRect(
backbuffer, nullptr,
topSurface, &originRect,
D3DTEXF_LINEAR);
if (hr != D3D_OK) {
log_warning(
"graphics::d3d9",
"SurfaceHook - StretchRect backbuffer failed, rect: {} {} {} {}",
originRect.left, originRect.top, originRect.right, originRect.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.
RECT targetRect {
rectLeft,
rectTop,
(LONG)(rectLeft + w),
(LONG)(rectTop + h),
};
if (cfg::SCREENRESIZE->enable_screen_resize) {
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
auto w_new = w / scene.scale_x;
auto h_new = h / scene.scale_y;
// make sure the scaling is within bounds
if (cfg::SCREENRESIZE->client_keep_aspect_ratio) {
if (w_new > topSurface_width || h_new > topSurface_height) {
w_new = topSurface_width;
h_new = topSurface_height;
}
} else {
w_new = MIN(w_new, topSurface_width);
h_new = MIN(h_new, topSurface_height);
}
targetRect.left = (topSurface_width / 2) - (w_new / 2) - scene.offset_x;
targetRect.top = (topSurface_height / 2) - (h_new / 2) - scene.offset_y;
targetRect.right = targetRect.left + w_new;
targetRect.bottom = targetRect.top + h_new;
// boundary check to prevent StretchRect failures
if (targetRect.left < 0) {
targetRect.left = 0;
targetRect.right = w_new;
} else if (targetRect.right > (LONG)topSurface_width) {
targetRect.left = topSurface_width - w_new;
targetRect.right = topSurface_width;
}
if (targetRect.top < 0) {
targetRect.top = 0;
targetRect.bottom = h_new;
} else if (targetRect.bottom > (LONG)topSurface_height) {
targetRect.top = topSurface_height - h_new;
targetRect.bottom = topSurface_height;
}
} else if (GRAPHICS_FS_ORIENTATION_SWAP) {
// increase the viewport to fit the image on screen (effectively, zoom out a little)
if (GRAPHICS_FS_ORIGINAL_WIDTH < GRAPHICS_FS_ORIGINAL_HEIGHT) {
// portrait game on landscape display
// height should match the original image
targetRect.top = originRect.top;
targetRect.bottom = originRect.bottom;
// width of viewport should be wider, effectively shrinking the image on x-axis when rendered
const auto w_new = GRAPHICS_FS_ORIGINAL_HEIGHT * topSurface_aspect_ratio;
targetRect.left = (topSurface_width / 2) - (w_new / 2);
targetRect.right = targetRect.left + w_new;
} else {
// landscape game on portrait display
targetRect.left = originRect.left;
targetRect.right = originRect.right;
// height of viewport should be taller, effectively shrinking the image on y-axis when rendered
const auto h_new = GRAPHICS_FS_ORIGINAL_WIDTH / topSurface_aspect_ratio;
targetRect.top = (topSurface_height / 2) - (h_new / 2);
targetRect.bottom = targetRect.top + h_new;
}
}
// log_misc("graphics::d3d9", "targetRect: {} {} {} {}",
// 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;
}
hr = pReal->StretchRect(
topSurface, &targetRect,
backbuffer, nullptr,
use_linear_filter ? D3DTEXF_LINEAR : D3DTEXF_NONE);
backbuffer->UnlockRect();
if (hr != D3D_OK) {
log_warning(
"graphics::d3d9",
"SurfaceHook - StretchRect targetRect failed, you must reset image scaling to default values! rect: {} {} {} {}",
targetRect.left, targetRect.top, targetRect.right, targetRect.bottom);
}
}
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");
});
return pReal->EndScene();
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::Clear(
DWORD Count,
const D3DRECT *pRects,
DWORD Flags,
D3DCOLOR Color,
float Z,
DWORD Stencil)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->Clear(Count, pRects, Flags, Color, Z, Stencil));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetTransform(
D3DTRANSFORMSTATETYPE State,
const D3DMATRIX *pMatrix)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetTransform(State, pMatrix));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetTransform(
D3DTRANSFORMSTATETYPE State,
D3DMATRIX *pMatrix)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetTransform(State, pMatrix));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::MultiplyTransform(
D3DTRANSFORMSTATETYPE State,
const D3DMATRIX *pMatrix)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->MultiplyTransform(State, pMatrix));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetViewport(
const D3DVIEWPORT9 *pViewport)
{
WRAP_DEBUG;
#ifndef SPICE64
if (avs::game::is_model({ "JDZ", "KDZ", "K39", "L39", "LBR" })) {
// Set vertex shader constant to half pixel viewport offset
const float ConstantHalfTexelFixupOffset[2] = {-1.0F / (float)pViewport->Width, 1.0F / (float)pViewport->Height};
HRESULT ret = pReal->SetVertexShaderConstantF(63, ConstantHalfTexelFixupOffset, sizeof(ConstantHalfTexelFixupOffset) / sizeof(ConstantHalfTexelFixupOffset[0]));
CHECK_RESULT(ret);
}
#endif
CHECK_RESULT(pReal->SetViewport(pViewport));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetViewport(
D3DVIEWPORT9 *pViewport)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetViewport(pViewport));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetMaterial(
const D3DMATERIAL9 *pMaterial)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetMaterial(pMaterial));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetMaterial(
D3DMATERIAL9 *pMaterial)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetMaterial(pMaterial));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetLight(
DWORD Index,
const D3DLIGHT9 *pLight)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetLight(Index, pLight));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetLight(
DWORD Index,
D3DLIGHT9 *pLight)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetLight(Index, pLight));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::LightEnable(
DWORD Index,
BOOL Enable)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->LightEnable(Index, Enable));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetLightEnable(
DWORD Index,
BOOL *pEnable)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetLightEnable(Index, pEnable));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetClipPlane(
DWORD Index,
const float *pPlane)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetClipPlane(Index, pPlane));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetClipPlane(
DWORD Index,
float *pPlane)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetClipPlane(Index, pPlane));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetRenderState(
D3DRENDERSTATETYPE State,
DWORD Value)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetRenderState(State, Value));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetRenderState(
D3DRENDERSTATETYPE State,
DWORD *pValue)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetRenderState(State, pValue));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateStateBlock(
D3DSTATEBLOCKTYPE Type,
IDirect3DStateBlock9 **ppSB)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->CreateStateBlock(Type, ppSB));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::BeginStateBlock() {
WRAP_DEBUG;
return pReal->BeginStateBlock();
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::EndStateBlock(
IDirect3DStateBlock9 **ppSB)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->EndStateBlock(ppSB));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetClipStatus(
const D3DCLIPSTATUS9 *pClipStatus)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetClipStatus(pClipStatus));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetClipStatus(
D3DCLIPSTATUS9 *pClipStatus)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetClipStatus(pClipStatus));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetTexture(
DWORD Stage,
IDirect3DBaseTexture9 **ppTexture)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetTexture(Stage, ppTexture));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetTexture(
DWORD Stage,
IDirect3DBaseTexture9 *pTexture)
{
WRAP_DEBUG_FMT("SetTexture({}, {})", Stage, fmt::ptr(pTexture));
if (CUSTOM_RESET) {
void *dummy = nullptr;
if (pTexture && SUCCEEDED(pTexture->QueryInterface(
IID_WrappedIDirect3DTexture9, &dummy)))
{
pTexture = static_cast<WrappedIDirect3DTexture9 *>(pTexture)->pReal;
}
}
CHECK_RESULT(pReal->SetTexture(Stage, pTexture));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD *pValue)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetTextureStageState(Stage, Type, pValue));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetTextureStageState(
DWORD Stage,
D3DTEXTURESTAGESTATETYPE Type,
DWORD Value)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetTextureStageState(Stage, Type, Value));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSamplerState(
DWORD Sampler,
D3DSAMPLERSTATETYPE Type,
DWORD *pValue)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetSamplerState(Sampler, Type, pValue));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetSamplerState(
DWORD Sampler,
D3DSAMPLERSTATETYPE Type,
DWORD Value)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetSamplerState(Sampler, Type, Value));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::ValidateDevice(
DWORD *pNumPasses)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->ValidateDevice(pNumPasses));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPaletteEntries(
UINT PaletteNumber,
const PALETTEENTRY *pEntries)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetPaletteEntries(PaletteNumber, pEntries));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetPaletteEntries(
UINT PaletteNumber,
PALETTEENTRY *pEntries)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetPaletteEntries(PaletteNumber, pEntries));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetCurrentTexturePalette(
UINT PaletteNumber)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetCurrentTexturePalette(PaletteNumber));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetCurrentTexturePalette(
UINT *PaletteNumber)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetCurrentTexturePalette(PaletteNumber));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetScissorRect(
const RECT *pRect)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetScissorRect(pRect));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetScissorRect(
RECT *pRect)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetScissorRect(pRect));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetSoftwareVertexProcessing(
BOOL bSoftware)
{
WRAP_VERBOSE;
CHECK_RESULT(pReal->SetSoftwareVertexProcessing(bSoftware));
}
BOOL STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSoftwareVertexProcessing() {
WRAP_VERBOSE;
return pReal->GetSoftwareVertexProcessing();
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetNPatchMode(float nSegments) {
WRAP_DEBUG;
CHECK_RESULT(pReal->SetNPatchMode(nSegments));
}
float STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetNPatchMode() {
WRAP_DEBUG;
return pReal->GetNPatchMode();
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->DrawPrimitive(PrimitiveType, StartVertex, PrimitiveCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawIndexedPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
INT BaseVertexIndex,
UINT MinVertexIndex,
UINT NumVertices,
UINT StartIndex,
UINT PrimitiveCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->DrawIndexedPrimitive(
PrimitiveType, BaseVertexIndex,
MinVertexIndex, NumVertices,
StartIndex, PrimitiveCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT PrimitiveCount,
const void *pVertexStreamZeroData,
UINT VertexStreamZeroStride)
{
WRAP_DEBUG_FMT("DrawPrimitiveUP({}, {}, {}, {})",
PrimitiveType, PrimitiveCount,
fmt::ptr(pVertexStreamZeroData), VertexStreamZeroStride);
CHECK_RESULT(pReal->DrawPrimitiveUP(
PrimitiveType, PrimitiveCount,
pVertexStreamZeroData, VertexStreamZeroStride));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawIndexedPrimitiveUP(
D3DPRIMITIVETYPE PrimitiveType,
UINT MinVertexIndex,
UINT NumVertices,
UINT PrimitiveCount,
const void *pIndexData,
D3DFORMAT IndexDataFormat,
const void *pVertexStreamZeroData,
UINT VertexStreamZeroStride)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->DrawIndexedPrimitiveUP(
PrimitiveType, MinVertexIndex, NumVertices, PrimitiveCount, pIndexData,
IndexDataFormat, pVertexStreamZeroData, VertexStreamZeroStride));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::ProcessVertices(
UINT SrcStartIndex,
UINT DestIndex,
UINT VertexCount,
IDirect3DVertexBuffer9 *pDestBuffer,
IDirect3DVertexDeclaration9 *pVertexDecl,
DWORD Flags)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->ProcessVertices(SrcStartIndex, DestIndex,
VertexCount, pDestBuffer, pVertexDecl, Flags));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateVertexDeclaration(
const D3DVERTEXELEMENT9 *pVertexElements,
IDirect3DVertexDeclaration9 **ppDecl)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->CreateVertexDeclaration(pVertexElements, ppDecl));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexDeclaration(
IDirect3DVertexDeclaration9 *pDecl)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetVertexDeclaration(pDecl));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetVertexDeclaration(
IDirect3DVertexDeclaration9 **ppDecl)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetVertexDeclaration(ppDecl));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetFVF(
DWORD FVF)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetFVF(FVF));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetFVF(
DWORD *pFVF)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetFVF(pFVF));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateVertexShader(
const DWORD *pFunction,
IDirect3DVertexShader9 **ppShader)
{
WRAP_VERBOSE_FMT("CreateVertexShader({})", fmt::ptr(pFunction));
CHECK_RESULT(pReal->CreateVertexShader(pFunction, ppShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexShader(
IDirect3DVertexShader9 *pShader)
{
WRAP_DEBUG_FMT("SetVertexShader({})", fmt::ptr(pShader));
#ifndef SPICE64
// diagonal line fix
if (avs::game::is_model({ "JDZ", "KDZ", "K39", "L39", "LBR" })) {
if (!vertex_shader) {
log_info("graphics::d3d9", "initializing and setting up vertex shaders");
// create and set up the vertex shader shim
HRESULT ret = pReal->CreateVertexShader((const DWORD *) g_vs11_vs_main, &vertex_shader);
if (FAILED(ret)) {
log_warning("graphics::d3d9", "CreateVertexShader failed, hr={}", FMT_HRESULT(ret));
}
}
if (pShader != nullptr) {
pShader = vertex_shader;
}
}
#endif
CHECK_RESULT(pReal->SetVertexShader(pShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetVertexShader(
IDirect3DVertexShader9 **ppShader)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetVertexShader(ppShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexShaderConstantF(
UINT StartRegister,
const float *pConstantData,
UINT Vector4fCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetVertexShaderConstantF(
StartRegister, pConstantData, Vector4fCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetVertexShaderConstantF(
UINT StartRegister,
float *pConstantData,
UINT Vector4fCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetVertexShaderConstantF(
StartRegister, pConstantData, Vector4fCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexShaderConstantI(
UINT StartRegister,
const int *pConstantData,
UINT Vector4iCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetVertexShaderConstantI(
StartRegister, pConstantData, Vector4iCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetVertexShaderConstantI(
UINT StartRegister,
int *pConstantData,
UINT Vector4iCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetVertexShaderConstantI(
StartRegister, pConstantData, Vector4iCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexShaderConstantB(
UINT StartRegister,
const BOOL *pConstantData,
UINT BoolCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetVertexShaderConstantB(
StartRegister, pConstantData, BoolCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetVertexShaderConstantB(
UINT StartRegister,
BOOL *pConstantData,
UINT BoolCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetVertexShaderConstantB(
StartRegister, pConstantData, BoolCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9 *pStreamData,
UINT OffsetInBytes,
UINT Stride)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetStreamSource(
StreamNumber, pStreamData, OffsetInBytes, Stride));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetStreamSource(
UINT StreamNumber,
IDirect3DVertexBuffer9 **ppStreamData,
UINT *OffsetInBytes,
UINT *pStride)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetStreamSource(
StreamNumber, ppStreamData, OffsetInBytes, pStride));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetStreamSourceFreq(
UINT StreamNumber,
UINT Divider)
{
WRAP_DEBUG;
return pReal->SetStreamSourceFreq(StreamNumber, Divider);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetStreamSourceFreq(
UINT StreamNumber,
UINT *Divider)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetStreamSourceFreq(StreamNumber, Divider));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetIndices(
IDirect3DIndexBuffer9 *pIndexData)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetIndices(pIndexData));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetIndices(
IDirect3DIndexBuffer9 **ppIndexData)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetIndices(ppIndexData));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreatePixelShader(
const DWORD *pFunction,
IDirect3DPixelShader9 **ppShader)
{
WRAP_VERBOSE_FMT("CreatePixelShader({})", fmt::ptr(pFunction));
CHECK_RESULT(pReal->CreatePixelShader(pFunction, ppShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPixelShader(
IDirect3DPixelShader9 *pShader)
{
WRAP_DEBUG_FMT("SetPixelShader({})", fmt::ptr(pShader));
CHECK_RESULT(pReal->SetPixelShader(pShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetPixelShader(
IDirect3DPixelShader9 **ppShader)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetPixelShader(ppShader));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPixelShaderConstantF(
UINT StartRegister,
const float *pConstantData,
UINT Vector4fCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetPixelShaderConstantF(
StartRegister, pConstantData, Vector4fCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetPixelShaderConstantF(
UINT StartRegister,
float *pConstantData,
UINT Vector4fCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetPixelShaderConstantF(
StartRegister, pConstantData, Vector4fCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPixelShaderConstantI(
UINT StartRegister,
const int *pConstantData,
UINT Vector4iCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetPixelShaderConstantI(
StartRegister, pConstantData, Vector4iCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetPixelShaderConstantI(
UINT StartRegister,
int *pConstantData,
UINT Vector4iCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetPixelShaderConstantI(
StartRegister, pConstantData, Vector4iCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPixelShaderConstantB(
UINT StartRegister,
const BOOL *pConstantData,
UINT BoolCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->SetPixelShaderConstantB(
StartRegister, pConstantData, BoolCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetPixelShaderConstantB(
UINT StartRegister,
BOOL *pConstantData,
UINT BoolCount)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->GetPixelShaderConstantB(
StartRegister, pConstantData, BoolCount));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawRectPatch(
UINT Handle,
const float *pNumSegs,
const D3DRECTPATCH_INFO *pRectPatchInfo)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->DrawRectPatch(Handle, pNumSegs, pRectPatchInfo));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DrawTriPatch(
UINT Handle,
const float *pNumSegs,
const D3DTRIPATCH_INFO *pTriPatchInfo)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->DrawTriPatch(Handle, pNumSegs, pTriPatchInfo));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::DeletePatch(UINT Handle) {
WRAP_VERBOSE;
CHECK_RESULT(pReal->DeletePatch(Handle));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateQuery(
D3DQUERYTYPE Type,
IDirect3DQuery9 **ppQuery)
{
WRAP_DEBUG;
CHECK_RESULT(pReal->CreateQuery(Type, ppQuery));
}
/*
* IDirect3DDevice9Ex
*/
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetConvolutionMonoKernel(
UINT width,
UINT height,
float *rows,
float *columns)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->SetConvolutionMonoKernel(
width, height, rows, columns));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::ComposeRects(
IDirect3DSurface9 *pSrc,
IDirect3DSurface9 *pDst,
IDirect3DVertexBuffer9 *pSrcRectDescs,
UINT NumRects,
IDirect3DVertexBuffer9 *pDstRectDescs,
D3DCOMPOSERECTSOP Operation,
int Xoffset,
int Yoffset)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->ComposeRects(
pSrc, pDst, pSrcRectDescs, NumRects, pDstRectDescs,
Operation, Xoffset, Yoffset));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::PresentEx(
const RECT *pSourceRect,
const RECT *pDestRect,
HWND hDestWindowOverride,
const RGNDATA *pDirtyRegion,
DWORD dwFlags)
{
WRAP_DEBUG;
assert(is_d3d9ex);
static std::once_flag printed;
std::call_once(printed, []() {
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::PresentEx");
});
graphics_d3d9_on_present(hFocusWindow, pReal, this);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->PresentEx(
pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetGPUThreadPriority(
INT *pPriority)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->GetGPUThreadPriority(pPriority));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetGPUThreadPriority(
INT Priority)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->SetGPUThreadPriority(Priority));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::WaitForVBlank(
UINT iSwapChain)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->WaitForVBlank(iSwapChain));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CheckResourceResidency(
IDirect3DResource9 **pResourceArray,
UINT32 NumResources)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->CheckResourceResidency(
pResourceArray, NumResources));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetMaximumFrameLatency(
UINT MaxLatency)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->SetMaximumFrameLatency(MaxLatency));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetMaximumFrameLatency(
UINT *pMaxLatency)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->GetMaximumFrameLatency(pMaxLatency));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CheckDeviceState(HWND hDestinationWindow) {
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->CheckDeviceState(hDestinationWindow));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateRenderTargetEx(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
BOOL Lockable,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle,
DWORD Usage)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->CreateRenderTargetEx(
Width, Height, Format, MultiSample,
MultisampleQuality, Lockable, ppSurface, pSharedHandle, Usage));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateOffscreenPlainSurfaceEx(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DPOOL Pool,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle,
DWORD Usage)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->CreateOffscreenPlainSurfaceEx(
Width, Height, Format, Pool, ppSurface, pSharedHandle, Usage));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::CreateDepthStencilSurfaceEx(
UINT Width,
UINT Height,
D3DFORMAT Format,
D3DMULTISAMPLE_TYPE MultiSample,
DWORD MultisampleQuality,
BOOL Discard,
IDirect3DSurface9 **ppSurface,
HANDLE *pSharedHandle,
DWORD Usage)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->CreateDepthStencilSurfaceEx(
Width, Height, Format, MultiSample,
MultisampleQuality, Discard, ppSurface, pSharedHandle, Usage));
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::ResetEx(
D3DPRESENT_PARAMETERS *pPresentationParameters,
D3DDISPLAYMODEEX *pFullscreenDisplayMode)
{
WRAP_DEBUG;
assert(is_d3d9ex);
log_misc("graphics::d3d9", "WrappedIDirect3DDevice9::ResetEx");
if (GRAPHICS_WINDOWED) {
if (pPresentationParameters) {
pPresentationParameters->Windowed = true;
pPresentationParameters->FullScreen_RefreshRateInHz = 0;
}
pFullscreenDisplayMode = nullptr;
} else if (GRAPHICS_FORCE_REFRESH > 0) {
if (pPresentationParameters) {
pPresentationParameters->FullScreen_RefreshRateInHz = GRAPHICS_FORCE_REFRESH;
}
if (pFullscreenDisplayMode) {
pFullscreenDisplayMode->RefreshRate = GRAPHICS_FORCE_REFRESH;
}
}
// reset overlay
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(pReal)) {
overlay::OVERLAY->reset_invalidate();
}
HRESULT res = static_cast<IDirect3DDevice9Ex *>(pReal)->ResetEx(
pPresentationParameters, pFullscreenDisplayMode);
// recreate overlay
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(pReal) && SUCCEEDED(res)) {
overlay::OVERLAY->reset_recreate();
}
CHECK_RESULT(res);
}
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDisplayModeEx(
UINT iSwapChain,
D3DDISPLAYMODEEX *pMode,
D3DDISPLAYROTATION *pRotation)
{
WRAP_DEBUG;
assert(is_d3d9ex);
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->GetDisplayModeEx(
iSwapChain, pMode, pRotation));
}