#pragma once #include #include #include #include #include #include #include #include #include #include class RingBuffer { static constexpr auto BufferSize = 0x1000000u; public: RingBuffer(phys_ptr replayHeap) : mReplayHeap(replayHeap) { auto allocPtr = phys_ptr { nullptr }; cafe::TinyHeap_Alloc(replayHeap, 8, 0x100, &allocPtr); mRetireTimestampMemory = phys_cast(allocPtr); *mRetireTimestampMemory = 0ull; mRetireTimestamp = 0ull; mSubmitTimestamp = 0ull; } ~RingBuffer() { if (mRetireTimestampMemory) { cafe::TinyHeap_Free(mReplayHeap, mRetireTimestampMemory); } } uint64_t insertRetiredTimestamp() { auto submitTimestamp = ++mSubmitTimestamp; auto eventType = latte::VGT_EVENT_TYPE::CACHE_FLUSH_TS; writePM4( latte::pm4::EventWriteEOP { latte::VGT_EVENT_INITIATOR::get(0) .EVENT_TYPE(eventType) .EVENT_INDEX(latte::VGT_EVENT_INDEX::TS), latte::pm4::EW_ADDR_LO::get(0) .ADDR_LO(static_cast(phys_cast(mRetireTimestampMemory)) >> 2) .ENDIAN_SWAP(latte::CB_ENDIAN::SWAP_8IN64), latte::pm4::EWP_ADDR_HI::get(0) .DATA_SEL(latte::pm4::EWP_DATA_64) .INT_SEL(latte::pm4::EWP_INT_WRITE_CONFIRM), static_cast(submitTimestamp & 0xFFFFFFFF), static_cast(submitTimestamp >> 32) }); return submitTimestamp; } uint64_t flushCommandBuffer() { auto timestamp = insertRetiredTimestamp(); gpu::ringbuffer::write(mBuffer); mBuffer.clear(); return timestamp; } void waitTimestamp(uint64_t timestamp) { std::unique_lock lock{ mRetireMutex }; mRetireCV.wait(lock, [this]() { return mRetireTimestamp >= mSubmitTimestamp.load(); }); } void onGpuInterrupt() { std::unique_lock lock { mRetireMutex }; mRetireTimestamp = *mRetireTimestampMemory; mRetireCV.notify_all(); } template void writePM4(const Type &value) { auto &ncValue = const_cast(value); // Calculate the total size this object will be latte::pm4::PacketSizer sizer; ncValue.serialise(sizer); auto totalSize = sizer.getSize() + 1; // Resize buffer auto writePosition = static_cast(mBuffer.size()); mBuffer.resize(mBuffer.size() + totalSize); // Serialize the packet to the active command buffer auto writer = latte::pm4::PacketWriter { mBuffer.data(), writePosition, Type::Opcode, totalSize }; ncValue.serialise(writer); decaf_check(writePosition == mBuffer.size()); } void writeBuffer(const void *buffer, uint32_t numWords) { auto offset = mBuffer.size(); mBuffer.resize(mBuffer.size() + numWords); std::memcpy(mBuffer.data() + offset, buffer, numWords * 4); } private: std::vector mBuffer; phys_ptr mReplayHeap = nullptr; phys_ptr mRetireTimestampMemory = nullptr; std::atomic mSubmitTimestamp = 0ull; std::atomic mRetireTimestamp = 0ull; std::mutex mRetireMutex; std::condition_variable mRetireCV; };