Wrap IMVU's Direct3D renderer in the pass-through
IMVU's OpenGL renderer needs a real graphics driver; Direct3D also runs on Windows' basic display driver and is what most players use. The log can now be read while the client runs. Verified in rooms: every call goes through. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
dc643d271b
commit
3c28a8794b
6 files changed
+52
-35
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
A new 3D renderer for the IMVU Classic client, as a renderer plugin the client loads like its own OpenGL renderer.
|
A new 3D renderer for the IMVU Classic client, as a renderer plugin the client loads like its own OpenGL renderer.
|
||||||
Part of [T5DE](https://git.t5de.dev/t5de/t5de); unofficial, not affiliated with IMVU.
|
Part of [T5DE](https://git.t5de.dev/t5de/t5de); unofficial, not affiliated with IMVU.
|
||||||
|
|
||||||
For now it's a pass-through: it wraps IMVU's OpenGL renderer and logs what each frame draws, to prove our
|
For now it's a pass-through: it wraps IMVU's Direct3D renderer and logs what each frame draws, to prove our
|
||||||
declarations of IMVU's interface (`docs/interface.md`).
|
declarations of IMVU's interface (`docs/interface.md`).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@@ -20,6 +20,6 @@ That configures, builds `build/windows-x86/Release/t5deRenderEngine.dll` and run
|
|||||||
|
|
||||||
## Trying it
|
## Trying it
|
||||||
|
|
||||||
Copy `t5deRenderEngine.dll` into a T5DE client folder, and have the client load it in place of
|
Copy `t5deRenderEngine.dll` into a T5DE client folder built with the `t5de-renderer` patch, which has the OpenGL
|
||||||
`glRenderEngine.dll` (a T5DE patch, to come), then choose OpenGL in Settings > Advanced. The log is
|
setting load it in place of `glRenderEngine.dll`, then choose OpenGL in Settings > Advanced. The log is
|
||||||
`%TEMP%\t5de-renderer.log`.
|
`%TEMP%\t5de-renderer.log`.
|
||||||
+19
-3
@@ -79,9 +79,25 @@ virtual methods, IMVU's code only uses an engine through helpers that call its v
|
|||||||
ticks; `runApp` waits out `zero.dll`'s `millisecondsPerFrame`). Shorter ticks speed animations up. Smooth high
|
ticks; `runApp` waits out `zero.dll`'s `millisecondsPerFrame`). Shorter ticks speed animations up. Smooth high
|
||||||
frame rates need in-between poses: hooks in `SceneWindow.dll`'s animation update.
|
frame rates need in-between poses: hooks in `SceneWindow.dll`'s animation update.
|
||||||
|
|
||||||
|
## What the client draws
|
||||||
|
|
||||||
|
From the pass-through (Direct3D, client 554.8, a few rooms):
|
||||||
|
|
||||||
|
- A room frame: about 146 `drawTriangles`, each with its own `setModelSpace` (131) and `setTextures` (138), with
|
||||||
|
per-object alpha test, culling, normal inverting and blending, 252 texture-transform resets, 8 light updates; no
|
||||||
|
`drawLine`, `drawPointSprites` or `drawTrianglesFromMemory` in those rooms.
|
||||||
|
- Every frame creates index and vertex buffers (4 and 12 in a room, with nothing new loading): most likely avatars,
|
||||||
|
skinned on the CPU and uploaded again each frame.
|
||||||
|
- Each 3D window, and each room change, creates a new engine (`createRenderEngine`), so engines start and stop
|
||||||
|
often; frame buffers are resized freely (up to 3440x1272 here).
|
||||||
|
- IMVU's OpenGL engine needs a real graphics driver (`ARB_multitexture`); on Windows' basic display driver
|
||||||
|
(virtual machines) it throws and the client falls back. Its exceptions are `std::exception`s our code can
|
||||||
|
catch.
|
||||||
|
|
||||||
## Plan
|
## Plan
|
||||||
|
|
||||||
1. Pass-through plugin (this repository's first version): wraps IMVU's OpenGL engine and logs what each frame
|
1. Pass-through plugin (done, verified in rooms): wraps IMVU's Direct3D engine, made with `SceneWindow.dll`'s
|
||||||
calls, proving our declarations.
|
exported `createRenderer`, and logs what each frame calls, proving our declarations.
|
||||||
2. Work out the layouts above.
|
2. Work out the layouts above.
|
||||||
3. A renderer reproducing the current look on Direct3D 11 or OpenGL 4, then the improvements.
|
3. A renderer reproducing the current look on Direct3D 11 (it also runs in software, on WARP, where OpenGL can't),
|
||||||
|
then the improvements.
|
||||||
@@ -112,4 +112,16 @@ static_assert(sizeof(PiRenderEngine) == 12, "vtable pointer, magic, size");
|
|||||||
using IsRenderEngineSupported = int(__cdecl*)();
|
using IsRenderEngineSupported = int(__cdecl*)();
|
||||||
using CreateRenderEngine = SharedPtr<PiRenderEngine>(__cdecl*)(SharedPtr<INativeWindow> window);
|
using CreateRenderEngine = SharedPtr<PiRenderEngine>(__cdecl*)(SharedPtr<INativeWindow> window);
|
||||||
|
|
||||||
|
// The renderers SceneWindow.dll's createRenderer makes (values from _avatarwindow.pyd's RendererType).
|
||||||
|
enum class RendererType : int {
|
||||||
|
Direct3D = 1, // RenderEngineDx, Direct3D 9
|
||||||
|
OpenGL = 2, // the plugin glRenderEngine.dll
|
||||||
|
SwiftShader = 9, // RenderEngineDx on swiftshader.dll
|
||||||
|
};
|
||||||
|
|
||||||
|
// SceneWindow.dll's createRenderer(boost::shared_ptr<INativeWindow>, RendererType), exported by this name.
|
||||||
|
using CreateRenderer = SharedPtr<PiRenderEngine>(__cdecl*)(SharedPtr<INativeWindow> window, RendererType type);
|
||||||
|
inline constexpr const char* createRendererName =
|
||||||
|
"?createRenderer@@YA?AV?$shared_ptr@VPiRenderEngine@@@boost@@V?$shared_ptr@VINativeWindow@@@2@W4RendererType@@@Z";
|
||||||
|
|
||||||
} // namespace t5de::imvu
|
} // namespace t5de::imvu
|
||||||
+3
-3
@@ -1,5 +1,6 @@
|
|||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
|
|
||||||
|
#include <share.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -17,9 +18,8 @@ std::FILE* open()
|
|||||||
if (length == 0 || length > MAX_PATH) {
|
if (length == 0 || length > MAX_PATH) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
std::FILE* file = nullptr;
|
// Shared, so it can be read while the client runs.
|
||||||
_wfopen_s(&file, (std::filesystem::path(temp) / L"t5de-renderer.log").c_str(), L"w");
|
return _wfsopen((std::filesystem::path(temp) / L"t5de-renderer.log").c_str(), L"w", _SH_DENYNO);
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
// A renderer that forwards every call to another one (IMVU's OpenGL engine) and logs what a frame is made of: proof
|
// A renderer that forwards every call to another one (IMVU's Direct3D engine) and logs what a frame is made of: proof
|
||||||
// that our declaration of the interface matches IMVU's, and a way to see how the client draws.
|
// that our declaration of the interface matches IMVU's, and a way to see how the client draws.
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|||||||
+14
-25
@@ -1,10 +1,9 @@
|
|||||||
// The renderer plugin's entry points, which SceneWindow.dll's createDLLRenderer looks up by name (exported
|
// The renderer plugin's entry points, which SceneWindow.dll's createDLLRenderer looks up by name (exported
|
||||||
// undecorated by plugin.def). For now the renderer is the pass-through: IMVU's OpenGL engine, wrapped.
|
// undecorated by plugin.def). For now the renderer is the pass-through: IMVU's Direct3D engine, wrapped.
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
#include "log.hpp"
|
#include "log.hpp"
|
||||||
#include "passthrough_engine.hpp"
|
#include "passthrough_engine.hpp"
|
||||||
@@ -14,33 +13,20 @@ namespace {
|
|||||||
|
|
||||||
using namespace t5de;
|
using namespace t5de;
|
||||||
|
|
||||||
// The folder this DLL is in: the client's program folder.
|
// IMVU's Direct3D 9 engine, made by SceneWindow.dll (already loaded: it's what loads this plugin). Direct3D
|
||||||
std::filesystem::path program_folder()
|
// rather than IMVU's OpenGL engine, which needs a real graphics driver: Direct3D also works on Windows' basic
|
||||||
{
|
// display driver (virtual machines), and it's what most players use.
|
||||||
HMODULE self = nullptr;
|
|
||||||
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
|
||||||
reinterpret_cast<LPCWSTR>(&program_folder), &self);
|
|
||||||
wchar_t path[MAX_PATH];
|
|
||||||
GetModuleFileNameW(self, path, MAX_PATH);
|
|
||||||
return std::filesystem::path(path).parent_path();
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMVU's OpenGL engine, from the plugin in the program folder (never unloaded: its engines use its code).
|
|
||||||
imvu::SharedPtr<imvu::PiRenderEngine> create_imvu_engine(imvu::SharedPtr<imvu::INativeWindow> window)
|
imvu::SharedPtr<imvu::PiRenderEngine> create_imvu_engine(imvu::SharedPtr<imvu::INativeWindow> window)
|
||||||
{
|
{
|
||||||
const auto dll = program_folder() / L"glRenderEngine.dll";
|
HMODULE scene_window = GetModuleHandleW(L"SceneWindow.dll");
|
||||||
HMODULE module = LoadLibraryW(dll.c_str());
|
auto create = scene_window == nullptr
|
||||||
if (module == nullptr) {
|
? nullptr
|
||||||
log::info("can't load {}: error {}", dll.string(), GetLastError());
|
: reinterpret_cast<imvu::CreateRenderer>(GetProcAddress(scene_window, imvu::createRendererName));
|
||||||
|
if (create == nullptr) {
|
||||||
|
log::info("SceneWindow.dll's createRenderer not found");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto supported = reinterpret_cast<imvu::IsRenderEngineSupported>(GetProcAddress(module, "isRenderEngineSupported"));
|
return create(std::move(window), imvu::RendererType::Direct3D);
|
||||||
auto create = reinterpret_cast<imvu::CreateRenderEngine>(GetProcAddress(module, "createRenderEngine"));
|
|
||||||
if (supported == nullptr || create == nullptr || supported() == 0) {
|
|
||||||
log::info("{} isn't a usable renderer", dll.string());
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return create(std::move(window));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -62,5 +48,8 @@ extern "C" imvu::SharedPtr<imvu::PiRenderEngine> __cdecl createRenderEngine(imvu
|
|||||||
// Exceptions mustn't cross into IMVU's code (a different C++ runtime).
|
// Exceptions mustn't cross into IMVU's code (a different C++ runtime).
|
||||||
log::info("createRenderEngine failed: {}", e.what());
|
log::info("createRenderEngine failed: {}", e.what());
|
||||||
return {};
|
return {};
|
||||||
|
} catch (...) {
|
||||||
|
log::info("createRenderEngine failed");
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user