rodecomp/Source/Render/DeviceOwnerDX9.cpp
2026-10-24 00:35:19 +00:00

281 lines
7.6 KiB
C++

#include "Render/DeviceOwnerDX9.h"
#include <cstring>
namespace {
constexpr HRESULT invalidDevice = static_cast<HRESULT>(0x8200000Eu);
constexpr HRESULT deviceError = static_cast<HRESULT>(0x82000000u);
}
// OFFSET: 0x0054B7C0
// FUNCTION: fn_01906
DeviceOwnerDX9::DeviceOwnerDX9()
: window(nullptr), direct3D(nullptr), device(nullptr), state{}, adapter(0), backBuffer(nullptr),
capturedBackBuffer(nullptr), dynamicTextures(false), lastPresent(0) {
std::memset(
&this->presentation, 0,
sizeof(this->presentation) + sizeof(this->displayMode) + sizeof(this->backBufferDescription) + sizeof(this->caps)
);
}
// OFFSET: 0x0054B840
// FUNCTION: fn_01908
bool DeviceOwnerDX9::SelectDisplayMode(
D3DFORMAT format,
UINT selectedAdapter,
UINT width,
UINT height,
D3DSCANLINEORDERING ordering
) {
D3DDISPLAYMODEFILTER filter{sizeof(filter), format, ordering};
const UINT count = this->direct3D->GetAdapterModeCountEx(selectedAdapter, &filter);
UINT refresh = 0;
for (UINT index = 0; index < count; ++index) {
D3DDISPLAYMODEEX mode{};
mode.Size = sizeof(mode);
if (
SUCCEEDED(this->direct3D->EnumAdapterModesEx(selectedAdapter, &filter, index, &mode)) && mode.Width == width &&
mode.Height == height && mode.RefreshRate >= refresh
) {
refresh = mode.RefreshRate;
this->displayMode = mode;
}
}
return refresh != 0;
}
// OFFSET: 0x0054B980
// FUNCTION: fn_01910
UINT* DeviceOwnerDX9::AdjustTextureDimensions(UINT* output, UINT width, UINT height) const {
const auto roundUp = [](UINT value) {
--value;
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
++value;
return value + (value == 0);
};
UINT adjustedWidth = roundUp(width);
UINT adjustedHeight = roundUp(height);
const DWORD ratio = this->caps.MaxTextureAspectRatio;
if (this->caps.MaxTextureWidth < adjustedWidth) {
adjustedWidth = this->caps.MaxTextureWidth;
}
if (this->caps.MaxTextureHeight < adjustedHeight) {
adjustedHeight = this->caps.MaxTextureHeight;
}
if (ratio != 0) {
if (adjustedWidth / adjustedHeight > ratio) {
adjustedHeight = adjustedWidth / ratio;
}
if (adjustedHeight / adjustedWidth > ratio) {
adjustedWidth = adjustedHeight / ratio;
}
}
output[0] = adjustedWidth;
output[1] = adjustedHeight;
return output;
}
// OFFSET: 0x0054BD10
// FUNCTION: fn_01914
HRESULT DeviceOwnerDX9::CreateDevice(UINT selectedAdapter, const DeviceOptionsDX9& options) {
HRESULT result = Direct3DCreate9Ex(D3D_SDK_VERSION, &this->direct3D);
if (FAILED(result)) {
return result;
}
this->adapter = selectedAdapter;
UINT width = options.width;
UINT height = options.height;
if (options.fullscreen == 0) {
RECT rectangle;
GetClientRect(this->window, &rectangle);
width = rectangle.right - rectangle.left;
height = rectangle.bottom - rectangle.top;
}
this->presentation = {};
this->presentation.BackBufferWidth = width;
this->presentation.BackBufferHeight = height;
this->presentation.BackBufferFormat = D3DFMT_X8R8G8B8;
this->presentation.BackBufferCount = 1;
this->presentation.MultiSampleType = D3DMULTISAMPLE_NONE;
this->presentation.SwapEffect = D3DSWAPEFFECT_DISCARD;
this->presentation.hDeviceWindow = this->window;
this->presentation.Windowed = options.fullscreen == 0;
this->presentation.EnableAutoDepthStencil = TRUE;
this->presentation.AutoDepthStencilFormat = D3DFMT_D24S8;
this->presentation.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER | D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
if (!this->presentation.Windowed) {
if (
!this->SelectDisplayMode(D3DFMT_X8R8G8B8, selectedAdapter, width, height, D3DSCANLINEORDERING_PROGRESSIVE) &&
!this->SelectDisplayMode(D3DFMT_X8R8G8B8, selectedAdapter, width, height, D3DSCANLINEORDERING_INTERLACED)
) {
D3DDISPLAYMODEEX mode{};
mode.Size = sizeof(mode);
D3DDISPLAYROTATION rotation = D3DDISPLAYROTATION_IDENTITY;
if (FAILED(this->direct3D->GetAdapterDisplayModeEx(selectedAdapter, &mode, &rotation))) {
return deviceError;
}
this->displayMode = mode;
}
this->presentation.BackBufferWidth = this->displayMode.Width;
this->presentation.BackBufferHeight = this->displayMode.Height;
this->presentation.FullScreen_RefreshRateInHz = this->displayMode.RefreshRate;
}
result = this->direct3D->CreateDeviceEx(
selectedAdapter, D3DDEVTYPE_HAL, this->window, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
&this->presentation, this->presentation.Windowed ? nullptr : &this->displayMode, &this->device
);
if (FAILED(result)) {
return static_cast<HRESULT>(0x82000004u);
}
if (FAILED(this->AcquireBackBuffer())) {
return static_cast<HRESULT>(0x8200000Au);
}
if (FAILED(this->device->GetDeviceCaps(&this->caps))) {
return deviceError;
}
return D3D_OK;
}
// OFFSET: 0x0054B920
// FUNCTION: fn_01909
HRESULT DeviceOwnerDX9::AcquireBackBuffer() {
IDirect3DSurface9* surface = nullptr;
HRESULT result = this->device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &surface);
if (FAILED(result)) {
return result;
}
result = surface->GetDesc(&this->backBufferDescription);
if (FAILED(result)) {
// Original does not release the acquired local surface on this failure.
return result;
}
if (this->backBuffer) {
this->backBuffer->Release();
}
this->backBuffer = surface;
return D3D_OK;
}
// OFFSET: 0x0054C140
// FUNCTION: fn_01918
HRESULT DeviceOwnerDX9::Initialize(HWND value, UINT selectedAdapter, const DeviceOptionsDX9& options) {
if (!value) {
return E_INVALIDARG;
}
this->window = value;
const HRESULT result = this->CreateDevice(selectedAdapter, options);
if (FAILED(result)) {
this->Close();
return result;
}
this->state.Initialize(this->device);
this->state.SetInitialStates();
this->dynamicTextures = (this->caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0 && options.dynamicTextures != 0;
this->ClipFullscreenCursor();
return D3D_OK;
}
// OFFSET: 0x0054BFA0
// FUNCTION: fn_01915
HRESULT DeviceOwnerDX9::Close() {
if (this->backBuffer) {
this->backBuffer->Release();
this->backBuffer = nullptr;
}
if (this->capturedBackBuffer) {
this->capturedBackBuffer->Release();
this->capturedBackBuffer = nullptr;
}
ULONG references = 0;
if (this->device) {
references = this->device->Release();
this->device = nullptr;
}
if (this->direct3D) {
this->direct3D->Release();
this->direct3D = nullptr;
}
return references != 0 ? static_cast<HRESULT>(0x8200000Bu) : D3D_OK;
}
// OFFSET: 0x0054C490
// FUNCTION: fn_01920
HRESULT DeviceOwnerDX9::ClipFullscreenCursor() {
if (!this->presentation.Windowed) {
RECT rectangle;
GetWindowRect(this->window, &rectangle);
ClipCursor(&rectangle);
}
return D3D_OK;
}
// OFFSET: 0x0054C510
// FUNCTION: fn_01921
HRESULT DeviceOwnerDX9::Present(int) {
if (!this->device) {
return invalidDevice;
}
this->lastPresent = this->device->Present(nullptr, nullptr, nullptr, nullptr);
return this->lastPresent;
}
SurfaceDX9Context DeviceOwnerDX9::SurfaceContext() const {
return {this->device, this->backBufferDescription, this->backBuffer};
}
TextureDX9Context DeviceOwnerDX9::TextureContext(
D3DX9Runtime& d3dx,
RenderPrimitivePool& primitives,
RenderQueues& queues,
bool depthAlpha,
float depthStart,
float depthEnd
) const {
return {this->direct3D, this->device,
this->adapter, this->presentation.BackBufferFormat,
this->caps, this->dynamicTextures,
&d3dx, &primitives,
&queues, depthAlpha,
depthStart, depthEnd};
}