#include "runtime/ps2_gif_arbiter.h" #include #include GifArbiter::GifArbiter(ProcessPacketFn processFn) : m_processFn(std::move(processFn)) { } bool GifArbiter::isImagePacket(const uint8_t *data, uint32_t sizeBytes) { if (!data || sizeBytes < 16u) return false; uint64_t tagLo = 0; std::memcpy(&tagLo, data, sizeof(tagLo)); const uint8_t flg = static_cast((tagLo >> 58) & 0x3u); return flg == 2u; } void GifArbiter::submit(GifPathId pathId, const uint8_t *data, uint32_t sizeBytes, bool path2DirectHl) { if (!data || sizeBytes < 16 || !m_processFn) return; GifArbiterPacket pkt; pkt.pathId = pathId; pkt.path2DirectHl = (pathId == GifPathId::Path2) && path2DirectHl; pkt.path3Image = (pathId == GifPathId::Path3) && isImagePacket(data, sizeBytes); pkt.data.resize(sizeBytes); std::memcpy(pkt.data.data(), data, sizeBytes); m_queue.push_back(std::move(pkt)); } void GifArbiter::drain() { if (!m_processFn) return; std::stable_sort(m_queue.begin(), m_queue.end(), [](const GifArbiterPacket &a, const GifArbiterPacket &b) { // DIRECTHL cannot preempt PATH3 IMAGE transfers. if (a.path2DirectHl != b.path2DirectHl || a.path3Image != b.path3Image) { if (a.path3Image && b.path2DirectHl) return true; if (a.path2DirectHl && b.path3Image) return false; } return pathPriority(a.pathId) < pathPriority(b.pathId); }); for (size_t i = 0; i < m_queue.size(); ++i) { auto &pkt = m_queue[i]; if (!pkt.data.empty()) { m_processFn(pkt.data.data(), static_cast(pkt.data.size())); } } m_queue.clear(); } uint8_t GifArbiter::pathPriority(GifPathId id) { return static_cast(id); }