rpcsx/rpcs3/Emu/RSX/Common/surface_cache_dma.hpp
DH 62ad27d1e2 split rpcs3 and hle libraries
merge rpcs3 utilities
2025-04-08 19:46:57 +03:00

132 lines
3.2 KiB
C++

#pragma once
#include <util/types.hpp>
#include "util/address_range.h"
namespace rsx
{
template <typename Traits, int BlockSize>
class surface_cache_dma
{
protected:
static inline u32 block_for(u32 address)
{
return address / BlockSize;
}
static inline u32 block_address(u32 block_id)
{
return block_id * BlockSize;
}
using buffer_object_storage_type = typename Traits::buffer_object_storage_type;
using buffer_object_type = typename Traits::buffer_object_type;
using command_list_type = typename Traits::command_list_type;
struct memory_buffer_entry_t
{
u32 id;
buffer_object_storage_type bo;
u64 memory_tag = 0;
u32 base_address = 0;
inline buffer_object_type get()
{
return Traits::get(bo);
}
inline operator bool() const
{
return base_address != 0;
}
inline void release()
{
bo.release();
}
inline void acquire(buffer_object_type b)
{
bo = b;
}
};
using buffer_block_array = typename std::array<memory_buffer_entry_t, 0x100000000ull / BlockSize>;
buffer_block_array m_buffer_list;
public:
surface_cache_dma()
{
for (u32 i = 0; i < ::size32(m_buffer_list); ++i)
{
m_buffer_list[i].id = i;
}
}
surface_cache_dma& with_range(command_list_type cmd, const utils::address_range& range)
{
// Prepare underlying memory so that the range specified is provisioned and contiguous
// 1. Check if we have a pre-existing bo layer
const auto& this_entry = m_buffer_list[block_for(range.start)];
if (this_entry)
{
const auto bo = this_entry.get();
const auto buffer_range = utils::address_range::start_length(bo.base_address, ::size32(*bo));
if (range.inside(buffer_range))
{
// All is well
return *this;
}
}
// Data does not exist or is not contiguous. Merge the layer
std::vector<buffer_object_type> bo_list;
const auto start_address = this_entry ? this_entry.base_address : block_address(this_entry.id);
for (u32 address = start_address; address <= range.end;)
{
auto& bo_storage = m_buffer_list[block_for(address)];
bo_storage.base_address = start_address;
if (auto bo = bo_storage.get())
{
bo_list.push_back(bo);
bo_storage.release();
address += ::size32(*bo);
continue;
}
bo_list.push_back(nullptr);
address += BlockSize;
}
auto unified = Traits::template merge_bo_list<BlockSize>(cmd, bo_list);
ensure(unified);
m_buffer_list[block_for(start_address)].acquire(unified);
return *this;
}
utils::address_range to_block_range(const utils::address_range& range)
{
u32 start = block_address(block_for(range.start));
u32 end = block_address(block_for(range.end + BlockSize - 1));
return utils::address_range::start_end(start, end - 1);
}
std::tuple<buffer_object_type, u32, u64> get(u32 address)
{
const auto& block = m_buffer_list[block_for(address)];
return {block.get(), block.base_address - address};
}
void touch(const utils::address_range& range)
{
const u64 stamp = rsx::get_shared_tag();
for (usz i = block_for(range.start); i <= block_for(range.end); i++)
{
m_buffer_list[i].memory_tag = stamp;
}
}
};
} // namespace rsx