2021-04-12 20:49:04 +00:00
|
|
|
/*
|
|
|
|
Copyright 2021 flyinghead
|
|
|
|
|
|
|
|
This file is part of Flycast.
|
|
|
|
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "dxcontext.h"
|
2021-04-15 13:17:32 +00:00
|
|
|
#include "d3d_renderer.h"
|
2021-07-08 18:09:09 +00:00
|
|
|
#include "rend/osd.h"
|
2021-04-29 16:58:04 +00:00
|
|
|
#ifdef USE_SDL
|
2021-04-12 20:49:04 +00:00
|
|
|
#include "sdl/sdl.h"
|
2021-04-29 16:58:04 +00:00
|
|
|
#endif
|
2021-04-15 13:17:32 +00:00
|
|
|
#include "hw/pvr/Renderer_if.h"
|
2021-09-07 12:16:05 +00:00
|
|
|
#include "emulator.h"
|
2021-11-10 19:35:30 +00:00
|
|
|
#include "dx9_driver.h"
|
2023-05-03 07:50:37 +00:00
|
|
|
#include "imgui/backends/imgui_impl_dx9.h"
|
2021-04-12 20:49:04 +00:00
|
|
|
|
|
|
|
DXContext theDXContext;
|
|
|
|
|
2021-11-10 19:35:30 +00:00
|
|
|
bool DXContext::init(bool keepCurrentWindow)
|
2021-04-12 20:49:04 +00:00
|
|
|
{
|
2023-01-14 11:48:56 +00:00
|
|
|
NOTICE_LOG(RENDERER, "DX9 Context initializing");
|
2021-11-10 19:35:30 +00:00
|
|
|
GraphicsContext::instance = this;
|
2021-04-29 16:58:04 +00:00
|
|
|
#ifdef USE_SDL
|
2021-09-07 12:16:05 +00:00
|
|
|
if (!keepCurrentWindow && !sdl_recreate_window(0))
|
2021-04-12 20:49:04 +00:00
|
|
|
return false;
|
2021-04-29 16:58:04 +00:00
|
|
|
#endif
|
2021-04-12 20:49:04 +00:00
|
|
|
|
|
|
|
pD3D.reset(Direct3DCreate9(D3D_SDK_VERSION));
|
2023-01-13 10:15:32 +00:00
|
|
|
if (!pD3D) {
|
|
|
|
ERROR_LOG(RENDERER, "Direct3DCreate9 failed");
|
2021-04-12 20:49:04 +00:00
|
|
|
return false;
|
2023-01-13 10:15:32 +00:00
|
|
|
}
|
2021-04-12 20:49:04 +00:00
|
|
|
memset(&d3dpp, 0, sizeof(d3dpp));
|
2021-11-10 19:35:30 +00:00
|
|
|
d3dpp.hDeviceWindow = (HWND)window;
|
2021-04-15 13:17:32 +00:00
|
|
|
d3dpp.Windowed = true;
|
2021-04-12 20:49:04 +00:00
|
|
|
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
2021-04-15 13:17:32 +00:00
|
|
|
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
|
|
|
|
d3dpp.EnableAutoDepthStencil = FALSE; // No need for depth/stencil buffer for the backbuffer
|
2021-09-07 12:16:05 +00:00
|
|
|
swapOnVSync = !settings.input.fastForwardMode && config::VSync;
|
2021-11-10 19:35:30 +00:00
|
|
|
if (swapOnVSync)
|
|
|
|
{
|
|
|
|
switch ((int)(settings.display.refreshRate / 60))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_TWO;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_THREE;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
default:
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_FOUR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
2021-09-07 12:16:05 +00:00
|
|
|
// TODO should be 0 in windowed mode
|
|
|
|
//d3dpp.FullScreen_RefreshRateInHz = swapOnVSync ? 60 : 0;
|
2023-01-13 10:15:32 +00:00
|
|
|
HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, (HWND)window,
|
|
|
|
D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDevice.get());
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
|
|
|
ERROR_LOG(RENDERER, "DirectX9 device creation failed: %x", hr);
|
2021-04-12 20:49:04 +00:00
|
|
|
return false;
|
2023-01-13 10:15:32 +00:00
|
|
|
}
|
2022-04-13 16:06:19 +00:00
|
|
|
imguiDriver = std::unique_ptr<ImGuiDriver>(new DX9Driver(pDevice));
|
2021-04-16 16:30:47 +00:00
|
|
|
overlay.init(pDevice);
|
2022-04-13 16:06:19 +00:00
|
|
|
|
2022-12-21 15:49:08 +00:00
|
|
|
D3DADAPTER_IDENTIFIER9 id;
|
|
|
|
pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &id);
|
|
|
|
driverName = std::string(id.Description);
|
|
|
|
driverVersion = std::to_string(id.DriverVersion.HighPart >> 16) + "." + std::to_string((u16)id.DriverVersion.HighPart)
|
|
|
|
+ "." + std::to_string(id.DriverVersion.LowPart >> 16) + "." + std::to_string((u16)id.DriverVersion.LowPart);
|
2023-01-09 17:01:44 +00:00
|
|
|
deviceReady = true;
|
2022-12-21 15:49:08 +00:00
|
|
|
|
2022-04-13 16:06:19 +00:00
|
|
|
return true;
|
2021-04-12 20:49:04 +00:00
|
|
|
}
|
|
|
|
|
2021-11-10 19:35:30 +00:00
|
|
|
void DXContext::term()
|
2021-04-12 20:49:04 +00:00
|
|
|
{
|
2023-01-14 11:48:56 +00:00
|
|
|
NOTICE_LOG(RENDERER, "DX9 Context terminating");
|
2021-11-10 19:35:30 +00:00
|
|
|
GraphicsContext::instance = nullptr;
|
2021-04-16 16:30:47 +00:00
|
|
|
overlay.term();
|
2021-11-10 19:35:30 +00:00
|
|
|
imguiDriver.reset();
|
2021-04-12 20:49:04 +00:00
|
|
|
pDevice.reset();
|
|
|
|
pD3D.reset();
|
2023-01-09 17:01:44 +00:00
|
|
|
deviceReady = false;
|
2021-04-12 20:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DXContext::Present()
|
|
|
|
{
|
2021-11-11 10:17:39 +00:00
|
|
|
if (!frameRendered)
|
|
|
|
return;
|
2023-01-13 10:15:32 +00:00
|
|
|
if (!pDevice)
|
|
|
|
{
|
|
|
|
if (init(true))
|
|
|
|
{
|
|
|
|
renderer = new D3DRenderer();
|
|
|
|
rend_init_renderer();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2021-04-12 20:49:04 +00:00
|
|
|
HRESULT result = pDevice->Present(NULL, NULL, NULL, NULL);
|
|
|
|
// Handle loss of D3D9 device
|
2021-04-15 13:17:32 +00:00
|
|
|
if (result == D3DERR_DEVICELOST)
|
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
deviceReady = false;
|
2021-04-15 13:17:32 +00:00
|
|
|
result = pDevice->TestCooperativeLevel();
|
|
|
|
if (result == D3DERR_DEVICENOTRESET)
|
|
|
|
resetDevice();
|
|
|
|
}
|
|
|
|
else if (FAILED(result))
|
|
|
|
WARN_LOG(RENDERER, "Present failed %x", result);
|
2021-09-07 12:16:05 +00:00
|
|
|
else
|
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
frameRendered = false;
|
2021-09-07 12:16:05 +00:00
|
|
|
if (swapOnVSync != (!settings.input.fastForwardMode && config::VSync))
|
|
|
|
{
|
|
|
|
DEBUG_LOG(RENDERER, "Switch vsync %d", !swapOnVSync);
|
|
|
|
if (renderer != nullptr)
|
|
|
|
{
|
|
|
|
renderer->Term();
|
|
|
|
delete renderer;
|
2023-01-13 10:15:32 +00:00
|
|
|
renderer = nullptr;
|
2021-09-07 12:16:05 +00:00
|
|
|
}
|
2021-11-10 19:35:30 +00:00
|
|
|
term();
|
2023-01-13 10:15:32 +00:00
|
|
|
if (init(true))
|
2021-09-07 12:16:05 +00:00
|
|
|
{
|
|
|
|
renderer = new D3DRenderer();
|
2023-01-13 10:15:32 +00:00
|
|
|
rend_init_renderer();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
deviceReady = false;
|
2021-09-07 12:16:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-12 20:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DXContext::EndImGuiFrame()
|
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
if (deviceReady)
|
2021-04-12 20:49:04 +00:00
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
verify((bool)pDevice);
|
|
|
|
pDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
|
|
|
|
pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
|
|
|
|
pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
|
|
|
|
if (!overlayOnly)
|
2021-04-16 16:30:47 +00:00
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 255), 1.0f, 0);
|
|
|
|
if (renderer != nullptr)
|
|
|
|
renderer->RenderLastFrame();
|
2021-04-16 16:30:47 +00:00
|
|
|
}
|
2023-01-09 17:01:44 +00:00
|
|
|
if (SUCCEEDED(pDevice->BeginScene()))
|
2021-04-16 16:30:47 +00:00
|
|
|
{
|
2023-01-09 17:01:44 +00:00
|
|
|
if (overlayOnly)
|
|
|
|
{
|
|
|
|
if (crosshairsNeeded() || config::FloatVMUs)
|
|
|
|
overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
overlay.draw(settings.display.width, settings.display.height, true, false);
|
|
|
|
}
|
|
|
|
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
pDevice->EndScene();
|
2021-04-16 16:30:47 +00:00
|
|
|
}
|
2021-04-12 20:49:04 +00:00
|
|
|
}
|
2021-11-11 10:17:39 +00:00
|
|
|
frameRendered = true;
|
2021-04-12 20:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DXContext::resize()
|
|
|
|
{
|
|
|
|
if (!pDevice)
|
|
|
|
return;
|
|
|
|
RECT rect;
|
2021-11-10 19:35:30 +00:00
|
|
|
GetClientRect((HWND)window, &rect);
|
2021-09-27 18:29:23 +00:00
|
|
|
d3dpp.BackBufferWidth = settings.display.width = rect.right;
|
|
|
|
d3dpp.BackBufferHeight = settings.display.height = rect.bottom;
|
|
|
|
if (settings.display.width == 0 || settings.display.height == 0)
|
2021-04-15 13:17:32 +00:00
|
|
|
// window minimized
|
|
|
|
return;
|
2021-04-12 20:49:04 +00:00
|
|
|
resetDevice();
|
|
|
|
}
|
2021-04-15 13:17:32 +00:00
|
|
|
|
|
|
|
void DXContext::resetDevice()
|
|
|
|
{
|
2022-12-29 18:18:19 +00:00
|
|
|
D3DRenderer *dxrenderer{};
|
2021-04-15 13:17:32 +00:00
|
|
|
if (renderer != nullptr)
|
2022-12-29 18:18:19 +00:00
|
|
|
dxrenderer = dynamic_cast<D3DRenderer*>(renderer);
|
|
|
|
if (dxrenderer != nullptr)
|
|
|
|
dxrenderer->preReset();
|
2021-04-16 16:30:47 +00:00
|
|
|
overlay.term();
|
2021-04-15 13:17:32 +00:00
|
|
|
ImGui_ImplDX9_InvalidateDeviceObjects();
|
|
|
|
HRESULT hr = pDevice->Reset(&d3dpp);
|
2023-01-11 08:44:00 +00:00
|
|
|
if (FAILED(hr))
|
2021-04-15 13:17:32 +00:00
|
|
|
{
|
2023-01-11 08:44:00 +00:00
|
|
|
ERROR_LOG(RENDERER, "DX9 device reset failed: %x", hr);
|
2023-01-09 17:01:44 +00:00
|
|
|
deviceReady = false;
|
2021-04-15 13:17:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2023-01-09 17:01:44 +00:00
|
|
|
deviceReady = true;
|
2021-04-15 13:17:32 +00:00
|
|
|
ImGui_ImplDX9_CreateDeviceObjects();
|
2021-04-16 16:30:47 +00:00
|
|
|
overlay.init(pDevice);
|
2022-12-29 18:18:19 +00:00
|
|
|
if (dxrenderer != nullptr)
|
|
|
|
dxrenderer->postReset();
|
2021-04-15 13:17:32 +00:00
|
|
|
}
|