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,395 @@
|
||||
#include "passthrough_engine.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
namespace t5de {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<const char*, PassthroughEngine::slots> names = {
|
||||
"~PiRenderEngine",
|
||||
"resizeFrameBuffer",
|
||||
"enable2dFrameBuffer",
|
||||
"clear",
|
||||
"setProjection",
|
||||
"setModelSpace",
|
||||
"setViewSpace",
|
||||
"setTextures",
|
||||
"setDepthTestingOn",
|
||||
"setDepthWritingOn",
|
||||
"setBlendMode",
|
||||
"setAlphaTestingOn",
|
||||
"setAlphaTestValue",
|
||||
"setBlendingOn",
|
||||
"setBackFaceCullingOn",
|
||||
"setNormalInvertingOn",
|
||||
"setLightingOn",
|
||||
"setLightProperties",
|
||||
"setFogOn",
|
||||
"setFogAttributes",
|
||||
"setTextureTransformMatrixIdentity",
|
||||
"setTextureTransformMatrix",
|
||||
"setEnvironmentAmbient",
|
||||
"newRenderTexture",
|
||||
"newIndexBuffer",
|
||||
"newStaticVertexBuffer",
|
||||
"newDynamicVertexBuffer",
|
||||
"newPixmapRenderTarget",
|
||||
"newTextureRenderTarget",
|
||||
"beginRendering",
|
||||
"endRendering",
|
||||
"drawTriangles",
|
||||
"drawLine",
|
||||
"drawPointSprites",
|
||||
"drawTrianglesFromMemory",
|
||||
};
|
||||
|
||||
// Frames whose calls are logged: the first few, then one a minute or so at 30 FPS.
|
||||
bool logged(std::uint64_t frame) { return frame < 5 || frame % 1800 == 0; }
|
||||
|
||||
// Whether to log a call's arguments: the first few calls of each method, in the whole process. Enough to check that
|
||||
// our layouts of IMVU's types read sensible values.
|
||||
bool sampled(std::size_t slot)
|
||||
{
|
||||
static std::array<std::atomic<int>, PassthroughEngine::slots> calls{};
|
||||
return calls[slot]++ < 3;
|
||||
}
|
||||
|
||||
std::string text(const imvu::Vec3f& v) { return std::format("({:.3f}, {:.3f}, {:.3f})", v.x, v.y, v.z); }
|
||||
|
||||
std::string text(const imvu::Transform& t)
|
||||
{
|
||||
const auto& m = t.linear;
|
||||
return std::format("[{:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f}] + {}", m[0][0], m[0][1],
|
||||
m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], text(t.translation));
|
||||
}
|
||||
|
||||
// A pixmap's size, from pixmap.dll's exported Pixmap::width and Pixmap::height.
|
||||
imvu::Vec2i pixmap_size(const imvu::Pixmap& pixmap)
|
||||
{
|
||||
using Getter = unsigned(__thiscall*)(const imvu::Pixmap*);
|
||||
struct Getters {
|
||||
Getter width;
|
||||
Getter height;
|
||||
};
|
||||
static const Getters getters = [] {
|
||||
HMODULE module = GetModuleHandleW(L"pixmap.dll");
|
||||
if (module == nullptr) {
|
||||
return Getters{};
|
||||
}
|
||||
return Getters{reinterpret_cast<Getter>(GetProcAddress(module, "?width@Pixmap@@QBEIXZ")),
|
||||
reinterpret_cast<Getter>(GetProcAddress(module, "?height@Pixmap@@QBEIXZ"))};
|
||||
}();
|
||||
if (getters.width == nullptr || getters.height == nullptr) {
|
||||
return {-1, -1};
|
||||
}
|
||||
return {static_cast<int>(getters.width(&pixmap)), static_cast<int>(getters.height(&pixmap))};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PassthroughEngine::PassthroughEngine(imvu::SharedPtr<imvu::PiRenderEngine> inner,
|
||||
std::unique_ptr<RendererProcess> renderer)
|
||||
: inner_(std::move(inner)), renderer_(std::move(renderer))
|
||||
{
|
||||
log::info("passthrough: wrapping the engine at {}", static_cast<void*>(inner_.get()));
|
||||
}
|
||||
|
||||
PassthroughEngine::~PassthroughEngine() { log::info("passthrough: destroyed after {} frames", frames_); }
|
||||
|
||||
void PassthroughEngine::count(std::size_t slot) { ++calls_[slot]; }
|
||||
|
||||
void PassthroughEngine::resizeFrameBuffer(imvu::Vec2i size)
|
||||
{
|
||||
log::info("resizeFrameBuffer({}, {})", size.x, size.y);
|
||||
count(1);
|
||||
inner_->resizeFrameBuffer(size);
|
||||
}
|
||||
|
||||
void PassthroughEngine::enable2dFrameBuffer()
|
||||
{
|
||||
count(2);
|
||||
inner_->enable2dFrameBuffer();
|
||||
}
|
||||
|
||||
void PassthroughEngine::clear(std::uint32_t flags, imvu::Color color)
|
||||
{
|
||||
if (sampled(3)) {
|
||||
log::info("clear(flags {}, color {:08x})", flags, color);
|
||||
}
|
||||
count(3);
|
||||
inner_->clear(flags, color);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setProjection(imvu::Vec2i viewport, const imvu::Mat4f& projection)
|
||||
{
|
||||
if (sampled(4)) {
|
||||
const auto& m = projection.m;
|
||||
log::info("setProjection({}x{}, [{:.3f} {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f} {:.3f}; "
|
||||
"{:.3f} {:.3f} {:.3f} {:.3f}; {:.3f} {:.3f} {:.3f} {:.3f}])",
|
||||
viewport.x, viewport.y, m[0][0], m[0][1], m[0][2], m[0][3], m[1][0], m[1][1], m[1][2],
|
||||
m[1][3], m[2][0], m[2][1], m[2][2], m[2][3], m[3][0], m[3][1], m[3][2], m[3][3]);
|
||||
}
|
||||
count(4);
|
||||
inner_->setProjection(viewport, projection);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setModelSpace(const imvu::Transform& model)
|
||||
{
|
||||
if (sampled(5)) {
|
||||
log::info("setModelSpace({})", text(model));
|
||||
}
|
||||
count(5);
|
||||
inner_->setModelSpace(model);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setViewSpace(const imvu::Transform& view)
|
||||
{
|
||||
if (sampled(6)) {
|
||||
log::info("setViewSpace({})", text(view));
|
||||
}
|
||||
count(6);
|
||||
inner_->setViewSpace(view);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setTextures(imvu::PiRenderTexture* texture0, imvu::PiRenderTexture* texture1)
|
||||
{
|
||||
count(7);
|
||||
inner_->setTextures(texture0, texture1);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setDepthTestingOn(bool on)
|
||||
{
|
||||
count(8);
|
||||
inner_->setDepthTestingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setDepthWritingOn(bool on)
|
||||
{
|
||||
count(9);
|
||||
inner_->setDepthWritingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setBlendMode(imvu::RenderBlendMode mode)
|
||||
{
|
||||
if (sampled(10)) {
|
||||
log::info("setBlendMode({})", static_cast<int>(mode));
|
||||
}
|
||||
count(10);
|
||||
inner_->setBlendMode(mode);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setAlphaTestingOn(bool on)
|
||||
{
|
||||
count(11);
|
||||
inner_->setAlphaTestingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setAlphaTestValue(float value)
|
||||
{
|
||||
if (sampled(12)) {
|
||||
log::info("setAlphaTestValue({:.3f})", value);
|
||||
}
|
||||
count(12);
|
||||
inner_->setAlphaTestValue(value);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setBlendingOn(bool on)
|
||||
{
|
||||
count(13);
|
||||
inner_->setBlendingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setBackFaceCullingOn(bool on)
|
||||
{
|
||||
count(14);
|
||||
inner_->setBackFaceCullingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setNormalInvertingOn(bool on)
|
||||
{
|
||||
count(15);
|
||||
inner_->setNormalInvertingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setLightingOn(bool on)
|
||||
{
|
||||
count(16);
|
||||
inner_->setLightingOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setLightProperties(std::uint32_t index, const imvu::LightProperties& light)
|
||||
{
|
||||
if (sampled(17)) {
|
||||
log::info("setLightProperties({}, enabled {}, diffuse {:08x}, vector {}, type {})", index,
|
||||
light.enabled, light.diffuse, text(light.vector), static_cast<int>(light.type));
|
||||
}
|
||||
count(17);
|
||||
inner_->setLightProperties(index, light);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setFogOn(bool on)
|
||||
{
|
||||
count(18);
|
||||
inner_->setFogOn(on);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setFogAttributes(float start, float end, imvu::Color color)
|
||||
{
|
||||
if (sampled(19)) {
|
||||
log::info("setFogAttributes(start {:.3f}, end {:.3f}, color {:08x})", start, end, color);
|
||||
}
|
||||
count(19);
|
||||
inner_->setFogAttributes(start, end, color);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setTextureTransformMatrixIdentity(std::uint32_t stage)
|
||||
{
|
||||
count(20);
|
||||
inner_->setTextureTransformMatrixIdentity(stage);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setTextureTransformMatrix(std::uint32_t stage, const imvu::TextureTransform& transform)
|
||||
{
|
||||
if (sampled(21)) {
|
||||
const auto& m = transform.m;
|
||||
log::info("setTextureTransformMatrix({}, [{:.3f} {:.3f} {:.3f} {:.3f} {:.3f} {:.3f}])", stage, m[0],
|
||||
m[1], m[2], m[3], m[4], m[5]);
|
||||
}
|
||||
count(21);
|
||||
inner_->setTextureTransformMatrix(stage, transform);
|
||||
}
|
||||
|
||||
void PassthroughEngine::setEnvironmentAmbient(imvu::Color color)
|
||||
{
|
||||
if (sampled(22)) {
|
||||
log::info("setEnvironmentAmbient({:08x})", color);
|
||||
}
|
||||
count(22);
|
||||
inner_->setEnvironmentAmbient(color);
|
||||
}
|
||||
|
||||
imvu::PiRenderTexture* PassthroughEngine::newRenderTexture(const imvu::Pixmap& pixmap, std::uint32_t flags)
|
||||
{
|
||||
if (sampled(23)) {
|
||||
const auto size = pixmap_size(pixmap);
|
||||
log::info("newRenderTexture({}x{} pixmap, flags {:x})", size.x, size.y, flags);
|
||||
}
|
||||
count(23);
|
||||
return inner_->newRenderTexture(pixmap, flags);
|
||||
}
|
||||
|
||||
imvu::PiInternalIndexBuffer* PassthroughEngine::newIndexBuffer(std::uint32_t indexCount, const std::uint16_t* indices)
|
||||
{
|
||||
if (sampled(24)) {
|
||||
log::info("newIndexBuffer({} indices)", indexCount);
|
||||
}
|
||||
count(24);
|
||||
return inner_->newIndexBuffer(indexCount, indices);
|
||||
}
|
||||
|
||||
imvu::PiInternalVertexBuffer* PassthroughEngine::newStaticVertexBuffer(const void* data, std::uint32_t stride,
|
||||
std::uint32_t elementSize, std::uint32_t elementCount)
|
||||
{
|
||||
if (sampled(25)) {
|
||||
log::info("newStaticVertexBuffer(stride {}, element size {}, {} elements)", stride, elementSize,
|
||||
elementCount);
|
||||
}
|
||||
count(25);
|
||||
return inner_->newStaticVertexBuffer(data, stride, elementSize, elementCount);
|
||||
}
|
||||
|
||||
imvu::PiInternalVertexBuffer* PassthroughEngine::newDynamicVertexBuffer(std::uint32_t elementSize, std::uint32_t elementCount)
|
||||
{
|
||||
if (sampled(26)) {
|
||||
log::info("newDynamicVertexBuffer(element size {}, {} elements)", elementSize, elementCount);
|
||||
}
|
||||
count(26);
|
||||
return inner_->newDynamicVertexBuffer(elementSize, elementCount);
|
||||
}
|
||||
|
||||
imvu::SharedPtr<imvu::PiPixmapRenderTarget> PassthroughEngine::newPixmapRenderTarget(imvu::Vec2i size)
|
||||
{
|
||||
log::info("newPixmapRenderTarget({}, {})", size.x, size.y);
|
||||
count(27);
|
||||
return inner_->newPixmapRenderTarget(size);
|
||||
}
|
||||
|
||||
imvu::SharedPtr<imvu::PiTextureRenderTarget> PassthroughEngine::newTextureRenderTarget(imvu::Vec2i size)
|
||||
{
|
||||
log::info("newTextureRenderTarget({}, {})", size.x, size.y);
|
||||
count(28);
|
||||
return inner_->newTextureRenderTarget(size);
|
||||
}
|
||||
|
||||
void PassthroughEngine::beginRendering(imvu::PiRenderTarget* target)
|
||||
{
|
||||
count(29);
|
||||
inner_->beginRendering(target);
|
||||
}
|
||||
|
||||
void PassthroughEngine::endRendering()
|
||||
{
|
||||
count(30);
|
||||
inner_->endRendering();
|
||||
if (renderer_) {
|
||||
renderer_->frameDrawn();
|
||||
}
|
||||
if (logged(frames_)) {
|
||||
std::string line = std::format("frame {}:", frames_);
|
||||
for (std::size_t slot = 1; slot < slots; ++slot) {
|
||||
if (calls_[slot] != 0) {
|
||||
line += std::format(" {} x{},", names[slot], calls_[slot]);
|
||||
}
|
||||
}
|
||||
line.pop_back();
|
||||
log::write(line);
|
||||
}
|
||||
calls_ = {};
|
||||
++frames_;
|
||||
}
|
||||
|
||||
void PassthroughEngine::drawTriangles(imvu::PiInternalIndexBuffer* indices, imvu::PiInternalVertexBuffer* positions,
|
||||
imvu::PiInternalVertexBuffer* normals, imvu::PiInternalVertexBuffer* texCoords,
|
||||
imvu::PiInternalVertexBuffer* colors)
|
||||
{
|
||||
count(31);
|
||||
inner_->drawTriangles(indices, positions, normals, texCoords, colors);
|
||||
}
|
||||
|
||||
void PassthroughEngine::drawLine(imvu::Vec3f from, imvu::Vec3f to, std::uint32_t color)
|
||||
{
|
||||
if (sampled(32)) {
|
||||
log::info("drawLine({}, {}, {:08x})", text(from), text(to), color);
|
||||
}
|
||||
count(32);
|
||||
inner_->drawLine(from, to, color);
|
||||
}
|
||||
|
||||
void PassthroughEngine::drawPointSprites(const imvu::PointSpriteVector& sprites)
|
||||
{
|
||||
count(33);
|
||||
inner_->drawPointSprites(sprites);
|
||||
}
|
||||
|
||||
void PassthroughEngine::drawTrianglesFromMemory(std::uint32_t vertexCount, std::uint32_t indexCount,
|
||||
const std::uint16_t* indices,
|
||||
const imvu::VertexAttributeArray<imvu::Vec3f>& positions,
|
||||
const imvu::VertexAttributeArray<imvu::Vec3f>& normals,
|
||||
const imvu::VertexAttributeArray<imvu::Vec2f>& texCoords,
|
||||
const imvu::VertexAttributeArray<imvu::Color>& colors)
|
||||
{
|
||||
if (sampled(34)) {
|
||||
log::info("drawTrianglesFromMemory({} vertices, {} indices; positions {} stride {}, colors {} stride {})",
|
||||
vertexCount, indexCount, positions.count, positions.stride, colors.count, colors.stride);
|
||||
}
|
||||
count(34);
|
||||
inner_->drawTrianglesFromMemory(vertexCount, indexCount, indices, positions, normals, texCoords, colors);
|
||||
}
|
||||
|
||||
} // namespace t5de
|
||||
Reference in new issue
Block a user