Files
t5de-renderer/adapter/plugin.cpp
T
dhkatzandClaude Opus 5.5 05a7be5d41
CI / build (push) Successful in 1m31s
Ask the client for frames between its steps
The frame pacer repaints the client's 3D windows at the user's frame rate
(HKCU\Software\T5DE\Graphics\FrameRate: 30, the default, adds nothing; 60, 120,
240, 360; 0 uncapped), so the client draws frames between its 30-per-second
steps. Only when affordable: a window is asked once its last frame is drawn,
and only if its frames take under three quarters of the interval; uncapped,
the client is left idle a third of a frame between frames. The engine's stats
now log frames per second.

The extra frames show the same scene for now.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 20:38:31 -07:00

77 lines
3.1 KiB
C++

// The renderer plugin's entry points, which SceneWindow.dll's createDLLRenderer looks up by name (exported
// undecorated by plugin.def). The engine is the adapter's (engine.hpp): IMVU's Direct3D engine draws, and the
// renderer process draws over part of it.
#include <windows.h>
#include <exception>
#include <string_view>
#include "log.hpp"
#include "engine.hpp"
#include "recorder.hpp"
#include "renderer_process.hpp"
#include "t5de/imvu/render_engine.hpp"
namespace {
using namespace t5de;
// IMVU's Direct3D 9 engine, made by SceneWindow.dll (already loaded: it's what loads this plugin). Direct3D
// 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.
imvu::SharedPtr<imvu::PiRenderEngine> create_imvu_engine(imvu::SharedPtr<imvu::INativeWindow> window)
{
HMODULE scene_window = GetModuleHandleW(L"SceneWindow.dll");
auto create = scene_window == nullptr
? nullptr
: reinterpret_cast<imvu::CreateRenderer>(GetProcAddress(scene_window, imvu::createRendererName));
if (create == nullptr) {
log::info("SceneWindow.dll's createRenderer not found");
return {};
}
return create(std::move(window), imvu::RendererType::Direct3D);
}
// What the renderer process draws: T5DE_RENDERER_VIEW=off, split (the right half), full (the default) or compare.
// (The renderer process can also be switched while it runs: renderer/main.cpp.)
protocol::View view()
{
wchar_t value[16] = {};
GetEnvironmentVariableW(L"T5DE_RENDERER_VIEW", value, 16);
const std::wstring_view setting = value;
return setting == L"off" ? protocol::View::Off
: setting == L"split" ? protocol::View::Split
: setting == L"compare" ? protocol::View::Compare
: protocol::View::Full;
}
} // namespace
extern "C" int __cdecl isRenderEngineSupported() { return 1; }
// Returns a C++ object from a C-linkage function: fine here, the caller is C++ built for the same ABI.
#pragma warning(suppress : 4190)
extern "C" imvu::SharedPtr<imvu::PiRenderEngine> __cdecl createRenderEngine(imvu::SharedPtr<imvu::INativeWindow> window)
{
try {
log::info("createRenderEngine: window {}", static_cast<void*>(window.get()));
HWND hwnd = window ? window->getPlatformWindow() : nullptr;
auto inner = create_imvu_engine(std::move(window));
if (!inner) {
return {};
}
const protocol::View shown = view();
auto renderer = hwnd != nullptr && shown != protocol::View::Off ? RendererProcess::start(hwnd, shown) : nullptr;
return imvu::SharedPtr<imvu::PiRenderEngine>(std::make_unique<Engine>(
std::move(inner), std::make_shared<Recorder>(std::move(renderer)), hwnd));
} catch (const std::exception& e) {
// Exceptions mustn't cross into IMVU's code (a different C++ runtime).
log::info("createRenderEngine failed: {}", e.what());
return {};
} catch (...) {
log::info("createRenderEngine failed");
return {};
}
}