#include "renderer_process.hpp" #include #include #include #include #include "log.hpp" namespace t5de { namespace { // The folder this DLL is in: the client's program folder. std::filesystem::path module_folder() { HMODULE self = nullptr; GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(&module_folder), &self); wchar_t path[MAX_PATH]; GetModuleFileNameW(self, path, MAX_PATH); return std::filesystem::path(path).parent_path(); } } // namespace std::unique_ptr RendererProcess::start(HWND window) { static std::atomic engines = 0; const std::wstring name = std::format(L"Local\\t5de-renderer-{}-{}", GetCurrentProcessId(), engines++); std::unique_ptr renderer(new RendererProcess()); renderer->mapping_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, sizeof(protocol::Channel), name.c_str()); if (renderer->mapping_ == nullptr) { log::info("renderer process: can't create the channel: error {}", GetLastError()); return nullptr; } renderer->channel_ = static_cast( MapViewOfFile(renderer->mapping_, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(protocol::Channel))); renderer->frameEvent_ = CreateEventW(nullptr, FALSE, FALSE, (name + protocol::frameEventSuffix).c_str()); if (renderer->channel_ == nullptr || renderer->frameEvent_ == nullptr) { log::info("renderer process: can't set up the channel: error {}", GetLastError()); return nullptr; } *renderer->channel_ = {.version = protocol::version, .closing = 0, .window = reinterpret_cast(window), .frames = 0}; // The renderer's window goes on top of the client's 3D view: keep the client's drawing out of it. SetWindowLongW(window, GWL_STYLE, GetWindowLongW(window, GWL_STYLE) | WS_CLIPCHILDREN); const auto exe = module_folder() / L"t5de-renderer.exe"; std::wstring command = std::format(L"\"{}\" --channel {} --parent {}", exe.wstring(), name, GetCurrentProcessId()); STARTUPINFOW startup{.cb = sizeof(STARTUPINFOW)}; PROCESS_INFORMATION process{}; if (!CreateProcessW(exe.c_str(), command.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup, &process)) { log::info("renderer process: can't start {}: error {}", exe.string(), GetLastError()); return nullptr; } CloseHandle(process.hThread); renderer->process_ = process.hProcess; log::info("renderer process: started {} (pid {}) on channel {}", exe.string(), process.dwProcessId, std::filesystem::path(name).string()); return renderer; } RendererProcess::~RendererProcess() { if (channel_ != nullptr) { channel_->closing = 1; if (frameEvent_ != nullptr) { SetEvent(frameEvent_); } UnmapViewOfFile(channel_); } for (HANDLE handle : {frameEvent_, mapping_, process_}) { if (handle != nullptr) { CloseHandle(handle); } } } void RendererProcess::frameDrawn() { ++channel_->frames; SetEvent(frameEvent_); } } // namespace t5de