#include "passthrough_engine.hpp" #include #include #include #include "log.hpp" namespace t5de { namespace { constexpr std::array 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, 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(GetProcAddress(module, "?width@Pixmap@@QBEIXZ")), reinterpret_cast(GetProcAddress(module, "?height@Pixmap@@QBEIXZ"))}; }(); if (getters.width == nullptr || getters.height == nullptr) { return {-1, -1}; } return {static_cast(getters.width(&pixmap)), static_cast(getters.height(&pixmap))}; } } // namespace PassthroughEngine::PassthroughEngine(imvu::SharedPtr inner, std::unique_ptr renderer) : inner_(std::move(inner)), renderer_(std::move(renderer)) { log::info("passthrough: wrapping the engine at {}", static_cast(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(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(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 PassthroughEngine::newPixmapRenderTarget(imvu::Vec2i size) { log::info("newPixmapRenderTarget({}, {})", size.x, size.y); count(27); return inner_->newPixmapRenderTarget(size); } imvu::SharedPtr 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& positions, const imvu::VertexAttributeArray& normals, const imvu::VertexAttributeArray& texCoords, const imvu::VertexAttributeArray& 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