rodecomp/Source/Render/DX9/DeviceOwnerDX9.cpp
ragnarok-clanker bf6a44c117 Group Utility units into subsystem folders and require OFFSET provenance
Move Lexicon*, Text (TextTokens, TextEscapes, Base64), Math/RadialGrid, Hash (MD5, XxHash32) and Cipher (Rijndael, XorBytes) into Source/Utility subfolders; update includes, guards, compile.json and CMakeLists.txt. Backfill // OFFSET: above every standalone // FUNCTION: block (89 IDs, addresses from the symbol database) and make it mandatory in CODING_RULES.md and AGENTS.md. Remove the MatchLexicon cast chain.
2026-10-26 23:41:07 +01:00

281 lines
7.8 KiB
C++

#include "Render/DX9/DeviceOwnerDX9.h"
#include <cstring>
namespace {
constexpr HRESULT invalidDevice = static_cast<HRESULT>(0x8200000Eu);
constexpr HRESULT deviceError = static_cast<HRESULT>(0x82000000u);
}
struct DisplayModeSelectorDX9 {
/* 0000 */ const D3DFORMAT& format;
/* 0004 */ DeviceOwnerDX9* owner;
/* 0008 */ const UINT& adapter;
/* 000C */ const UINT& width;
/* 0010 */ const UINT& height;
bool Select(D3DSCANLINEORDERING ordering);
};
// OFFSET: 0x0054B7C0
// FUNCTION: fn_01906
DeviceOwnerDX9::DeviceOwnerDX9() {
this->adapter = 0;
this->window = nullptr;
this->direct3D = nullptr;
this->device = nullptr;
std::memset(&this->presentation, 0, sizeof(this->presentation) + sizeof(this->displayMode) + sizeof(this->backBufferDescription) + sizeof(this->caps));
this->backBuffer = nullptr;
this->capturedBackBuffer = nullptr;
this->dynamicTextures = false;
this->lastPresent = 0;
}
// OFFSET: 0x0054B830
// FUNCTION: fn_01907
void DeviceOwnerDX9Noop() {}
// OFFSET: 0x0054B840
// FUNCTION: fn_01908
bool DisplayModeSelectorDX9::Select(D3DSCANLINEORDERING ordering) {
D3DDISPLAYMODEFILTER filter{sizeof(filter), format, ordering};
const UINT count = this->owner->direct3D->GetAdapterModeCountEx(this->adapter, &filter);
UINT refresh = 0;
for (UINT index = 0; index < count; ++index) {
D3DDISPLAYMODEEX mode{};
mode.Size = sizeof(mode);
if (SUCCEEDED(this->owner->direct3D->EnumAdapterModesEx(this->adapter, &filter, index, &mode)) && mode.Width == this->width &&
mode.Height == this->height && mode.RefreshRate >= refresh) {
refresh = mode.RefreshRate;
this->owner->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) {
const D3DFORMAT format = D3DFMT_X8R8G8B8;
DisplayModeSelectorDX9 selectMode{format, this, selectedAdapter, width, height};
if (!selectMode.Select(D3DSCANLINEORDERING_PROGRESSIVE) && !selectMode.Select(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)) {
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};
}