## Link to GitHub Issue or related Pull Request, if one exists #0 ## Description of change Significantly speeds up API screen capture and D3D9 screenshots saving. Two reasons for doing this: 1. We now have a 4K game (GITADORA) and existing capture code was taking multiple seconds. 2. Renewed user interest on streaming as we have a couple more companion apps in active development. **API screen capture (streaming), 1280x720:** 14.3ms -> 6.3ms per frame. Back buffer copies go to pooled `D3DPOOL_SYSTEMMEM` surfaces via `GetRenderTargetData` instead of allocating a lockable render target every frame, and TooJpeg is replaced with libjpeg-turbo (encode 9.8ms -> 3.0ms). MSAA remains unsupported **Screenshots for GITADORA arena model, across 4 screens with one of them 4K**: 4068ms -> 124ms. `D3DXSaveSurfaceToFileA` is replaced with fpng (encode 4043ms -> 76ms) and the screens encode in parallel. Dropping D3DX also removes the `d3dx9_43.dll` ... `d3dx9_24.dll` probing loop, so screenshots no longer fail outright on machines with no D3DX9 runtime installed. Screenshot surfaces are read on the present thread, so no D3D call reaches another thread for screenshots. This fixes a hang in DDR X2 introduced earlier in the branch: its device has no internal locking, and reading the surface on a pool thread while the present thread sat inside `GetRenderTargetData` left the game's own render thread deadlocked. ## Testing - **GITADORA** (arena model, D3D9Ex, 4K main plus three subscreens, windowed) with `-screenshotsub`: three sets of four screenshots, images verified correct. Completion order differs between sets, so the screens really are encoding in parallel. - **LovePlus** (KLP, plain D3D9, 768x1360): covers the inline path used by games whose image processing must not leave the present thread. - **API screen capture** through a companion app: live video correct throughout. - **Print Screen** bound as the screenshot key: the clipboard copy succeeded on every shot. - Quitting the game after capturing leaves no `IDirect3DDevice9` reference count warning, so the pooled readback surfaces are released along with the device.
2383 lines
75 KiB
C++
2383 lines
75 KiB
C++
#include "d3d9_device.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <climits>
|
|
#include <mutex>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "avs/game.h"
|
|
#include "games/gitadora/gitadora.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_live2d.h"
|
|
#include "d3d9_readback.h"
|
|
#include "d3d9_texture.h"
|
|
|
|
#ifndef SPICE64
|
|
#include "shaders/vertex_shader.h"
|
|
#endif
|
|
|
|
// maps arena's cached additional swap chains (SMALL, LEFT, RIGHT) to screen numbers.
|
|
// MAIN is the implicit swap chain, is not in those slots, and is always screen 0.
|
|
// screen 1 is the subscreen for every other game, so SMALL takes that number here too.
|
|
static constexpr int GFDM_ARENA_SLOT_SCREENS[] { 1, 2, 3 };
|
|
|
|
#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'));
|
|
auto format_as(D3DPOOL f) {
|
|
return fmt::underlying(f);
|
|
}
|
|
|
|
auto format_as(D3DFORMAT f) {
|
|
return fmt::underlying(f);
|
|
}
|
|
|
|
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_IUnknown && is_gfdm_two_head_exclusive()) ||
|
|
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->implicit_sub_swapchain) {
|
|
this->implicit_sub_swapchain->Release();
|
|
this->implicit_sub_swapchain = nullptr;
|
|
}
|
|
for (auto &sc : this->sub_swapchain) {
|
|
if (sc) {
|
|
sc->Release();
|
|
sc = nullptr;
|
|
}
|
|
}
|
|
for (auto &sc : this->fake_sub_swapchain) {
|
|
if (sc) {
|
|
sc->Release();
|
|
sc = nullptr;
|
|
}
|
|
}
|
|
|
|
d3d9_readback::release_device_resources(this->pReal);
|
|
|
|
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;
|
|
if (is_gfdm_two_head_exclusive() && gfdm_parent_d3d != nullptr) {
|
|
if (ppD3D9 == nullptr) {
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
gfdm_parent_d3d->AddRef();
|
|
*ppD3D9 = gfdm_parent_d3d;
|
|
return D3D_OK;
|
|
}
|
|
|
|
CHECK_RESULT(pReal->GetDirect3D(ppD3D9));
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDeviceCaps(
|
|
D3DCAPS9 *pCaps)
|
|
{
|
|
WRAP_VERBOSE;
|
|
HRESULT result = pReal->GetDeviceCaps(pCaps);
|
|
if (SUCCEEDED(result) && is_gfdm_two_head_exclusive() && pCaps != nullptr) {
|
|
pCaps->NumberOfAdaptersInGroup = GFDM_LOGICAL_HEAD_COUNT;
|
|
pCaps->MasterAdapterOrdinal = 0;
|
|
pCaps->AdapterOrdinalInGroup = 0;
|
|
}
|
|
CHECK_RESULT(result);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetDisplayMode(
|
|
UINT iSwapChain,
|
|
D3DDISPLAYMODE *pMode)
|
|
{
|
|
WRAP_VERBOSE;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return ensure_gfdm_hidden_side_swapchain(iSwapChain)->GetDisplayMode(pMode);
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(pReal->GetDisplayMode(GFDM_NATIVE_SMALL_SWAPCHAIN, pMode));
|
|
}
|
|
|
|
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;
|
|
|
|
if (pPresentationParameters == nullptr || ppSwapChain == nullptr) {
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
if (is_gfdm_two_head_exclusive()) {
|
|
if (pPresentationParameters->BackBufferWidth == 800) {
|
|
return GetSwapChain(gfdm_logical_small_swapchain, ppSwapChain);
|
|
}
|
|
if (pPresentationParameters->BackBufferWidth != 1080) {
|
|
log_warning(
|
|
"graphics::d3d9",
|
|
"two-head exclusive: rejecting unexpected additional swap chain {}x{}",
|
|
pPresentationParameters->BackBufferWidth,
|
|
pPresentationParameters->BackBufferHeight);
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
size_t index = 3;
|
|
for (UINT logical_swapchain = 1;
|
|
logical_swapchain < GFDM_LOGICAL_HEAD_COUNT;
|
|
logical_swapchain++)
|
|
{
|
|
if (!is_gfdm_logical_side_swapchain(logical_swapchain)) {
|
|
continue;
|
|
}
|
|
const size_t candidate =
|
|
gfdm_hidden_side_swapchain_slot(logical_swapchain);
|
|
if (fake_sub_swapchain[candidate] == nullptr) {
|
|
index = candidate;
|
|
break;
|
|
}
|
|
}
|
|
if (index == 3) {
|
|
return D3DERR_OUTOFVIDEOMEMORY;
|
|
}
|
|
fake_sub_swapchain[index] = new FakeIDirect3DSwapChain9(
|
|
this,
|
|
pPresentationParameters,
|
|
is_d3d9ex,
|
|
true);
|
|
fake_sub_swapchain[index]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[index]);
|
|
return D3D_OK;
|
|
}
|
|
|
|
int index = 0;
|
|
bool create_swap_chain = false;
|
|
bool create_fake_swap_chain = false;
|
|
bool arena_slot = false;
|
|
if (avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
|
create_swap_chain = true;
|
|
|
|
} else if (games::gitadora::is_arena_model() &&
|
|
(GRAPHICS_SCREENSHOT_SUBSCREENS ||
|
|
GRAPHICS_PREVENT_SECONDARY_WINDOWS ||
|
|
GRAPHICS_GITADORA_HIDE_SIDE_WINDOWS)) {
|
|
|
|
if (pPresentationParameters->BackBufferWidth == 800) {
|
|
// SMALL (subscreen)
|
|
create_swap_chain = true;
|
|
arena_slot = true;
|
|
index = 0;
|
|
|
|
} else if (pPresentationParameters->BackBufferWidth == 1080) {
|
|
// LEFT/RIGHT
|
|
create_swap_chain = true;
|
|
arena_slot = true;
|
|
index = 1;
|
|
if (sub_swapchain[index] || fake_sub_swapchain[index]) {
|
|
index = 2;
|
|
}
|
|
create_fake_swap_chain = GRAPHICS_GITADORA_HIDE_SIDE_WINDOWS &&
|
|
!GRAPHICS_PREVENT_SECONDARY_WINDOWS;
|
|
} else {
|
|
log_warning("graphics::d3d9", "unknown swap chain detected in CreateAdditionalSwapChain");
|
|
}
|
|
}
|
|
|
|
// the api lists screens from this registry, so arena heads need their logical numbers in it
|
|
if (arena_slot) {
|
|
graphics_screens_register(GFDM_ARENA_SLOT_SCREENS[index]);
|
|
}
|
|
|
|
if (create_fake_swap_chain) {
|
|
if (!fake_sub_swapchain[index]) {
|
|
log_info(
|
|
"graphics::d3d9",
|
|
"CreateAdditionalSwapChain called for hidden GITADORA side swap chain {}, "
|
|
"using fake swap chain",
|
|
index);
|
|
|
|
fake_sub_swapchain[index] =
|
|
new FakeIDirect3DSwapChain9(this, pPresentationParameters, false);
|
|
}
|
|
fake_sub_swapchain[index]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[index]);
|
|
|
|
return D3D_OK;
|
|
}
|
|
|
|
HRESULT hr = pReal->CreateAdditionalSwapChain(pPresentationParameters, ppSwapChain);
|
|
|
|
if (create_swap_chain) {
|
|
log_misc(
|
|
"graphics::d3d9",
|
|
"CreateAdditionalSwapChain called for swap chain {}, creating swap chain",
|
|
index);
|
|
|
|
if (SUCCEEDED(hr) && !sub_swapchain[index]) {
|
|
sub_swapchain[index] = new WrappedIDirect3DSwapChain9(this, *ppSwapChain);
|
|
sub_swapchain[index]->should_run_hooks = false;
|
|
} else if (FAILED(hr) && !fake_sub_swapchain[index]) {
|
|
log_warning("graphics::d3d9",
|
|
"failed to create sub swap chain, hr={}, using fake swap chain",
|
|
FMT_HRESULT(hr));
|
|
|
|
fake_sub_swapchain[index] = new FakeIDirect3DSwapChain9(this, pPresentationParameters, false);
|
|
fake_sub_swapchain[index]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[index]);
|
|
|
|
return D3D_OK;
|
|
}
|
|
}
|
|
|
|
CHECK_RESULT(hr);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetSwapChain(
|
|
UINT iSwapChain,
|
|
IDirect3DSwapChain9 **ppSwapChain)
|
|
{
|
|
WRAP_VERBOSE_FMT("GetSwapChain({})", iSwapChain);
|
|
|
|
if (ppSwapChain == nullptr) {
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
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,
|
|
is_gfdm_two_head_exclusive()
|
|
? WrappedIDirect3DSwapChain9::NativeGroupHead::Main
|
|
: WrappedIDirect3DSwapChain9::NativeGroupHead::Unknown);
|
|
}
|
|
|
|
main_swapchain->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(main_swapchain);
|
|
|
|
graphics_screens_register(iSwapChain);
|
|
return D3D_OK;
|
|
}
|
|
|
|
if (is_gfdm_two_head_exclusive()) {
|
|
if (is_gfdm_logical_small_swapchain(iSwapChain)) {
|
|
if (!sub_swapchain[0]) {
|
|
IDirect3DSwapChain9 *real_swapchain = nullptr;
|
|
HRESULT ret = pReal->GetSwapChain(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN,
|
|
&real_swapchain);
|
|
if (FAILED(ret)) {
|
|
return ret;
|
|
}
|
|
sub_swapchain[0] = new WrappedIDirect3DSwapChain9(
|
|
this,
|
|
real_swapchain,
|
|
WrappedIDirect3DSwapChain9::NativeGroupHead::Small);
|
|
sub_swapchain[0]->should_run_hooks = false;
|
|
}
|
|
|
|
sub_swapchain[0]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain[0]);
|
|
graphics_screens_register(iSwapChain);
|
|
return D3D_OK;
|
|
}
|
|
if (is_gfdm_logical_side_swapchain(iSwapChain)) {
|
|
FakeIDirect3DSwapChain9 *swapchain =
|
|
ensure_gfdm_hidden_side_swapchain(iSwapChain);
|
|
swapchain->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(swapchain);
|
|
graphics_screens_register(iSwapChain);
|
|
return D3D_OK;
|
|
}
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
bool swap = false;
|
|
if (iSwapChain == 1 && avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
|
swap = true;
|
|
}
|
|
if (games::gitadora::is_arena_model() && iSwapChain == 2) {
|
|
swap = true;
|
|
}
|
|
|
|
if (swap) {
|
|
if (sub_swapchain[0]) {
|
|
sub_swapchain[0]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(sub_swapchain[0]);
|
|
|
|
graphics_screens_register(iSwapChain);
|
|
return D3D_OK;
|
|
} else if (fake_sub_swapchain[0]) {
|
|
fake_sub_swapchain[0]->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(fake_sub_swapchain[0]);
|
|
|
|
graphics_screens_register(iSwapChain);
|
|
return D3D_OK;
|
|
} else if (SUBSCREEN_FORCE_REDRAW) {
|
|
// store implicit sub swap chain
|
|
if (!implicit_sub_swapchain) {
|
|
IDirect3DSwapChain9 *real_swapchain = nullptr;
|
|
HRESULT ret = pReal->GetSwapChain(iSwapChain, &real_swapchain);
|
|
if (FAILED(ret)) {
|
|
return ret;
|
|
}
|
|
|
|
implicit_sub_swapchain = new WrappedIDirect3DSwapChain9(this, real_swapchain);
|
|
implicit_sub_swapchain->should_run_hooks = false;
|
|
}
|
|
|
|
implicit_sub_swapchain->AddRef();
|
|
*ppSwapChain = static_cast<IDirect3DSwapChain9 *>(implicit_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;
|
|
|
|
if (is_gfdm_two_head_exclusive()) {
|
|
return GFDM_LOGICAL_HEAD_COUNT;
|
|
}
|
|
|
|
UINT n = pReal->GetNumberOfSwapChains();
|
|
|
|
if (sub_swapchain[0] && avs::game::is_model({"LDJ", "KFC", "M39"})) {
|
|
n += 1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
void WrappedIDirect3DDevice9::get_screenshot_screens(std::vector<int> &screens) const {
|
|
if (games::gitadora::is_arena_model()) {
|
|
screens.push_back(0);
|
|
|
|
// every head the game renders into, whether or not it reaches a display
|
|
for (int slot = 0; slot < 3; slot++) {
|
|
if (sub_swapchain[slot] != nullptr || fake_sub_swapchain[slot] != nullptr) {
|
|
screens.push_back(GFDM_ARENA_SLOT_SCREENS[slot]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
graphics_screens_get(screens);
|
|
|
|
// the sub screen is only registered once the game asks for it by index
|
|
if (sub_swapchain[0] != nullptr &&
|
|
avs::game::is_model({"LDJ", "KFC", "M39"}) &&
|
|
std::find(screens.begin(), screens.end(), 1) == screens.end())
|
|
{
|
|
screens.push_back(1);
|
|
}
|
|
}
|
|
|
|
HRESULT WrappedIDirect3DDevice9::get_screenshot_swap_chain(
|
|
UINT iSwapChain,
|
|
IDirect3DSwapChain9 **ppSwapChain)
|
|
{
|
|
if (ppSwapChain == nullptr) {
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
|
|
// the game numbers the two-head SMALL head itself; keep screen 1 meaning SMALL
|
|
if (games::gitadora::is_arena_model() && is_gfdm_two_head_exclusive() && iSwapChain == 1) {
|
|
return GetSwapChain(gfdm_logical_small_swapchain, ppSwapChain);
|
|
}
|
|
|
|
if (games::gitadora::is_arena_model()) {
|
|
for (int slot = 0; slot < 3; slot++) {
|
|
if (GFDM_ARENA_SLOT_SCREENS[slot] != (int) iSwapChain) {
|
|
continue;
|
|
}
|
|
if (sub_swapchain[slot] != nullptr) {
|
|
sub_swapchain[slot]->AddRef();
|
|
*ppSwapChain = sub_swapchain[slot];
|
|
return D3D_OK;
|
|
}
|
|
if (fake_sub_swapchain[slot] != nullptr) {
|
|
fake_sub_swapchain[slot]->AddRef();
|
|
*ppSwapChain = fake_sub_swapchain[slot];
|
|
return D3D_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return GetSwapChain(iSwapChain, ppSwapChain);
|
|
}
|
|
|
|
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;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return ensure_gfdm_hidden_side_swapchain(iSwapChain)->GetBackBuffer(
|
|
iBackBuffer,
|
|
Type,
|
|
ppBackBuffer);
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(pReal->GetBackBuffer(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN,
|
|
iBackBuffer,
|
|
Type,
|
|
ppBackBuffer));
|
|
}
|
|
|
|
CHECK_RESULT(pReal->GetBackBuffer(iSwapChain, iBackBuffer, Type, ppBackBuffer));
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetRasterStatus(
|
|
UINT iSwapChain,
|
|
D3DRASTER_STATUS *pRasterStatus)
|
|
{
|
|
WRAP_DEBUG;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return ensure_gfdm_hidden_side_swapchain(iSwapChain)->GetRasterStatus(
|
|
pRasterStatus);
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(pReal->GetRasterStatus(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN,
|
|
pRasterStatus));
|
|
}
|
|
|
|
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;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return;
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
pReal->SetGammaRamp(GFDM_NATIVE_SMALL_SWAPCHAIN, Flags, pRamp);
|
|
return;
|
|
}
|
|
|
|
pReal->SetGammaRamp(iSwapChain, Flags, pRamp);
|
|
}
|
|
|
|
void STDMETHODCALLTYPE WrappedIDirect3DDevice9::GetGammaRamp(
|
|
UINT iSwapChain,
|
|
D3DGAMMARAMP *pRamp)
|
|
{
|
|
WRAP_DEBUG;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
if (pRamp != nullptr) {
|
|
*pRamp = {};
|
|
}
|
|
return;
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
pReal->GetGammaRamp(GFDM_NATIVE_SMALL_SWAPCHAIN, pRamp);
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
#if defined(__GNUC__) && !defined(__clang__)
|
|
|
|
// 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;
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(pReal->GetFrontBufferData(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN,
|
|
pDestSurface));
|
|
}
|
|
|
|
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 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) {
|
|
|
|
// 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) {
|
|
// 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,
|
|
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 (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 = 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
|
|
// SDVX Live2D scenario
|
|
if (cfg::SCREENRESIZE->need_surface_clean) {
|
|
pReal->ColorFill(topSurface, nullptr, D3DCOLOR_XRGB(0, 0, 0));
|
|
cfg::SCREENRESIZE->need_surface_clean = false;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
// 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;
|
|
}
|
|
|
|
bool duplicate = false;
|
|
RECT originRect2 = originRect;
|
|
if (cfg::SCREENRESIZE->enable_screen_resize) {
|
|
auto& scene = cfg::SCREENRESIZE->scene_settings[cfg::SCREENRESIZE->screen_resize_current_scene];
|
|
if (scene.duplicate == cfg::ScreenDuplicateMode::CopyLeft) {
|
|
duplicate = true;
|
|
originRect2.right = originRect.left;
|
|
originRect2.left = (LONG)(originRect2.left - (originRect.right - originRect.left));
|
|
} else if (scene.duplicate == cfg::ScreenDuplicateMode::CopyRight) {
|
|
duplicate = true;
|
|
originRect2.left = originRect.right;
|
|
originRect2.right = (LONG)(originRect2.left + (originRect.right - originRect.left));
|
|
}
|
|
}
|
|
|
|
// 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 for main image, rect: {} {} {} {}",
|
|
originRect.left, originRect.top, originRect.right, originRect.bottom);
|
|
}
|
|
if (duplicate) {
|
|
hr = pReal->StretchRect(
|
|
backbuffer, nullptr,
|
|
topSurface, &originRect2,
|
|
D3DTEXF_LINEAR);
|
|
if (hr != D3D_OK) {
|
|
log_warning(
|
|
"graphics::d3d9",
|
|
"SurfaceHook - StretchRect backbuffer failed for the duplicate, rect: {} {} {} {}",
|
|
originRect2.left, originRect2.top, originRect2.right, originRect2.bottom);
|
|
}
|
|
}
|
|
|
|
// 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
|
|
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);
|
|
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;
|
|
|
|
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;
|
|
const auto result = pReal->GetViewport(pViewport);
|
|
|
|
// SDVX landscape mode
|
|
// without this, the game calculates the camera angle incorrectly and
|
|
// ends up with zoomed out view of the lanes
|
|
const auto landscape_width = GRAPHICS_FS_CUSTOM_RESOLUTION.has_value() ?
|
|
GRAPHICS_FS_CUSTOM_RESOLUTION.value().first : GRAPHICS_FS_ORIGINAL_HEIGHT;
|
|
const auto landscape_height = GRAPHICS_FS_CUSTOM_RESOLUTION.has_value() ?
|
|
GRAPHICS_FS_CUSTOM_RESOLUTION.value().second : GRAPHICS_FS_ORIGINAL_WIDTH;
|
|
if (SUCCEEDED(result) &&
|
|
GRAPHICS_FS_ORIENTATION_SWAP &&
|
|
avs::game::is_model("KFC") &&
|
|
pViewport != nullptr &&
|
|
pViewport->Width == landscape_width &&
|
|
pViewport->Height == landscape_height) {
|
|
|
|
static std::once_flag log_once;
|
|
std::call_once(log_once, [pViewport]() {
|
|
log_info(
|
|
"graphics::d3d9",
|
|
"SDVX landscape viewport fix: {}x{} => {}x{}",
|
|
pViewport->Width,
|
|
pViewport->Height,
|
|
pViewport->Height,
|
|
pViewport->Width);
|
|
});
|
|
std::swap(pViewport->Width, pViewport->Height);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
if (d3d9_live2d::should_skip_draw()) [[unlikely]] {
|
|
return D3D_OK;
|
|
}
|
|
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;
|
|
if (d3d9_live2d::should_skip_draw()) [[unlikely]] {
|
|
return D3D_OK;
|
|
}
|
|
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);
|
|
if (d3d9_live2d::should_skip_draw()) [[unlikely]] {
|
|
return D3D_OK;
|
|
}
|
|
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;
|
|
if (d3d9_live2d::should_skip_draw()) [[unlikely]] {
|
|
return D3D_OK;
|
|
}
|
|
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));
|
|
HRESULT ret = pReal->CreateVertexShader(pFunction, ppShader);
|
|
if (SUCCEEDED(ret) && ppShader != nullptr) {
|
|
d3d9_live2d::on_create_vertex_shader(*ppShader, pFunction);
|
|
}
|
|
if (GRAPHICS_LOG_HRESULT && FAILED(ret)) [[unlikely]] {
|
|
log_warning("graphics::d3d9", "{} failed, hr={}", __FUNCTION__, FMT_HRESULT(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetVertexShader(
|
|
IDirect3DVertexShader9 *pShader)
|
|
{
|
|
WRAP_DEBUG_FMT("SetVertexShader({})", fmt::ptr(pShader));
|
|
|
|
d3d9_live2d::on_set_vertex_shader(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));
|
|
HRESULT ret = pReal->CreatePixelShader(pFunction, ppShader);
|
|
if (SUCCEEDED(ret) && ppShader != nullptr) {
|
|
d3d9_live2d::on_create_pixel_shader(*ppShader, pFunction);
|
|
}
|
|
if (GRAPHICS_LOG_HRESULT && FAILED(ret)) [[unlikely]] {
|
|
log_warning("graphics::d3d9", "{} failed, hr={}", __FUNCTION__, FMT_HRESULT(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE WrappedIDirect3DDevice9::SetPixelShader(
|
|
IDirect3DPixelShader9 *pShader)
|
|
{
|
|
WRAP_DEBUG_FMT("SetPixelShader({})", fmt::ptr(pShader));
|
|
d3d9_live2d::on_set_pixel_shader(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);
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return D3D_OK;
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->WaitForVBlank(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN));
|
|
}
|
|
|
|
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 (is_gfdm_two_head_exclusive()) {
|
|
gfdm_disarm_present_mode_recovery();
|
|
if (pPresentationParameters == nullptr) {
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
release_gfdm_hidden_side_swapchains();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
GfdmTwoHeadDeviceState gfdm_parameters(
|
|
pPresentationParameters,
|
|
pFullscreenDisplayMode,
|
|
gfdm_logical_small_swapchain);
|
|
if (is_gfdm_two_head_exclusive()) {
|
|
HRESULT result = graphics_d3d9_gfdm_select_two_head_group_parameters(
|
|
pPresentationParameters,
|
|
pFullscreenDisplayMode,
|
|
gfdm_parameters.native_presentation_parameters.data(),
|
|
pFullscreenDisplayMode != nullptr
|
|
? gfdm_parameters.native_fullscreen_display_modes.data()
|
|
: nullptr,
|
|
&gfdm_parameters.logical_small_swapchain,
|
|
"ResetEx");
|
|
if (FAILED(result)) {
|
|
return result;
|
|
}
|
|
set_gfdm_logical_group_parameters(pPresentationParameters);
|
|
gfdm_parameters.use_native_parameters();
|
|
if (gfdm_parameters.native_presentation_parameters[0].Windowed
|
|
|| gfdm_parameters.native_presentation_parameters[1].Windowed)
|
|
{
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
if (GRAPHICS_FORCE_REFRESH_SUB.has_value()) {
|
|
gfdm_parameters.native_presentation_parameters[1]
|
|
.FullScreen_RefreshRateInHz =
|
|
GRAPHICS_FORCE_REFRESH_SUB.value();
|
|
if (gfdm_parameters.fullscreen_display_modes != nullptr) {
|
|
gfdm_parameters.fullscreen_display_modes[1].RefreshRate =
|
|
GRAPHICS_FORCE_REFRESH_SUB.value();
|
|
}
|
|
}
|
|
result = graphics_d3d9_gfdm_remap_two_head_group_parameters(
|
|
gfdm_parameters.native_presentation_parameters.data(),
|
|
gfdm_parameters.fullscreen_display_modes,
|
|
"ResetEx");
|
|
if (FAILED(result)) {
|
|
return result;
|
|
}
|
|
if (!graphics_gitadora_prepare_two_head_device_window(
|
|
gfdm_parameters.native_presentation_parameters[1].hDeviceWindow,
|
|
nullptr,
|
|
gfdm_parameters.native_presentation_parameters[1].BackBufferWidth,
|
|
gfdm_parameters.native_presentation_parameters[1].BackBufferHeight))
|
|
{
|
|
return D3DERR_INVALIDCALL;
|
|
}
|
|
if (gfdm_parameters.fullscreen_display_modes != nullptr) {
|
|
IDirect3D9 *raw_d3d = nullptr;
|
|
IDirect3D9Ex *raw_d3d_ex = nullptr;
|
|
D3DDEVICE_CREATION_PARAMETERS creation {};
|
|
HRESULT raw_result = pReal->GetDirect3D(&raw_d3d);
|
|
if (SUCCEEDED(raw_result) && raw_d3d != nullptr) {
|
|
raw_result = raw_d3d->QueryInterface(IID_PPV_ARGS(&raw_d3d_ex));
|
|
}
|
|
if (SUCCEEDED(raw_result)) {
|
|
raw_result = pReal->GetCreationParameters(&creation);
|
|
}
|
|
if (SUCCEEDED(raw_result) && raw_d3d_ex != nullptr) {
|
|
graphics_d3d9_gfdm_align_two_head_refresh_to_desktop(
|
|
raw_d3d_ex,
|
|
creation.AdapterOrdinal,
|
|
gfdm_parameters.native_presentation_parameters.data(),
|
|
gfdm_parameters.fullscreen_display_modes,
|
|
"ResetEx");
|
|
}
|
|
if (raw_d3d_ex != nullptr) {
|
|
raw_d3d_ex->Release();
|
|
}
|
|
if (raw_d3d != nullptr) {
|
|
raw_d3d->Release();
|
|
}
|
|
gfdm_parameters.recovery_parameters =
|
|
gfdm_parameters.native_presentation_parameters;
|
|
gfdm_parameters.recovery_modes =
|
|
gfdm_parameters.native_fullscreen_display_modes;
|
|
gfdm_parameters.recovery_candidate = true;
|
|
}
|
|
}
|
|
|
|
// reset overlay
|
|
if (overlay::OVERLAY && overlay::OVERLAY->uses_device(pReal)) {
|
|
overlay::OVERLAY->reset_invalidate();
|
|
}
|
|
|
|
HRESULT res = static_cast<IDirect3DDevice9Ex *>(pReal)->ResetEx(
|
|
gfdm_parameters.presentation_parameters,
|
|
gfdm_parameters.fullscreen_display_modes);
|
|
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& SUCCEEDED(res)
|
|
&& gfdm_parameters.recovery_candidate)
|
|
{
|
|
gfdm_arm_present_mode_recovery(
|
|
gfdm_parameters.recovery_parameters.data(),
|
|
gfdm_parameters.recovery_modes.data());
|
|
}
|
|
if (is_gfdm_two_head_exclusive() && SUCCEEDED(res)) {
|
|
gfdm_logical_small_swapchain = gfdm_parameters.logical_small_swapchain;
|
|
pPresentationParameters[0] = gfdm_parameters.presentation_parameters[0];
|
|
pPresentationParameters[gfdm_parameters.logical_small_swapchain] =
|
|
gfdm_parameters.presentation_parameters[1];
|
|
if (pFullscreenDisplayMode != nullptr) {
|
|
pFullscreenDisplayMode[0] = gfdm_parameters.fullscreen_display_modes[0];
|
|
pFullscreenDisplayMode[gfdm_parameters.logical_small_swapchain] =
|
|
gfdm_parameters.fullscreen_display_modes[1];
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_side_swapchain(iSwapChain))
|
|
{
|
|
return ensure_gfdm_hidden_side_swapchain(iSwapChain)->GetDisplayModeEx(
|
|
pMode,
|
|
pRotation);
|
|
}
|
|
if (is_gfdm_two_head_exclusive()
|
|
&& is_gfdm_logical_small_swapchain(iSwapChain))
|
|
{
|
|
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->GetDisplayModeEx(
|
|
GFDM_NATIVE_SMALL_SWAPCHAIN,
|
|
pMode,
|
|
pRotation));
|
|
}
|
|
|
|
CHECK_RESULT(static_cast<IDirect3DDevice9Ex *>(pReal)->GetDisplayModeEx(
|
|
iSwapChain, pMode, pRotation));
|
|
}
|