#include "recorder.hpp" #include #include "log.hpp" #include "pixmap.hpp" namespace t5de { namespace { // Between frames, recorded commands (mostly textures and buffers being loaded) are sent once they reach this size. constexpr std::size_t flushSize = 4 * 1024 * 1024; } // namespace void pack(std::byte* destination, const void* source, std::uint32_t stride, std::uint32_t elementSize, std::uint32_t count) { const auto* from = static_cast(source); if (stride == 0 || stride == elementSize) { std::memcpy(destination, from, std::size_t{elementSize} * count); return; } for (std::uint32_t i = 0; i < count; ++i) { std::memcpy(destination + std::size_t{i} * elementSize, from + std::size_t{i} * stride, elementSize); } } Recorder::Recorder(std::unique_ptr process) : process_(std::move(process)) {} void Recorder::flush(bool endsFrame) { if (!active()) { writer_.clear(); return; } process_->send(writer_.bytes(), endsFrame); writer_.clear(); } void Recorder::flushIfLarge() { if (writer_.size() >= flushSize) { flush(false); } } void Recorder::addPixels(protocol::Op op, std::uint32_t id, const imvu::Pixmap& pixmap, std::uint32_t flags) { const auto pixels = pixmap_pixels(pixmap); if (!pixels || pixels->pixels == nullptr) { return; } const std::size_t rowSize = std::size_t{pixels->width} * 4; std::byte* payload = nullptr; if (op == protocol::Op::CreateTexture) { payload = add(op, protocol::CreateTexture{id, pixels->width, pixels->height, flags}, rowSize * pixels->height); } else { payload = add(op, protocol::UpdateTexture{id, pixels->width, pixels->height}, rowSize * pixels->height); } if (payload == nullptr) { return; } const auto* rows = reinterpret_cast(pixels->pixels); for (std::uint32_t y = 0; y < pixels->height; ++y) { std::memcpy(payload + y * rowSize, rows + std::size_t{y} * pixels->rowBytes, rowSize); } flushIfLarge(); } std::uint32_t Recorder::createRenderTarget(std::uint32_t width, std::uint32_t height) { const std::uint32_t id = nextId_++; add(protocol::Op::CreateRenderTarget, protocol::CreateRenderTarget{id, width, height}); return id; } std::span Recorder::readBack() { return active() ? process_->readBack() : std::span{}; } std::uint32_t Recorder::createTexture(const imvu::Pixmap& pixmap, std::uint32_t flags) { const std::uint32_t id = nextId_++; addPixels(protocol::Op::CreateTexture, id, pixmap, flags); return id; } void Recorder::updateTexture(std::uint32_t id, const imvu::Pixmap& pixmap) { addPixels(protocol::Op::UpdateTexture, id, pixmap, 0); } void Recorder::destroyTexture(std::uint32_t id) { add(protocol::Op::DestroyTexture, protocol::DestroyTexture{id}); } std::uint32_t Recorder::createBuffer(protocol::BufferKind kind, const void* data, std::uint32_t stride, std::uint32_t elementSize, std::uint32_t count) { const std::uint32_t id = nextId_++; const std::size_t size = data != nullptr ? std::size_t{elementSize} * count : 0; if (std::byte* payload = add(protocol::Op::CreateBuffer, protocol::CreateBuffer{id, kind, elementSize, count}, size); payload != nullptr && size != 0) { pack(payload, data, stride, elementSize, count); } flushIfLarge(); return id; } void Recorder::updateBuffer(std::uint32_t id, const void* data, std::uint32_t stride, std::uint32_t elementSize, std::uint32_t count) { const std::size_t size = std::size_t{elementSize} * count; if (std::byte* payload = add(protocol::Op::UpdateBuffer, protocol::UpdateBuffer{id, elementSize, count}, size); payload != nullptr && data != nullptr) { pack(payload, data, stride, elementSize, count); } } void Recorder::destroyBuffer(std::uint32_t id) { add(protocol::Op::DestroyBuffer, protocol::DestroyBuffer{id}); } } // namespace t5de