Add the renderer process
The renderer becomes two programs: the adapter (t5deRenderEngine.dll, 32-bit, in IMVU's process) and a 64-bit renderer process (t5de-renderer.exe) drawing with Diligent Engine, fetched with FetchContent and pinned. For now a spike: the adapter starts the renderer process for each engine and signals each frame through a shared memory channel, and the renderer process draws a box into a child window in the client's 3D view. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
0d126574bf
commit
9a7d7d2908
18 files changed
+491
-57
No files matched your search
@@ -0,0 +1,27 @@
|
||||
# t5de-renderer.exe, the 64-bit renderer process, drawing with Diligent Engine.
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
# Diligent Engine's core, pinned to an exact commit. Only the Direct3D 11 backend for now; the others (Direct3D 12,
|
||||
# Vulkan for ray tracing and mesh shaders, WebGPU for browsers) are switched on as they're needed.
|
||||
set(DILIGENT_NO_DIRECT3D12 ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_NO_VULKAN ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_NO_OPENGL ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_NO_WEBGPU ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_NO_ARCHIVER ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_NO_SUPER_RESOLUTION ON CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(DILIGENT_INSTALL_CORE OFF CACHE BOOL "" FORCE)
|
||||
FetchContent_Declare(DiligentCore
|
||||
GIT_REPOSITORY https://github.com/DiligentGraphics/DiligentCore.git
|
||||
GIT_TAG c555e4d26de3f414eaf792fa16c0c24827619b17 # master, 2026-10-27
|
||||
GIT_SUBMODULES ThirdParty/xxHash
|
||||
EXCLUDE_FROM_ALL
|
||||
SYSTEM)
|
||||
FetchContent_MakeAvailable(DiligentCore)
|
||||
|
||||
add_executable(t5de_renderer WIN32 main.cpp)
|
||||
set_target_properties(t5de_renderer PROPERTIES OUTPUT_NAME t5de-renderer)
|
||||
target_link_libraries(t5de_renderer PRIVATE t5de::protocol t5de::options Diligent-GraphicsEngineD3D11-static Diligent-Common)
|
||||
|
||||
install(TARGETS t5de_renderer RUNTIME DESTINATION .)
|
||||
@@ -0,0 +1,137 @@
|
||||
// t5de-renderer.exe: the renderer process. Started by the adapter (t5deRenderEngine.dll) for each of the client's
|
||||
// 3D views; draws into a child window it places in the client's window, frame by frame as the client draws.
|
||||
//
|
||||
// For now a spike proving the architecture: a small box in the 3D view's corner, its color changing with each frame
|
||||
// the client draws, drawn with Diligent (Direct3D 11) from this 64-bit process.
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "EngineFactoryD3D11.h"
|
||||
#include "RefCntAutoPtr.hpp"
|
||||
#include "t5de/protocol/channel.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace Diligent;
|
||||
|
||||
// The box: where in the client's 3D view, and how big.
|
||||
constexpr int boxX = 16;
|
||||
constexpr int boxY = 16;
|
||||
constexpr int boxWidth = 240;
|
||||
constexpr int boxHeight = 80;
|
||||
|
||||
struct Arguments {
|
||||
std::wstring channel;
|
||||
DWORD parent = 0;
|
||||
};
|
||||
|
||||
Arguments parse_arguments()
|
||||
{
|
||||
Arguments arguments;
|
||||
int count = 0;
|
||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &count);
|
||||
for (int i = 1; i + 1 < count; i += 2) {
|
||||
const std::wstring_view name = argv[i];
|
||||
if (name == L"--channel") {
|
||||
arguments.channel = argv[i + 1];
|
||||
} else if (name == L"--parent") {
|
||||
arguments.parent = static_cast<DWORD>(std::wcstoul(argv[i + 1], nullptr, 10));
|
||||
}
|
||||
}
|
||||
LocalFree(argv);
|
||||
return arguments;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK window_procedure(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
|
||||
{
|
||||
if (message == WM_ERASEBKGND) {
|
||||
return 1; // we draw all of it
|
||||
}
|
||||
return DefWindowProcW(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
// A child of the client's window. Disabled, so mouse input goes to the client's window under it.
|
||||
HWND create_child_window(HWND parent)
|
||||
{
|
||||
WNDCLASSEXW windowClass{.cbSize = sizeof(WNDCLASSEXW),
|
||||
.lpfnWndProc = window_procedure,
|
||||
.hInstance = GetModuleHandleW(nullptr),
|
||||
.hCursor = LoadCursorW(nullptr, IDC_ARROW),
|
||||
.lpszClassName = L"T5DERenderer"};
|
||||
RegisterClassExW(&windowClass);
|
||||
return CreateWindowExW(0, windowClass.lpszClassName, L"", WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_CLIPSIBLINGS,
|
||||
boxX, boxY, boxWidth, boxHeight, parent, nullptr, windowClass.hInstance, nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
{
|
||||
const Arguments arguments = parse_arguments();
|
||||
if (arguments.channel.empty()) {
|
||||
return 2;
|
||||
}
|
||||
HANDLE mapping = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, arguments.channel.c_str());
|
||||
auto* channel = mapping == nullptr ? nullptr
|
||||
: static_cast<t5de::protocol::Channel*>(MapViewOfFile(
|
||||
mapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(t5de::protocol::Channel)));
|
||||
HANDLE frameEvent = OpenEventW(SYNCHRONIZE, FALSE, (arguments.channel + t5de::protocol::frameEventSuffix).c_str());
|
||||
HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, arguments.parent);
|
||||
if (channel == nullptr || frameEvent == nullptr || parent == nullptr || channel->version != t5de::protocol::version) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
HWND window = create_child_window(reinterpret_cast<HWND>(static_cast<std::uintptr_t>(channel->window)));
|
||||
if (window == nullptr) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
IEngineFactoryD3D11* factory = GetEngineFactoryD3D11();
|
||||
RefCntAutoPtr<IRenderDevice> device;
|
||||
RefCntAutoPtr<IDeviceContext> context;
|
||||
EngineD3D11CreateInfo engineInfo;
|
||||
factory->CreateDeviceAndContextsD3D11(engineInfo, &device, &context);
|
||||
if (!device) {
|
||||
return 5;
|
||||
}
|
||||
RefCntAutoPtr<ISwapChain> swapChain;
|
||||
SwapChainDesc swapChainDesc;
|
||||
factory->CreateSwapChainD3D11(device, context, swapChainDesc, FullScreenModeDesc{}, Win32NativeWindow{window},
|
||||
&swapChain);
|
||||
if (!swapChain) {
|
||||
return 6;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const HANDLE handles[] = {frameEvent, parent};
|
||||
const DWORD result = MsgWaitForMultipleObjects(2, handles, FALSE, 1000, QS_ALLINPUT);
|
||||
MSG message;
|
||||
while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) {
|
||||
DispatchMessageW(&message);
|
||||
}
|
||||
if (result == WAIT_OBJECT_0 + 1 || channel->closing != 0) {
|
||||
break; // the client exited, or the engine is gone
|
||||
}
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// T5DE gold, pulsing with the client's frames.
|
||||
const float pulse = 0.5f + 0.5f * std::sin(static_cast<float>(channel->frames) * 0.1f);
|
||||
const float color[] = {1.0f, 0.82f * pulse + 0.1f, 0.0f, 1.0f};
|
||||
ITextureView* backBuffer = swapChain->GetCurrentBackBufferRTV();
|
||||
context->SetRenderTargets(1, &backBuffer, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
|
||||
context->ClearRenderTarget(backBuffer, color, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
|
||||
swapChain->Present(0);
|
||||
}
|
||||
|
||||
DestroyWindow(window);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user