mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29825a3290 | |||
| 10e924c8cb | |||
| f67a300a69 | |||
| 1330abd2b6 | |||
| db708d2ea0 | |||
| cf76977977 | |||
| 20e6c163a6 | |||
| dfb2fa717e | |||
| 787f80e05c | |||
| 142eeb0b8a | |||
| c394eef32e | |||
| a2d24b3eb7 | |||
| 59ae72c97d | |||
| 723afa7d46 | |||
| 8d96b2e894 | |||
| 1b01f5c93a | |||
| 43ed5cf9b5 | |||
| 3c2f298101 | |||
| cece80d688 | |||
| 0b1c69bc39 | |||
| f3bd7aa402 | |||
| e0a742277a | |||
| aec1658697 | |||
| 65e45f0a9a | |||
| a8f4fdd20f | |||
| b24d8b3912 |
+1
-1
@@ -594,7 +594,7 @@ abstract class SettingsItem(
|
||||
IntSetting.ANDROID_PIPELINE_WORKERS,
|
||||
titleId = R.string.pipeline_worker_cores,
|
||||
descriptionId = R.string.pipeline_worker_cores_description,
|
||||
min = 4,
|
||||
min = 1,
|
||||
max = 8,
|
||||
units = "cores"
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace AndroidSettings {
|
||||
&show_performance_overlay};
|
||||
|
||||
|
||||
Settings::Setting<s32> pipeline_worker_count{linkage, 4, "pipeline_worker_count",
|
||||
Settings::Setting<s32> pipeline_worker_count{linkage, 2, "pipeline_worker_count",
|
||||
Settings::Category::Android,
|
||||
Settings::Specialization::Default,
|
||||
true,
|
||||
|
||||
@@ -157,6 +157,8 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info,
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
|
||||
constexpr size_t NCE_WRITE_FAULT_CLUSTER_PAGES = 4;
|
||||
|
||||
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
@@ -165,7 +167,7 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi
|
||||
const Common::ProcessAddress addr =
|
||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
||||
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE * NCE_WRITE_FAULT_CLUSTER_PAGES)) {
|
||||
// We handled the access successfully and are returning to guest code.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,10 @@ public:
|
||||
// New batch API to update multiple ranges with a single lock acquisition.
|
||||
void UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta);
|
||||
|
||||
void UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta);
|
||||
|
||||
[[nodiscard]] bool IsRegionTextureCached(DAddr addr, size_t size) const noexcept;
|
||||
|
||||
private:
|
||||
struct TranslationEntry {
|
||||
DAddr guest_page{};
|
||||
@@ -234,6 +238,7 @@ private:
|
||||
(1ULL << (device_virtual_bits - page_bits)) / subentries;
|
||||
using CachedPages = std::array<CounterEntry, num_counter_entries>;
|
||||
std::unique_ptr<CachedPages> cached_pages;
|
||||
std::unique_ptr<CachedPages> texture_cached_pages;
|
||||
Common::RangeMutex counter_guard;
|
||||
std::mutex mapping_guard;
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
||||
{
|
||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||
cached_pages = std::make_unique<CachedPages>();
|
||||
texture_cached_pages = std::make_unique<CachedPages>();
|
||||
|
||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||
for (size_t i = 0; i < total_virtual; i++) {
|
||||
@@ -625,6 +626,28 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
UpdatePagesCachedCountNoLock(addr, size, delta);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta) {
|
||||
Common::ScopedRangeLock lk(counter_guard, addr, size);
|
||||
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
|
||||
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
|
||||
CounterAtomicType& count = texture_cached_pages->at(page >> subentries_shift).Count(page);
|
||||
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
bool DeviceMemoryManager<Traits>::IsRegionTextureCached(DAddr addr, size_t size) const noexcept {
|
||||
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
|
||||
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
|
||||
if (texture_cached_pages->at(page >> subentries_shift).Count(page).load(
|
||||
std::memory_order_acquire) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
|
||||
if (ranges.empty()) {
|
||||
|
||||
@@ -34,8 +34,7 @@ oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
||||
return pass;
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
@@ -126,53 +125,31 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label pass = EmitA32Cond(code, ctx, terminal.if_);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(pass);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||
code.CBZ(Wscratch0, fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDAR(Wscratch0, Xhalt);
|
||||
code.CBNZ(Wscratch0, fail);
|
||||
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA32LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||
|
||||
@@ -33,8 +33,7 @@ oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
||||
return pass;
|
||||
}
|
||||
|
||||
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
@@ -109,53 +108,31 @@ void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label pass = EmitA64Cond(code, ctx, terminal.if_);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(pass);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||
code.CBZ(Wscratch0, fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
oaknut::Label fail;
|
||||
code.LDAR(Wscratch0, Xhalt);
|
||||
code.CBNZ(Wscratch0, fail);
|
||||
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||
code.l(fail);
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||
}
|
||||
|
||||
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA64LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
boost::apply_visitor([&](const auto& t) { EmitA64Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||
|
||||
@@ -112,7 +112,6 @@ void EmitA32Cond(biscuit::Assembler& as, EmitContext&, IR::Cond cond, biscuit::L
|
||||
}
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
@@ -171,18 +170,18 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::FastDis
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
biscuit::Label pass;
|
||||
EmitA32Cond(as, ctx, terminal.if_, &pass);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
as.Bind(&pass);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
biscuit::Label fail;
|
||||
as.LBU(Xscratch0, offsetof(StackLayout, check_bit), Xstate);
|
||||
as.BEQZ(Xscratch0, &fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||
as.Bind(&fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
@@ -190,35 +189,13 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHa
|
||||
as.LWU(Xscratch0, 0, Xhalt);
|
||||
as.FENCE(biscuit::FenceOrder::RW, biscuit::FenceOrder::RW);
|
||||
as.BNEZ(Xscratch0, &fail);
|
||||
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||
as.Bind(&fail);
|
||||
EmitRelocation(as, ctx, LinkTarget::ReturnFromRunCode);
|
||||
}
|
||||
|
||||
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(as, ctx, t, initial_location, is_single_step); }, terminal);
|
||||
}
|
||||
|
||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) {
|
||||
|
||||
@@ -175,7 +175,7 @@ finish_this_inst:
|
||||
if (conf.enable_cycle_counting)
|
||||
EmitAddCycles(block.CycleCount());
|
||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.int3();
|
||||
|
||||
for (auto& deferred_emit : ctx.deferred_emits)
|
||||
@@ -219,7 +219,7 @@ void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) {
|
||||
if (conf.enable_cycle_counting) {
|
||||
EmitAddCycles(ctx.block.ConditionFailedCycleCount());
|
||||
}
|
||||
EmitLeafTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.L(pass);
|
||||
}
|
||||
|
||||
@@ -1155,12 +1155,11 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
e.code.ReturnFromRunCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||
@@ -1187,10 +1186,9 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
||||
e.PushRSBHelper(rax, rbx, terminal.next);
|
||||
e.code.ForceReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||
@@ -1203,78 +1201,55 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
||||
e.EmitPatchJmp(terminal.next);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.code.L(pass);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label fail;
|
||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||
e.code.jz(fail);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.code.L(fail);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool A32EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
void EmitTerminalImpl(A32EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool A32EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitLeafTerminal(*e, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void A32EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||
|
||||
@@ -112,8 +112,7 @@ public:
|
||||
|
||||
// Terminal instruction emitters
|
||||
void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location);
|
||||
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
|
||||
// Patching
|
||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include <fmt/ostream.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/ir/terminal.h"
|
||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
@@ -148,7 +147,7 @@ finish_this_inst:
|
||||
if (conf.enable_cycle_counting)
|
||||
EmitAddCycles(block.CycleCount());
|
||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||
code.int3();
|
||||
for (auto& deferred_emit : ctx.deferred_emits)
|
||||
deferred_emit();
|
||||
@@ -618,12 +617,11 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||
e.code.ReturnFromRunCode();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
// Used for patches and linking
|
||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||
if (e.conf.enable_cycle_counting) {
|
||||
@@ -651,10 +649,9 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||
e.patch_information[terminal.next].jmp.push_back(e.code.getCurr());
|
||||
if (auto next_bb = e.GetBasicBlock(terminal.next)) {
|
||||
@@ -667,86 +664,63 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) {
|
||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||
} else {
|
||||
e.code.ReturnFromRunCode();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||
e.code.ReturnFromRunCode();
|
||||
} else {
|
||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
switch (terminal.if_) {
|
||||
case IR::Cond::AL:
|
||||
case IR::Cond::NV:
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
break;
|
||||
default:
|
||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
e.code.L(pass);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
Xbyak::Label fail;
|
||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||
e.code.jz(fail);
|
||||
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
||||
e.code.L(fail);
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||
return true;
|
||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool A64EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
void EmitTerminalImpl(A64EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool A64EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||
return EmitLeafTerminal(*x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||
UNREACHABLE();
|
||||
void A64EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||
|
||||
@@ -107,8 +107,7 @@ public:
|
||||
void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst);
|
||||
|
||||
// Terminal instruction emitters
|
||||
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||
|
||||
// Patching
|
||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||
|
||||
@@ -111,8 +111,7 @@ public:
|
||||
#ifndef NDEBUG
|
||||
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
||||
#endif
|
||||
virtual bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
virtual bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||
|
||||
// Patching
|
||||
struct PatchInformation {
|
||||
|
||||
@@ -66,44 +66,43 @@ void Block::Reset(LocationDescriptor location_) noexcept {
|
||||
location = location_;
|
||||
end_location = location_;
|
||||
cond = Cond::AL;
|
||||
terminal = std::monostate{};
|
||||
terminal = Term::Invalid{};
|
||||
cond_failed_cycle_count = 0;
|
||||
cycle_count = 0;
|
||||
ASSERT(instructions.size() == 0);
|
||||
}
|
||||
|
||||
static std::string TerminalToString(const Term::Terminal& terminal_variant) noexcept {
|
||||
// struct : boost::static_visitor<std::string> {
|
||||
// std::string operator()(const std::monostate&) const {
|
||||
// return "<invalid>";
|
||||
// }
|
||||
// std::string operator()(const Term::ReturnToDispatch&) const {
|
||||
// return "ReturnToDispatch{}";
|
||||
// }
|
||||
// std::string operator()(const Term::LinkBlock& terminal) const {
|
||||
// return fmt::format("LinkBlock{{{}}}", terminal.next);
|
||||
// }
|
||||
// std::string operator()(const Term::LinkBlockFast& terminal) const {
|
||||
// return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
||||
// }
|
||||
// std::string operator()(const Term::PopRSBHint&) const {
|
||||
// return "PopRSBHint{}";
|
||||
// }
|
||||
// std::string operator()(const Term::FastDispatchHint&) const {
|
||||
// return "FastDispatchHint{}";
|
||||
// }
|
||||
// std::string operator()(const Term::If& terminal) const {
|
||||
// return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
// }
|
||||
// std::string operator()(const Term::CheckBit& terminal) const {
|
||||
// return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
// }
|
||||
// std::string operator()(const Term::CheckHalt& terminal) const {
|
||||
// return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||
// }
|
||||
// } visitor;
|
||||
// return boost::apply_visitor(visitor, terminal_variant);
|
||||
return "";
|
||||
static std::string TerminalToString(const Terminal& terminal_variant) noexcept {
|
||||
struct : boost::static_visitor<std::string> {
|
||||
std::string operator()(const Term::Invalid&) const {
|
||||
return "<invalid terminal>";
|
||||
}
|
||||
std::string operator()(const Term::ReturnToDispatch&) const {
|
||||
return "ReturnToDispatch{}";
|
||||
}
|
||||
std::string operator()(const Term::LinkBlock& terminal) const {
|
||||
return fmt::format("LinkBlock{{{}}}", terminal.next);
|
||||
}
|
||||
std::string operator()(const Term::LinkBlockFast& terminal) const {
|
||||
return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
||||
}
|
||||
std::string operator()(const Term::PopRSBHint&) const {
|
||||
return "PopRSBHint{}";
|
||||
}
|
||||
std::string operator()(const Term::FastDispatchHint&) const {
|
||||
return "FastDispatchHint{}";
|
||||
}
|
||||
std::string operator()(const Term::If& terminal) const {
|
||||
return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
}
|
||||
std::string operator()(const Term::CheckBit& terminal) const {
|
||||
return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||
}
|
||||
std::string operator()(const Term::CheckHalt& terminal) const {
|
||||
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||
}
|
||||
} visitor;
|
||||
return boost::apply_visitor(visitor, terminal_variant);
|
||||
}
|
||||
|
||||
std::string DumpBlock(const IR::Block& block) noexcept {
|
||||
|
||||
@@ -114,22 +114,22 @@ public:
|
||||
}
|
||||
|
||||
/// Gets the terminal instruction for this basic block.
|
||||
inline Term::Terminal GetTerminal() const noexcept {
|
||||
inline Terminal GetTerminal() const noexcept {
|
||||
return terminal;
|
||||
}
|
||||
/// Sets the terminal instruction for this basic block.
|
||||
inline void SetTerminal(Term::Terminal term) noexcept {
|
||||
inline void SetTerminal(Terminal term) noexcept {
|
||||
ASSERT(!HasTerminal() && "Terminal has already been set.");
|
||||
terminal = std::move(term);
|
||||
}
|
||||
/// Replaces the terminal instruction for this basic block.
|
||||
inline void ReplaceTerminal(Term::Terminal term) noexcept {
|
||||
inline void ReplaceTerminal(Terminal term) noexcept {
|
||||
ASSERT(HasTerminal() && "Terminal has not been set.");
|
||||
terminal = std::move(term);
|
||||
}
|
||||
/// Determines whether or not this basic block has a terminal instruction.
|
||||
inline bool HasTerminal() const noexcept {
|
||||
return !std::holds_alternative<std::monostate>(terminal);
|
||||
return terminal.which() != 0;
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the cycle count for this basic block.
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
/// Conditional to pass in order to execute this block
|
||||
Cond cond = Cond::AL;
|
||||
/// Terminal instruction of this block.
|
||||
Term::Terminal terminal = std::monostate{};
|
||||
Terminal terminal = Term::Invalid{};
|
||||
/// Number of cycles this block takes to execute if the conditional fails.
|
||||
size_t cond_failed_cycle_count = 0;
|
||||
/// Number of cycles this block takes to execute.
|
||||
|
||||
@@ -2943,7 +2943,7 @@ public:
|
||||
Inst(Opcode::CallHostFunction, Imm64(std::bit_cast<u64>(fn)), arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
void SetTerm(const Term::Terminal& terminal) {
|
||||
void SetTerm(const Terminal& terminal) {
|
||||
block.SetTerminal(terminal);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <boost/variant.hpp>
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "dynarmic/ir/cond.h"
|
||||
@@ -17,89 +17,106 @@
|
||||
namespace Dynarmic::IR {
|
||||
namespace Term {
|
||||
|
||||
/// This terminal instruction returns control to the dispatcher.
|
||||
/// The dispatcher will use the current cpu state to determine what comes next.
|
||||
struct Invalid {};
|
||||
|
||||
/**
|
||||
* This terminal instruction returns control to the dispatcher.
|
||||
* The dispatcher will use the current cpu state to determine what comes next.
|
||||
*/
|
||||
struct ReturnToDispatch {};
|
||||
|
||||
/// This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||
/// cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||
/// dispatcher, which will return control to the host.
|
||||
/**
|
||||
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||
* dispatcher, which will return control to the host.
|
||||
*/
|
||||
struct LinkBlock {
|
||||
explicit LinkBlock(const LocationDescriptor& next_) : next(next_) {}
|
||||
explicit LinkBlock(const LocationDescriptor& next_)
|
||||
: next(next_) {}
|
||||
LocationDescriptor next; ///< Location descriptor for next block.
|
||||
};
|
||||
|
||||
/// This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||
/// This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||
/// in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||
/// forward jumps to short-ish blocks would use this instruction.)
|
||||
/// A backend that doesn't support this optimization may choose to implement this exactly
|
||||
/// as LinkBlock.
|
||||
/**
|
||||
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||
* forward jumps to short-ish blocks would use this instruction.)
|
||||
* A backend that doesn't support this optimization may choose to implement this exactly
|
||||
* as LinkBlock.
|
||||
*/
|
||||
struct LinkBlockFast {
|
||||
explicit LinkBlockFast(const LocationDescriptor& next_) : next(next_) {}
|
||||
explicit LinkBlockFast(const LocationDescriptor& next_)
|
||||
: next(next_) {}
|
||||
LocationDescriptor next; ///< Location descriptor for next block.
|
||||
};
|
||||
|
||||
/// This terminal instruction checks the top of the Return Stack Buffer against the current
|
||||
/// location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
||||
/// This is an optimization for faster function calls. A backend that doesn't support
|
||||
/// this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||
/// ReturnToDispatch.
|
||||
/**
|
||||
* This terminal instruction checks the top of the Return Stack Buffer against the current
|
||||
* location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
||||
* This is an optimization for faster function calls. A backend that doesn't support
|
||||
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||
* ReturnToDispatch.
|
||||
*/
|
||||
struct PopRSBHint {};
|
||||
|
||||
/// This terminal instruction performs a lookup of the current location descriptor in the
|
||||
/// fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
||||
/// to implement this exactly as ReturnToDispatch.
|
||||
/**
|
||||
* This terminal instruction performs a lookup of the current location descriptor in the
|
||||
* fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
||||
* to implement this exactly as ReturnToDispatch.
|
||||
*/
|
||||
struct FastDispatchHint {};
|
||||
|
||||
struct If;
|
||||
struct CheckBit;
|
||||
struct CheckHalt;
|
||||
|
||||
/// Non recursive kind of terminal
|
||||
using LeafTerminal = std::variant<
|
||||
std::monostate,
|
||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||
using Terminal = boost::variant<
|
||||
Invalid,
|
||||
ReturnToDispatch,
|
||||
LinkBlock,
|
||||
LinkBlockFast,
|
||||
PopRSBHint,
|
||||
FastDispatchHint
|
||||
>;
|
||||
FastDispatchHint,
|
||||
boost::recursive_wrapper<If>,
|
||||
boost::recursive_wrapper<CheckBit>,
|
||||
boost::recursive_wrapper<CheckHalt>>;
|
||||
|
||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||
using Terminal = std::variant<
|
||||
std::monostate,
|
||||
LeafTerminal,
|
||||
If,
|
||||
CheckBit,
|
||||
CheckHalt
|
||||
>;
|
||||
|
||||
/// This terminal instruction conditionally executes one terminal or another depending
|
||||
/// on the run-time state of the ARM flags.
|
||||
/**
|
||||
* This terminal instruction conditionally executes one terminal or another depending
|
||||
* on the run-time state of the ARM flags.
|
||||
*/
|
||||
struct If {
|
||||
explicit If(Cond if_, LeafTerminal then_, LeafTerminal else_) : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
If(Cond if_, Terminal then_, Terminal else_)
|
||||
: if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
Cond if_;
|
||||
LeafTerminal then_;
|
||||
LeafTerminal else_;
|
||||
Terminal then_;
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
/// This terminal instruction conditionally executes one terminal or another depending
|
||||
/// on the run-time state of the check bit.
|
||||
/// then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||
/**
|
||||
* This terminal instruction conditionally executes one terminal or another depending
|
||||
* on the run-time state of the check bit.
|
||||
* then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||
*/
|
||||
struct CheckBit {
|
||||
explicit CheckBit(LeafTerminal then_, LeafTerminal else_) : then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
LeafTerminal then_;
|
||||
LeafTerminal else_;
|
||||
CheckBit(Terminal then_, Terminal else_)
|
||||
: then_(std::move(then_)), else_(std::move(else_)) {}
|
||||
Terminal then_;
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
/// This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||
/// executed.
|
||||
/**
|
||||
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||
* executed.
|
||||
*/
|
||||
struct CheckHalt {
|
||||
explicit CheckHalt(LeafTerminal else_) : else_(std::move(else_)) {}
|
||||
LeafTerminal else_;
|
||||
explicit CheckHalt(Terminal else_)
|
||||
: else_(std::move(else_)) {}
|
||||
Terminal else_;
|
||||
};
|
||||
|
||||
} // namespace Term
|
||||
|
||||
using Term::Terminal;
|
||||
|
||||
} // namespace Dynarmic::IR
|
||||
|
||||
@@ -43,20 +43,32 @@ namespace {
|
||||
using namespace Dynarmic;
|
||||
|
||||
template<typename Fn>
|
||||
bool AnyLocationDescriptorForTerminalHas(IR::Term::Terminal terminal, Fn fn) {
|
||||
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal)) {
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlock>(e))
|
||||
return fn(x->next);
|
||||
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(e))
|
||||
return fn(x->next);
|
||||
}
|
||||
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||
return AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||
return false;
|
||||
bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) {
|
||||
return boost::apply_visitor([&](auto t) -> bool {
|
||||
using T = std::decay_t<decltype(t)>;
|
||||
if constexpr (std::is_same_v<T, IR::Term::Invalid>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::ReturnToDispatch>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlock>) {
|
||||
return fn(t.next);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlockFast>) {
|
||||
return fn(t.next);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::PopRSBHint>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::FastDispatchHint>) {
|
||||
return false;
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::If>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckBit>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckHalt>) {
|
||||
return AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
||||
} else {
|
||||
ASSERT(false && "Invalid terminal type");
|
||||
return false;
|
||||
}
|
||||
}, terminal);
|
||||
}
|
||||
|
||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||
|
||||
@@ -121,6 +121,15 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CpuWriteInvalidate(DAddr device_addr, u64 size) {
|
||||
if (!memory_tracker.CpuMarkIfNotGpuModified(device_addr, size)) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{mutex};
|
||||
WriteMemory(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CachedWriteMemory(DAddr device_addr, u64 size) {
|
||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||
@@ -175,9 +184,71 @@ std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(DA
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
|
||||
DownloadBufferMemory(buffer, device_addr, size);
|
||||
if constexpr (!USE_MEMORY_MAPS) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
|
||||
DownloadBufferMemory(buffer, device_addr, size);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 8> downloads;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
memory_tracker.ForEachDownloadRangeAndClear(
|
||||
device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||
const DAddr buffer_addr = buffer.CpuAddr();
|
||||
const auto add_download = [&](DAddr start, DAddr end) {
|
||||
const u64 new_offset = start - buffer_addr;
|
||||
const u64 new_size = end - start;
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = new_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = new_size,
|
||||
},
|
||||
buffer_id,
|
||||
});
|
||||
constexpr u64 align = 64ULL;
|
||||
constexpr u64 mask = ~(align - 1ULL);
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = (std::max)(largest_copy, new_size);
|
||||
};
|
||||
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
|
||||
ClearDownload(device_addr_out, range_size);
|
||||
gpu_modified_ranges.Subtract(device_addr_out, range_size);
|
||||
});
|
||||
});
|
||||
if (total_size_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||
boost::container::small_vector<BufferCopy, 8> writebacks;
|
||||
runtime.PreCopyBarrier();
|
||||
for (auto& [copy, buffer_id] : downloads) {
|
||||
copy.dst_offset += download_staging.offset;
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
||||
const std::array copies{copy};
|
||||
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
||||
BufferCopy writeback{copy};
|
||||
writeback.src_offset = static_cast<u64>(buffer.CpuAddr()) + copy.src_offset;
|
||||
writebacks.push_back(writeback);
|
||||
}
|
||||
runtime.PostCopyBarrier();
|
||||
lock.unlock();
|
||||
|
||||
runtime.Finish();
|
||||
const u8* const base = download_staging.mapped_span.data();
|
||||
for (const BufferCopy& writeback : writebacks) {
|
||||
const u64 staging_offset = writeback.dst_offset - download_staging.offset;
|
||||
device_memory.WriteBlockUnsafe(static_cast<DAddr>(writeback.src_offset),
|
||||
base + staging_offset, writeback.size);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -214,7 +285,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
auto& src_buffer = slot_buffers[buffer_a];
|
||||
auto& dest_buffer = slot_buffers[buffer_b];
|
||||
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
|
||||
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
|
||||
memory_tracker.UnmarkRegionAsCpuModified(*cpu_dest_address, static_cast<u32>(amount));
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = src_buffer.Offset(*cpu_src_address),
|
||||
.dst_offset = dest_buffer.Offset(*cpu_dest_address),
|
||||
@@ -673,32 +744,44 @@ void BufferCache<P>::PopAsyncFlushes() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncBuffers() {
|
||||
if (async_buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!async_buffers.front().has_value()) {
|
||||
struct Writeback {
|
||||
DAddr addr;
|
||||
const u8* src;
|
||||
u64 size;
|
||||
};
|
||||
boost::container::small_vector<Writeback, 8> writebacks;
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
if (async_buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!async_buffers.front().has_value()) {
|
||||
async_buffers.pop_front();
|
||||
return;
|
||||
}
|
||||
auto& downloads = pending_downloads.front();
|
||||
auto& async_buffer = async_buffers.front();
|
||||
const u8* base = async_buffer->mapped_span.data();
|
||||
const size_t base_offset = async_buffer->offset;
|
||||
for (const auto& copy : downloads) {
|
||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
||||
const u64 dst_offset = copy.dst_offset - base_offset;
|
||||
const u8* read_mapped_memory = base + dst_offset;
|
||||
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
|
||||
writebacks.push_back(
|
||||
{start, &read_mapped_memory[start - device_addr], end - start});
|
||||
});
|
||||
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
||||
gpu_modified_ranges.Subtract(start, end - start);
|
||||
});
|
||||
}
|
||||
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||
async_buffers.pop_front();
|
||||
return;
|
||||
pending_downloads.pop_front();
|
||||
}
|
||||
auto& downloads = pending_downloads.front();
|
||||
auto& async_buffer = async_buffers.front();
|
||||
u8* base = async_buffer->mapped_span.data();
|
||||
const size_t base_offset = async_buffer->offset;
|
||||
for (const auto& copy : downloads) {
|
||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
||||
const u64 dst_offset = copy.dst_offset - base_offset;
|
||||
const u8* read_mapped_memory = base + dst_offset;
|
||||
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
|
||||
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
|
||||
end - start);
|
||||
});
|
||||
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
||||
gpu_modified_ranges.Subtract(start, end - start);
|
||||
});
|
||||
for (const auto& wb : writebacks) {
|
||||
device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size);
|
||||
}
|
||||
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||
async_buffers.pop_front();
|
||||
pending_downloads.pop_front();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1638,6 +1721,9 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) {
|
||||
if (!memory_tracker.HasCpuModifiedCheap(device_addr, size)) {
|
||||
return true;
|
||||
}
|
||||
upload_copies.clear();
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
|
||||
@@ -217,6 +217,8 @@ public:
|
||||
|
||||
void WriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
void CpuWriteInvalidate(DAddr device_addr, u64 size);
|
||||
|
||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
bool OnCPUWrite(DAddr device_addr, u64 size);
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
@@ -49,6 +51,24 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasCpuModifiedCheap(VAddr query_cpu_addr, u64 query_size) noexcept {
|
||||
std::size_t remaining_size{query_size};
|
||||
std::size_t page_index{query_cpu_addr >> HIGHER_PAGE_BITS};
|
||||
u64 page_offset{query_cpu_addr & HIGHER_PAGE_MASK};
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
const Manager* manager = top_tier[page_index].load(std::memory_order_acquire);
|
||||
if (manager == nullptr || manager->CpuModifiedPageCount() != 0) {
|
||||
return true;
|
||||
}
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if a region has been modified from the CPU
|
||||
[[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
|
||||
return IteratePages<true>(query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
|
||||
@@ -130,12 +150,28 @@ public:
|
||||
}
|
||||
|
||||
void FlushCachedWrites() noexcept {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
for (auto id : cached_pages) {
|
||||
top_tier[id]->FlushCachedWrites();
|
||||
top_tier[id].load(std::memory_order_relaxed)->FlushCachedWrites();
|
||||
}
|
||||
cached_pages.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CpuMarkIfNotGpuModified(VAddr addr, u64 size) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
const bool gpu = IteratePagesNoLock<false>(
|
||||
addr, size, [](Manager* manager, u64 offset, size_t sz) {
|
||||
return manager->IsRegionModified(Type::GPU, offset, sz);
|
||||
});
|
||||
if (gpu) {
|
||||
return true;
|
||||
}
|
||||
IteratePagesNoLock<true>(addr, size, [](Manager* manager, u64 offset, size_t sz) {
|
||||
manager->ChangeRegionState(Type::CPU, true, manager->cpu_addr + offset, sz);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
|
||||
template <typename Func>
|
||||
void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) {
|
||||
@@ -162,6 +198,12 @@ public:
|
||||
private:
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
bool IteratePages(VAddr cpu_address, size_t size, Func&& func) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
return IteratePagesNoLock<create_region_on_fail>(cpu_address, size, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
bool IteratePagesNoLock(VAddr cpu_address, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, Manager*, u64, size_t>::type;
|
||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
||||
std::size_t remaining_size{size};
|
||||
@@ -170,7 +212,7 @@ private:
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
auto* manager{top_tier[page_index]};
|
||||
auto* manager{top_tier[page_index].load(std::memory_order_relaxed)};
|
||||
if (manager) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(manager, page_offset, copy_amount)) {
|
||||
@@ -181,7 +223,7 @@ private:
|
||||
}
|
||||
} else if constexpr (create_region_on_fail) {
|
||||
CreateRegion(page_index);
|
||||
manager = top_tier[page_index];
|
||||
manager = top_tier[page_index].load(std::memory_order_relaxed);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(manager, page_offset, copy_amount)) {
|
||||
return true;
|
||||
@@ -199,6 +241,7 @@ private:
|
||||
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
std::pair<u64, u64> IteratePairs(VAddr cpu_address, size_t size, Func&& func) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
|
||||
u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
|
||||
@@ -207,7 +250,7 @@ private:
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
auto* manager{top_tier[page_index]};
|
||||
auto* manager{top_tier[page_index].load(std::memory_order_relaxed)};
|
||||
const auto execute = [&] {
|
||||
auto [new_begin, new_end] = func(manager, page_offset, copy_amount);
|
||||
if (new_begin != 0 || new_end != 0) {
|
||||
@@ -220,7 +263,7 @@ private:
|
||||
execute();
|
||||
} else if constexpr (create_region_on_fail) {
|
||||
CreateRegion(page_index);
|
||||
manager = top_tier[page_index];
|
||||
manager = top_tier[page_index].load(std::memory_order_relaxed);
|
||||
execute();
|
||||
}
|
||||
page_index++;
|
||||
@@ -236,7 +279,7 @@ private:
|
||||
|
||||
void CreateRegion(std::size_t page_index) {
|
||||
const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS;
|
||||
top_tier[page_index] = GetNewManager(base_cpu_addr);
|
||||
top_tier[page_index].store(GetNewManager(base_cpu_addr), std::memory_order_release);
|
||||
}
|
||||
|
||||
Manager* GetNewManager(VAddr base_cpu_address) {
|
||||
@@ -254,11 +297,12 @@ private:
|
||||
return new_manager;
|
||||
}
|
||||
|
||||
std::array<Manager*, NUM_HIGH_PAGES> top_tier{};
|
||||
std::array<std::atomic<Manager*>, NUM_HIGH_PAGES> top_tier{};
|
||||
std::deque<std::array<Manager, MANAGER_POOL_SIZE>> manager_pool;
|
||||
std::deque<Manager*> free_managers;
|
||||
ankerl::unordered_dense::set<u32> cached_pages;
|
||||
DeviceTracker* device_tracker = nullptr;
|
||||
std::mutex tracker_mutex;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
@@ -48,6 +49,11 @@ struct WordManager {
|
||||
u64 const last_word = (~u64{0} << shift) >> shift;
|
||||
heap[num_words * size_t(Type::CPU) + num_words - 1] = last_word;
|
||||
heap[num_words * size_t(Type::Untracked) + num_words - 1] = last_word;
|
||||
u32 cpu_pages = 0;
|
||||
for (size_t i = 0; i < num_words; ++i) {
|
||||
cpu_pages += static_cast<u32>(std::popcount(heap[num_words * size_t(Type::CPU) + i]));
|
||||
}
|
||||
cpu_modified_pages.store(cpu_pages, std::memory_order_relaxed);
|
||||
}
|
||||
explicit WordManager() = default;
|
||||
|
||||
@@ -120,10 +126,15 @@ struct WordManager {
|
||||
[[maybe_unused]] std::span<u64> untracked_words = Span(Type::Untracked);
|
||||
[[maybe_unused]] std::span<u64> cached_words = Span(Type::CachedCPU);
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
|
||||
if (type == Type::CPU || type == Type::CachedCPU) {
|
||||
CollectChangedRanges(!enable, index, untracked_words[index], mask, ranges);
|
||||
}
|
||||
if (type == Type::CPU) {
|
||||
const u64 old = state_words[index];
|
||||
cpu_delta += enable ? std::popcount(~old & mask) : -std::popcount(old & mask);
|
||||
}
|
||||
if (enable) {
|
||||
state_words[index] |= mask;
|
||||
if (type == Type::CPU || type == Type::CachedCPU)
|
||||
@@ -138,6 +149,9 @@ struct WordManager {
|
||||
untracked_words[index] &= ~mask;
|
||||
}
|
||||
});
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (!ranges.empty()) {
|
||||
ApplyCollectedRanges(ranges, (!enable) ? 1 : -1);
|
||||
}
|
||||
@@ -165,6 +179,7 @@ struct WordManager {
|
||||
(pending_pointer - pending_offset) * BYTES_PER_PAGE);
|
||||
};
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
IterateWords(offset, size, [&](size_t index, u64 mask) {
|
||||
if (type == Type::GPU)
|
||||
mask &= ~untracked_words[index];
|
||||
@@ -173,6 +188,8 @@ struct WordManager {
|
||||
if (type == Type::CPU || type == Type::CachedCPU) {
|
||||
CollectChangedRanges(true, index, untracked_words[index], mask, ranges);
|
||||
}
|
||||
if (type == Type::CPU)
|
||||
cpu_delta -= std::popcount(word);
|
||||
state_words[index] &= ~mask;
|
||||
if (type == Type::CPU || type == Type::CachedCPU)
|
||||
untracked_words[index] &= ~mask;
|
||||
@@ -194,6 +211,9 @@ struct WordManager {
|
||||
}
|
||||
});
|
||||
});
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (pending) {
|
||||
release();
|
||||
}
|
||||
@@ -248,13 +268,18 @@ struct WordManager {
|
||||
auto const untracked_words = Span(Type::Untracked);
|
||||
auto const cpu_words = Span(Type::CPU);
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
for (u64 word_index = 0; word_index < num_words; ++word_index) {
|
||||
const u64 cached_bits = cached_words[word_index];
|
||||
CollectChangedRanges(false, word_index, untracked_words[word_index], cached_bits, ranges);
|
||||
cpu_delta += std::popcount(~cpu_words[word_index] & cached_bits);
|
||||
untracked_words[word_index] |= cached_bits;
|
||||
cpu_words[word_index] |= cached_bits;
|
||||
cached_words[word_index] = 0;
|
||||
}
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (!ranges.empty()) {
|
||||
ApplyCollectedRanges(ranges, -1);
|
||||
}
|
||||
@@ -319,9 +344,14 @@ struct WordManager {
|
||||
return std::span<const u64>(heap.data() + num_words * size_t(type), num_words);
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 CpuModifiedPageCount() const noexcept {
|
||||
return cpu_modified_pages.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::array<u64, size_t(Type::Max) * num_words> heap = {};
|
||||
DeviceTracker* tracker = nullptr;
|
||||
VAddr cpu_addr = 0;
|
||||
std::atomic<u32> cpu_modified_pages{0};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -91,9 +91,6 @@ public:
|
||||
func();
|
||||
}
|
||||
fences.push(std::move(new_fence));
|
||||
if (should_flush) {
|
||||
rasterizer.FlushCommands();
|
||||
}
|
||||
if constexpr (can_async_check) {
|
||||
guard.unlock();
|
||||
cv.notify_all();
|
||||
@@ -238,10 +235,10 @@ private:
|
||||
|
||||
void PopAsyncFlushes() {
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.PopAsyncFlushes();
|
||||
buffer_cache.PopAsyncFlushes();
|
||||
}
|
||||
buffer_cache.PopAsyncFlushes();
|
||||
query_cache.PopAsyncFlushes();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ set(GLSL_INCLUDES
|
||||
|
||||
set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||
|
||||
@@ -964,35 +964,70 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
|
||||
}
|
||||
|
||||
uint UnquantizeTexelWeight(EncodingData val) {
|
||||
uint encoding = Encoding(val), bitlen = NumBits(val), bitval = BitValue(val);
|
||||
if (encoding == JUST_BITS) {
|
||||
return (bitlen >= 1 && bitlen <= 5)
|
||||
? uint(floor(0.5f + float(bitval) * 64.0f / float((1 << bitlen) - 1)))
|
||||
: FastReplicateTo6(bitval, bitlen);
|
||||
} else if (encoding == TRIT || encoding == QUINT) {
|
||||
uint B = 0, C = 0, D = 0;
|
||||
uint b_mask = (0x3100 >> (bitlen * 4)) & 0xf;
|
||||
uint b = (bitval >> 1) & b_mask;
|
||||
const uint encoding = Encoding(val);
|
||||
const uint bitlen = NumBits(val);
|
||||
const uint bitval = BitValue(val);
|
||||
const uint A = ReplicateBitTo7((bitval & 1));
|
||||
uint B = 0, C = 0, D = 0;
|
||||
uint result = 0;
|
||||
const uint bitlen_0_results[5] = {0, 16, 32, 48, 64};
|
||||
switch (encoding) {
|
||||
case JUST_BITS:
|
||||
return FastReplicateTo6(bitval, bitlen);
|
||||
case TRIT: {
|
||||
D = QuintTritValue(val);
|
||||
if (encoding == TRIT) {
|
||||
switch (bitlen) {
|
||||
case 0: return D * 32; //0,32,64
|
||||
case 1: C = 50; break;
|
||||
case 2: C = 23; B = (b << 6) | (b << 2) | b; break;
|
||||
case 3: C = 11; B = (b << 5) | b; break;
|
||||
}
|
||||
} else if (encoding == QUINT) {
|
||||
switch (bitlen) {
|
||||
case 0: return D * 16; //0, 16, 32, 48, 64
|
||||
case 1: C = 28; break;
|
||||
case 2: C = 13; B = (b << 6) | (b << 1); break;
|
||||
}
|
||||
switch (bitlen) {
|
||||
case 0:
|
||||
return bitlen_0_results[D * 2];
|
||||
case 1: {
|
||||
C = 50;
|
||||
break;
|
||||
}
|
||||
uint A = ReplicateBitTo7(bitval & 1);
|
||||
uint res = (A & 0x20) | (((D * C + B) ^ A) >> 2);
|
||||
return res + (res > 32 ? 1 : 0);
|
||||
case 2: {
|
||||
C = 23;
|
||||
const uint b = (bitval >> 1) & 1;
|
||||
B = (b << 6) | (b << 2) | b;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
C = 11;
|
||||
const uint cb = (bitval >> 1) & 3;
|
||||
B = (cb << 5) | cb;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case QUINT: {
|
||||
D = QuintTritValue(val);
|
||||
switch (bitlen) {
|
||||
case 0:
|
||||
return bitlen_0_results[D];
|
||||
case 1: {
|
||||
C = 28;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
C = 13;
|
||||
const uint b = (bitval >> 1) & 1;
|
||||
B = (b << 6) | (b << 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (encoding != JUST_BITS && bitlen > 0) {
|
||||
result = D * C + B;
|
||||
result ^= A;
|
||||
result = (A & 0x20) | (result >> 2);
|
||||
}
|
||||
if (result > 32) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void UnquantizeTexelWeights(uvec2 size, bool is_dual_plane) {
|
||||
@@ -1394,11 +1429,10 @@ void DecompressBlock(ivec3 coord) {
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
return ((pos.x & 32u) << 3u) |
|
||||
((pos.y & 6u) << 5u) |
|
||||
((pos.x & 16u) << 1u) |
|
||||
((pos.y & 1u) << 4u) |
|
||||
(pos.x & 15u);
|
||||
const uint x = pos.x;
|
||||
const uint y = pos.y;
|
||||
return ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 +
|
||||
((x % 32) / 16) * 32 + (y % 2) * 16 + (x % 16);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450
|
||||
|
||||
#ifdef VULKAN
|
||||
#define VERTEX_ID gl_VertexIndex
|
||||
#else
|
||||
#define VERTEX_ID gl_VertexID
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
float x = float((VERTEX_ID & 1) << 2);
|
||||
float y = float((VERTEX_ID & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
|
||||
}
|
||||
@@ -58,8 +58,9 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, GPU
|
||||
|
||||
MemoryManager::~MemoryManager() = default;
|
||||
|
||||
MemoryManager::EntryType MemoryManager::GetEntry(size_t position, bool is_big_page) const {
|
||||
if (is_big_page) {
|
||||
template <bool is_big_page>
|
||||
MemoryManager::EntryType MemoryManager::GetEntry(size_t position) const {
|
||||
if constexpr (is_big_page) {
|
||||
position = position >> big_page_bits;
|
||||
const u64 entry_mask = big_entries[position / 32];
|
||||
const size_t sub_index = position % 32;
|
||||
@@ -72,8 +73,9 @@ MemoryManager::EntryType MemoryManager::GetEntry(size_t position, bool is_big_pa
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry, bool is_big_page) {
|
||||
if (is_big_page) {
|
||||
template <bool is_big_page>
|
||||
void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
|
||||
if constexpr (is_big_page) {
|
||||
position = position >> big_page_bits;
|
||||
const u64 entry_mask = big_entries[position / 32];
|
||||
const size_t sub_index = position % 32;
|
||||
@@ -106,21 +108,23 @@ inline void MemoryManager::SetBigPageContinuous(size_t big_page_index, bool valu
|
||||
(~(1ULL << sub_index) & continuous_mask) | (value ? 1ULL << sub_index : 0);
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, MemoryManager::EntryType entry_type) {
|
||||
template <MemoryManager::EntryType entry_type>
|
||||
GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
|
||||
PTEKind kind) {
|
||||
[[maybe_unused]] u64 remaining_size{size};
|
||||
if (entry_type == EntryType::Mapped) {
|
||||
if constexpr (entry_type == EntryType::Mapped) {
|
||||
page_table.ReserveRange(gpu_addr, size);
|
||||
}
|
||||
for (u64 offset{}; offset < size; offset += page_size) {
|
||||
const GPUVAddr current_gpu_addr = gpu_addr + offset;
|
||||
[[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr, false);
|
||||
SetEntry(current_gpu_addr, entry_type, false);
|
||||
[[maybe_unused]] const auto current_entry_type = GetEntry<false>(current_gpu_addr);
|
||||
SetEntry<false>(current_gpu_addr, entry_type);
|
||||
if (current_entry_type != entry_type) {
|
||||
rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, page_size);
|
||||
}
|
||||
if (entry_type == EntryType::Mapped) {
|
||||
if constexpr (entry_type == EntryType::Mapped) {
|
||||
const DAddr current_dev_addr = dev_addr + offset;
|
||||
const auto index = PageEntryIndex(current_gpu_addr, false);
|
||||
const auto index = PageEntryIndex<false>(current_gpu_addr);
|
||||
const u32 sub_value = static_cast<u32>(current_dev_addr >> cpu_page_bits);
|
||||
page_table[index] = sub_value;
|
||||
}
|
||||
@@ -130,18 +134,20 @@ GPUVAddr MemoryManager::PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr de
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, MemoryManager::EntryType entry_type) {
|
||||
template <MemoryManager::EntryType entry_type>
|
||||
GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr,
|
||||
size_t size, PTEKind kind) {
|
||||
[[maybe_unused]] u64 remaining_size{size};
|
||||
for (u64 offset{}; offset < size; offset += big_page_size) {
|
||||
const GPUVAddr current_gpu_addr = gpu_addr + offset;
|
||||
[[maybe_unused]] const auto current_entry_type = GetEntry(current_gpu_addr, true);
|
||||
SetEntry(current_gpu_addr, entry_type, true);
|
||||
[[maybe_unused]] const auto current_entry_type = GetEntry<true>(current_gpu_addr);
|
||||
SetEntry<true>(current_gpu_addr, entry_type);
|
||||
if (current_entry_type != entry_type) {
|
||||
rasterizer->ModifyGPUMemory(unique_identifier, current_gpu_addr, big_page_size);
|
||||
}
|
||||
if (entry_type == EntryType::Mapped) {
|
||||
if constexpr (entry_type == EntryType::Mapped) {
|
||||
const DAddr current_dev_addr = dev_addr + offset;
|
||||
const auto index = PageEntryIndex(current_gpu_addr, true);
|
||||
const auto index = PageEntryIndex<true>(current_gpu_addr);
|
||||
const u32 sub_value = static_cast<u32>(current_dev_addr >> cpu_page_bits);
|
||||
big_page_table_dev[index] = sub_value;
|
||||
const bool is_continuous = ([&] {
|
||||
@@ -175,16 +181,19 @@ void MemoryManager::BindRasterizer(VideoCore::RasterizerInterface* rasterizer_)
|
||||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind, bool is_big_pages) {
|
||||
if (is_big_pages)
|
||||
return BigPageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
|
||||
return PageTableOp(gpu_addr, dev_addr, size, kind, EntryType::Mapped);
|
||||
GPUVAddr MemoryManager::Map(GPUVAddr gpu_addr, DAddr dev_addr, std::size_t size, PTEKind kind,
|
||||
bool is_big_pages) {
|
||||
if (is_big_pages) [[likely]] {
|
||||
return BigPageTableOp<EntryType::Mapped>(gpu_addr, dev_addr, size, kind);
|
||||
}
|
||||
return PageTableOp<EntryType::Mapped>(gpu_addr, dev_addr, size, kind);
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::MapSparse(GPUVAddr gpu_addr, std::size_t size, bool is_big_pages) {
|
||||
if (is_big_pages)
|
||||
return BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
|
||||
return PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Reserved);
|
||||
if (is_big_pages) [[likely]] {
|
||||
return BigPageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
|
||||
}
|
||||
return PageTableOp<EntryType::Reserved>(gpu_addr, 0, size, PTEKind::INVALID);
|
||||
}
|
||||
|
||||
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
|
||||
@@ -198,21 +207,26 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
|
||||
}
|
||||
page_stash.clear();
|
||||
|
||||
BigPageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Free);
|
||||
PageTableOp(gpu_addr, 0, size, PTEKind::INVALID, EntryType::Free);
|
||||
BigPageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
|
||||
PageTableOp<EntryType::Free>(gpu_addr, 0, size, PTEKind::INVALID);
|
||||
}
|
||||
|
||||
std::optional<DAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
|
||||
if (!IsWithinGPUAddressRange(gpu_addr)) [[unlikely]] {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (GetEntry(gpu_addr, true) != EntryType::Mapped) [[unlikely]] {
|
||||
if (GetEntry(gpu_addr, false) != EntryType::Mapped)
|
||||
if (GetEntry<true>(gpu_addr) != EntryType::Mapped) [[unlikely]] {
|
||||
if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
|
||||
return std::nullopt;
|
||||
const DAddr dev_addr_base = DAddr(page_table[PageEntryIndex(gpu_addr, false)]) << cpu_page_bits;
|
||||
}
|
||||
|
||||
const DAddr dev_addr_base = static_cast<DAddr>(page_table[PageEntryIndex<false>(gpu_addr)])
|
||||
<< cpu_page_bits;
|
||||
return dev_addr_base + (gpu_addr & page_mask);
|
||||
}
|
||||
const DAddr dev_addr_base = DAddr(big_page_table_dev[PageEntryIndex(gpu_addr, true)]) << cpu_page_bits;
|
||||
|
||||
const DAddr dev_addr_base =
|
||||
static_cast<DAddr>(big_page_table_dev[PageEntryIndex<true>(gpu_addr)]) << cpu_page_bits;
|
||||
return dev_addr_base + (gpu_addr & big_page_mask);
|
||||
}
|
||||
|
||||
@@ -285,8 +299,10 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const {
|
||||
#pragma inline_recursion(on)
|
||||
#endif
|
||||
|
||||
template <typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
|
||||
inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, bool is_big_page, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const {
|
||||
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
|
||||
inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size,
|
||||
FuncMapped&& func_mapped, FuncReserved&& func_reserved,
|
||||
FuncUnmapped&& func_unmapped) const {
|
||||
using FuncMappedReturn =
|
||||
typename std::invoke_result<FuncMapped, std::size_t, std::size_t, std::size_t>::type;
|
||||
using FuncReservedReturn =
|
||||
@@ -299,7 +315,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
|
||||
u64 used_page_size;
|
||||
u64 used_page_mask;
|
||||
u64 used_page_bits;
|
||||
if (is_big_page) {
|
||||
if constexpr (is_big_pages) {
|
||||
used_page_size = big_page_size;
|
||||
used_page_mask = big_page_mask;
|
||||
used_page_bits = big_page_bits;
|
||||
@@ -316,7 +332,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
(std::min)(static_cast<std::size_t>(used_page_size) - page_offset, remaining_size)};
|
||||
auto entry = GetEntry(current_address, is_big_page);
|
||||
auto entry = GetEntry<is_big_pages>(current_address);
|
||||
if (entry == EntryType::Mapped) [[likely]] {
|
||||
if constexpr (BOOL_BREAK_MAPPED) {
|
||||
if (func_mapped(page_index, page_offset, copy_amount)) {
|
||||
@@ -351,14 +367,18 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, [[maybe_unused]] VideoCommon::CacheType which, bool unsafe) const {
|
||||
auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
|
||||
template <bool is_safe>
|
||||
void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
[[maybe_unused]] VideoCommon::CacheType which) const {
|
||||
auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
|
||||
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
|
||||
std::memset(dest_buffer, 0, copy_amount);
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
};
|
||||
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
if (!unsafe) {
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
|
||||
}
|
||||
u8* physical = memory.GetPointer<u8>(dev_addr_base);
|
||||
@@ -366,8 +386,9 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
};
|
||||
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
if (!unsafe) {
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
|
||||
}
|
||||
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
|
||||
@@ -378,28 +399,35 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
|
||||
}
|
||||
dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
|
||||
};
|
||||
auto read_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
auto read_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, mapped_normal, set_to_zero, set_to_zero);
|
||||
MemoryOperation<false>(base, copy_amount, mapped_normal, set_to_zero, set_to_zero);
|
||||
};
|
||||
MemoryOperation(gpu_src_addr, size, true, mapped_big, set_to_zero, read_short_pages);
|
||||
MemoryOperation<true>(gpu_src_addr, size, mapped_big, set_to_zero, read_short_pages);
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which) const {
|
||||
ReadBlockImpl(gpu_src_addr, dest_buffer, size, which, false);
|
||||
void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which) const {
|
||||
ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, const std::size_t size) const {
|
||||
ReadBlockImpl(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None, true);
|
||||
void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
|
||||
const std::size_t size) const {
|
||||
ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, [[maybe_unused]] VideoCommon::CacheType which, bool unsafe) {
|
||||
auto just_advance = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
|
||||
template <bool is_safe>
|
||||
void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
[[maybe_unused]] VideoCommon::CacheType which) {
|
||||
auto just_advance = [&]([[maybe_unused]] std::size_t page_index,
|
||||
[[maybe_unused]] std::size_t offset, std::size_t copy_amount) {
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
};
|
||||
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
if (!unsafe) {
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
|
||||
}
|
||||
u8* physical = memory.GetPointer<u8>(dev_addr_base);
|
||||
@@ -407,8 +435,9 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
};
|
||||
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
if (!unsafe) {
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
if constexpr (is_safe) {
|
||||
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
|
||||
}
|
||||
if (!IsBigPageContinuous(page_index)) [[unlikely]] {
|
||||
@@ -419,23 +448,26 @@ void MemoryManager::WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffe
|
||||
}
|
||||
src_buffer = static_cast<const u8*>(src_buffer) + copy_amount;
|
||||
};
|
||||
auto write_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
auto write_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, mapped_normal, just_advance, just_advance);
|
||||
MemoryOperation<false>(base, copy_amount, mapped_normal, just_advance, just_advance);
|
||||
};
|
||||
MemoryOperation(gpu_dest_addr, size, true, mapped_big, just_advance, write_short_pages);
|
||||
MemoryOperation<true>(gpu_dest_addr, size, mapped_big, just_advance, write_short_pages);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which) {
|
||||
WriteBlockImpl(gpu_dest_addr, src_buffer, size, which, false);
|
||||
void MemoryManager::WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which) {
|
||||
WriteBlockImpl<true>(gpu_dest_addr, src_buffer, size, which);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
|
||||
WriteBlockImpl(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None, true);
|
||||
void MemoryManager::WriteBlockUnsafe(GPUVAddr gpu_dest_addr, const void* src_buffer,
|
||||
std::size_t size) {
|
||||
WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
|
||||
}
|
||||
|
||||
void MemoryManager::WriteBlockCached(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size) {
|
||||
WriteBlockImpl(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None, true);
|
||||
WriteBlockImpl<false>(gpu_dest_addr, src_buffer, size, VideoCommon::CacheType::None);
|
||||
accumulator.Add(gpu_dest_addr, size);
|
||||
}
|
||||
|
||||
@@ -446,18 +478,21 @@ void MemoryManager::FlushRegion(GPUVAddr gpu_addr, size_t size,
|
||||
[[maybe_unused]] std::size_t copy_amount) {};
|
||||
|
||||
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
|
||||
};
|
||||
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
rasterizer->FlushRegion(dev_addr_base, copy_amount, which);
|
||||
};
|
||||
auto flush_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
auto flush_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
|
||||
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, flush_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, flush_short_pages);
|
||||
}
|
||||
|
||||
bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
|
||||
@@ -482,10 +517,10 @@ bool MemoryManager::IsMemoryDirty(GPUVAddr gpu_addr, size_t size,
|
||||
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
|
||||
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
|
||||
return result;
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, check_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, check_short_pages);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -522,10 +557,10 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
|
||||
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, short_check, fail, fail);
|
||||
MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
|
||||
return result;
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, big_check, fail, check_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
|
||||
return range_so_far;
|
||||
}
|
||||
|
||||
@@ -541,18 +576,21 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size,
|
||||
[[maybe_unused]] std::size_t copy_amount) {};
|
||||
|
||||
auto mapped_normal = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(page_table[page_index]) << cpu_page_bits) + offset;
|
||||
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
|
||||
};
|
||||
auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
const DAddr dev_addr_base = (DAddr(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
const DAddr dev_addr_base =
|
||||
(static_cast<DAddr>(big_page_table_dev[page_index]) << cpu_page_bits) + offset;
|
||||
rasterizer->InvalidateRegion(dev_addr_base, copy_amount, which);
|
||||
};
|
||||
auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
auto invalidate_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, mapped_normal, do_nothing, do_nothing);
|
||||
MemoryOperation<false>(base, copy_amount, mapped_normal, do_nothing, do_nothing);
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, mapped_big, do_nothing, invalidate_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, mapped_big, do_nothing, invalidate_short_pages);
|
||||
}
|
||||
|
||||
void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
|
||||
@@ -564,7 +602,7 @@ void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std
|
||||
}
|
||||
|
||||
bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
if (GetEntry(gpu_addr, true) == EntryType::Mapped) [[likely]] {
|
||||
if (GetEntry<true>(gpu_addr) == EntryType::Mapped) [[likely]] {
|
||||
size_t page_index = gpu_addr >> big_page_bits;
|
||||
if (IsBigPageContinuous(page_index)) [[likely]] {
|
||||
const std::size_t page{(page_index & big_page_mask) + size};
|
||||
@@ -573,7 +611,7 @@ bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
const std::size_t page{(gpu_addr & Core::DEVICE_PAGEMASK) + size};
|
||||
return page <= Core::DEVICE_PAGESIZE;
|
||||
}
|
||||
if (GetEntry(gpu_addr, false) != EntryType::Mapped) {
|
||||
if (GetEntry<false>(gpu_addr) != EntryType::Mapped) {
|
||||
return false;
|
||||
}
|
||||
const std::size_t page{(gpu_addr & Core::DEVICE_PAGEMASK) + size};
|
||||
@@ -611,10 +649,10 @@ bool MemoryManager::IsContinuousRange(GPUVAddr gpu_addr, std::size_t size) const
|
||||
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, short_check, fail, fail);
|
||||
MemoryOperation<false>(base, copy_amount, short_check, fail, fail);
|
||||
return !result;
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, big_check, fail, check_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, big_check, fail, check_short_pages);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -627,12 +665,13 @@ bool MemoryManager::IsFullyMappedRange(GPUVAddr gpu_addr, std::size_t size) cons
|
||||
};
|
||||
auto pass = [&]([[maybe_unused]] std::size_t page_index, [[maybe_unused]] std::size_t offset,
|
||||
[[maybe_unused]] std::size_t copy_amount) { return false; };
|
||||
auto check_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
auto check_short_pages = [&](std::size_t page_index, std::size_t offset,
|
||||
std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, pass, pass, fail);
|
||||
MemoryOperation<false>(base, copy_amount, pass, pass, fail);
|
||||
return !result;
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, pass, fail, check_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, pass, fail, check_short_pages);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -644,9 +683,13 @@ MemoryManager::GetSubmappedRange(GPUVAddr gpu_addr, std::size_t size) const {
|
||||
}
|
||||
|
||||
template <bool is_gpu_address>
|
||||
void MemoryManager::GetSubmappedRangeImpl(GPUVAddr gpu_addr, std::size_t size, boost::container::small_vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result)
|
||||
void MemoryManager::GetSubmappedRangeImpl(
|
||||
GPUVAddr gpu_addr, std::size_t size,
|
||||
boost::container::small_vector<
|
||||
std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result)
|
||||
const {
|
||||
std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>> last_segment{};
|
||||
std::optional<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>>
|
||||
last_segment{};
|
||||
std::optional<DAddr> old_page_addr{};
|
||||
const auto split = [&last_segment, &result]([[maybe_unused]] std::size_t page_index,
|
||||
[[maybe_unused]] std::size_t offset,
|
||||
@@ -702,9 +745,9 @@ void MemoryManager::GetSubmappedRangeImpl(GPUVAddr gpu_addr, std::size_t size, b
|
||||
};
|
||||
auto do_short_pages = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
|
||||
GPUVAddr base = (page_index << big_page_bits) + offset;
|
||||
MemoryOperation(base, copy_amount, false, extend_size_short, split, split);
|
||||
MemoryOperation<false>(base, copy_amount, extend_size_short, split, split);
|
||||
};
|
||||
MemoryOperation(gpu_addr, size, true, extend_size_big, split, do_short_pages);
|
||||
MemoryOperation<true>(gpu_addr, size, extend_size_big, split, do_short_pages);
|
||||
split(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
static constexpr bool HAS_FLUSH_INVALIDATION = true;
|
||||
|
||||
inline size_t GetID() const noexcept {
|
||||
size_t GetID() const {
|
||||
return unique_identifier;
|
||||
}
|
||||
|
||||
@@ -66,15 +66,16 @@ public:
|
||||
[[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] inline T* GetPointer(GPUVAddr addr) noexcept {
|
||||
const auto address = GpuToCpuAddress(addr);
|
||||
if (!address)
|
||||
[[nodiscard]] T* GetPointer(GPUVAddr addr) {
|
||||
const auto address{GpuToCpuAddress(addr)};
|
||||
if (!address) {
|
||||
return {};
|
||||
}
|
||||
return memory.GetPointer<T>(*address);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] inline const T* GetPointer(GPUVAddr addr) const noexcept {
|
||||
[[nodiscard]] const T* GetPointer(GPUVAddr addr) const {
|
||||
return GetPointer<T*>(addr);
|
||||
}
|
||||
|
||||
@@ -84,9 +85,12 @@ public:
|
||||
* in the Host Memory counterpart. Note: This functions cause Host GPU Memory
|
||||
* Flushes and Invalidations, respectively to each operation.
|
||||
*/
|
||||
void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
|
||||
void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All);
|
||||
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, VideoCommon::CacheType which = VideoCommon::CacheType::All);
|
||||
void ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which = VideoCommon::CacheType::All) const;
|
||||
void WriteBlock(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which = VideoCommon::CacheType::All);
|
||||
void CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size,
|
||||
VideoCommon::CacheType which = VideoCommon::CacheType::All);
|
||||
|
||||
/**
|
||||
* ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and
|
||||
@@ -156,14 +160,21 @@ public:
|
||||
u8* GetSpan(const GPUVAddr src_addr, const std::size_t size);
|
||||
|
||||
private:
|
||||
template <typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
|
||||
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, bool is_big_page, FuncMapped&& func_mapped, FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
|
||||
template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped>
|
||||
inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
|
||||
FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
|
||||
|
||||
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, VideoCommon::CacheType which, bool unsafe) const;
|
||||
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size, VideoCommon::CacheType which, bool unsafe);
|
||||
template <bool is_safe>
|
||||
void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which) const;
|
||||
|
||||
[[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr, bool is_big_page) const {
|
||||
if (is_big_page) {
|
||||
template <bool is_safe>
|
||||
void WriteBlockImpl(GPUVAddr gpu_dest_addr, const void* src_buffer, std::size_t size,
|
||||
VideoCommon::CacheType which);
|
||||
|
||||
template <bool is_big_page>
|
||||
[[nodiscard]] std::size_t PageEntryIndex(GPUVAddr gpu_addr) const {
|
||||
if constexpr (is_big_page) {
|
||||
return (gpu_addr >> big_page_bits) & big_page_table_mask;
|
||||
} else {
|
||||
return (gpu_addr >> page_bits) & page_table_mask;
|
||||
@@ -176,7 +187,9 @@ private:
|
||||
template <bool is_gpu_address>
|
||||
void GetSubmappedRangeImpl(
|
||||
GPUVAddr gpu_addr, std::size_t size,
|
||||
boost::container::small_vector<std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>& result) const;
|
||||
boost::container::small_vector<
|
||||
std::pair<std::conditional_t<is_gpu_address, GPUVAddr, DAddr>, std::size_t>, 32>&
|
||||
result) const;
|
||||
|
||||
Core::System& system;
|
||||
MaxwellDeviceMemoryManager& memory;
|
||||
@@ -206,11 +219,19 @@ private:
|
||||
std::vector<u64> entries;
|
||||
std::vector<u64> big_entries;
|
||||
|
||||
GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, EntryType entry_type);
|
||||
GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size, PTEKind kind, EntryType entry_type);
|
||||
template <EntryType entry_type>
|
||||
GPUVAddr PageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
|
||||
PTEKind kind);
|
||||
|
||||
inline EntryType GetEntry(size_t position, bool is_big_page) const;
|
||||
inline void SetEntry(size_t position, EntryType entry, bool is_big_page);
|
||||
template <EntryType entry_type>
|
||||
GPUVAddr BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] DAddr dev_addr, size_t size,
|
||||
PTEKind kind);
|
||||
|
||||
template <bool is_big_page>
|
||||
inline EntryType GetEntry(size_t position) const;
|
||||
|
||||
template <bool is_big_page>
|
||||
inline void SetEntry(size_t position, EntryType entry);
|
||||
|
||||
Common::MultiLevelPageTable<u32> page_table;
|
||||
Common::RangeMap<GPUVAddr, PTEKind> kind_map;
|
||||
|
||||
@@ -485,7 +485,6 @@ void RasterizerOpenGL::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
|
||||
texture_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::BufferCache))) {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::QueryCache))) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_command_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -14,32 +18,52 @@ constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
|
||||
struct CommandPool::Pool {
|
||||
vk::CommandPool handle;
|
||||
vk::CommandBuffers cmdbufs;
|
||||
u64 tick;
|
||||
};
|
||||
|
||||
CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_)
|
||||
: ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {}
|
||||
: master_semaphore{master_semaphore_}, device{device_} {}
|
||||
|
||||
CommandPool::~CommandPool() = default;
|
||||
|
||||
void CommandPool::Allocate(size_t begin, size_t end) {
|
||||
// Command buffers are going to be committed, recorded, executed every single usage cycle.
|
||||
// They are also going to be reset when committed.
|
||||
void CommandPool::AllocatePool() {
|
||||
Pool& pool = pools.emplace_back();
|
||||
pool.handle = device.GetLogical().CreateCommandPool({
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags =
|
||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||
.queueFamilyIndex = device.GetGraphicsFamily(),
|
||||
});
|
||||
pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
|
||||
pool.tick = 0;
|
||||
}
|
||||
|
||||
void CommandPool::AcquirePool() {
|
||||
if (!pools.empty()) {
|
||||
master_semaphore.Refresh();
|
||||
const u64 gpu_tick = master_semaphore.KnownGpuTick();
|
||||
for (size_t i = 0; i < pools.size(); ++i) {
|
||||
const size_t candidate = (current_pool + 1 + i) % pools.size();
|
||||
if (gpu_tick >= pools[candidate].tick) {
|
||||
current_pool = candidate;
|
||||
current_index = 0;
|
||||
pools[current_pool].handle.Reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
AllocatePool();
|
||||
current_pool = pools.size() - 1;
|
||||
current_index = 0;
|
||||
}
|
||||
|
||||
VkCommandBuffer CommandPool::Commit() {
|
||||
const size_t index = CommitResource();
|
||||
const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
|
||||
const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
|
||||
return pools[pool_index].cmdbufs[sub_index];
|
||||
if (pools.empty() || current_index >= COMMAND_BUFFER_POOL_SIZE) {
|
||||
AcquirePool();
|
||||
}
|
||||
Pool& pool = pools[current_pool];
|
||||
pool.tick = master_semaphore.CurrentTick();
|
||||
return pool.cmdbufs[current_index++];
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,7 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -14,20 +17,24 @@ namespace Vulkan {
|
||||
class Device;
|
||||
class MasterSemaphore;
|
||||
|
||||
class CommandPool final : public ResourcePool {
|
||||
class CommandPool final {
|
||||
public:
|
||||
explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_);
|
||||
~CommandPool() override;
|
||||
|
||||
void Allocate(size_t begin, size_t end) override;
|
||||
~CommandPool();
|
||||
|
||||
VkCommandBuffer Commit();
|
||||
|
||||
private:
|
||||
struct Pool;
|
||||
|
||||
void AllocatePool();
|
||||
void AcquirePool();
|
||||
|
||||
MasterSemaphore& master_semaphore;
|
||||
const Device& device;
|
||||
std::vector<Pool> pools;
|
||||
size_t current_pool = 0;
|
||||
size_t current_index = 0;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
@@ -17,6 +18,8 @@
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/host_shaders/astc_decoder_comp_spv.h"
|
||||
#include "video_core/host_shaders/astc_decoder_frag_spv.h"
|
||||
#include "video_core/host_shaders/astc_decoder_vert_spv.h"
|
||||
#include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h"
|
||||
#include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h"
|
||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||
@@ -25,8 +28,11 @@
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/texture_cache/accelerated_swizzle.h"
|
||||
@@ -613,7 +619,394 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
|
||||
});
|
||||
scheduler.Finish();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AstcFragPushConstants {
|
||||
std::array<u32, 2> blocks_dims;
|
||||
u32 layer_stride;
|
||||
u32 block_size;
|
||||
u32 x_shift;
|
||||
u32 block_height;
|
||||
u32 block_height_mask;
|
||||
u32 dest_layer;
|
||||
};
|
||||
|
||||
constexpr VkDescriptorSetLayoutBinding ASTC_FRAG_BINDING{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkDescriptorUpdateTemplateEntry ASTC_FRAG_TEMPLATE{
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = 0,
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
};
|
||||
|
||||
constexpr DescriptorBankInfo ASTC_FRAG_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 1,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 1,
|
||||
};
|
||||
|
||||
constexpr VkPushConstantRange ASTC_FRAG_PUSH_CONSTANT_RANGE{
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = static_cast<u32>(sizeof(AstcFragPushConstants)),
|
||||
};
|
||||
|
||||
constexpr VkPipelineVertexInputStateCreateInfo ASTC_FRAG_VERTEX_INPUT{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.vertexBindingDescriptionCount = 0,
|
||||
.pVertexBindingDescriptions = nullptr,
|
||||
.vertexAttributeDescriptionCount = 0,
|
||||
.pVertexAttributeDescriptions = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkPipelineInputAssemblyStateCreateInfo ASTC_FRAG_INPUT_ASSEMBLY{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
constexpr VkPipelineViewportStateCreateInfo ASTC_FRAG_VIEWPORT{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = 1,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = 1,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkPipelineRasterizationStateCreateInfo ASTC_FRAG_RASTERIZATION{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
constexpr VkPipelineMultisampleStateCreateInfo ASTC_FRAG_MULTISAMPLE{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.minSampleShading = 0.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
constexpr VkPipelineColorBlendAttachmentState ASTC_FRAG_BLEND_ATTACHMENT{
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
constexpr std::array ASTC_FRAG_DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR};
|
||||
} // Anonymous namespace
|
||||
|
||||
ASTCDecoderFragmentPass::ASTCDecoderFragmentPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_,
|
||||
RenderPassCache& render_pass_cache_)
|
||||
: device{device_}, scheduler{scheduler_},
|
||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_},
|
||||
render_pass_cache{render_pass_cache_},
|
||||
descriptor_set_layout{device.GetLogical().CreateDescriptorSetLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &ASTC_FRAG_BINDING,
|
||||
})},
|
||||
descriptor_template{device.GetLogical().CreateDescriptorUpdateTemplate({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.descriptorUpdateEntryCount = 1,
|
||||
.pDescriptorUpdateEntries = &ASTC_FRAG_TEMPLATE,
|
||||
.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET,
|
||||
.descriptorSetLayout = *descriptor_set_layout,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.pipelineLayout = VK_NULL_HANDLE,
|
||||
.set = 0,
|
||||
})},
|
||||
pipeline_layout{device.GetLogical().CreatePipelineLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &ASTC_FRAG_PUSH_CONSTANT_RANGE,
|
||||
})},
|
||||
descriptor_allocator{descriptor_pool_.Allocator(device_, scheduler_, *descriptor_set_layout,
|
||||
ASTC_FRAG_BANK_INFO)},
|
||||
vertex_shader{BuildShader(device, ASTC_DECODER_VERT_SPV)},
|
||||
fragment_shader{BuildShader(device, ASTC_DECODER_FRAG_SPV)} {}
|
||||
|
||||
ASTCDecoderFragmentPass::~ASTCDecoderFragmentPass() = default;
|
||||
|
||||
VkPipeline ASTCDecoderFragmentPass::FindOrEmplacePipeline(VkRenderPass render_pass) {
|
||||
const auto it = std::ranges::find(pipeline_keys, render_pass);
|
||||
if (it != pipeline_keys.end()) {
|
||||
return *pipelines[std::distance(pipeline_keys.begin(), it)];
|
||||
}
|
||||
pipeline_keys.push_back(render_pass);
|
||||
const std::array stages{
|
||||
VkPipelineShaderStageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = *vertex_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
VkPipelineShaderStageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = *fragment_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
};
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_CLEAR,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &ASTC_FRAG_BLEND_ATTACHMENT,
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
const VkPipelineDynamicStateCreateInfo dynamic_state{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.dynamicStateCount = static_cast<u32>(ASTC_FRAG_DYNAMIC_STATES.size()),
|
||||
.pDynamicStates = ASTC_FRAG_DYNAMIC_STATES.data(),
|
||||
};
|
||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &ASTC_FRAG_VERTEX_INPUT,
|
||||
.pInputAssemblyState = &ASTC_FRAG_INPUT_ASSEMBLY,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &ASTC_FRAG_VIEWPORT,
|
||||
.pRasterizationState = &ASTC_FRAG_RASTERIZATION,
|
||||
.pMultisampleState = &ASTC_FRAG_MULTISAMPLE,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &color_blend,
|
||||
.pDynamicState = &dynamic_state,
|
||||
.layout = *pipeline_layout,
|
||||
.renderPass = render_pass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void ASTCDecoderFragmentPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||
VideoCore::Surface::PixelFormat decoded_format) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
while (!frame_resources.empty() && scheduler.IsFree(frame_resources.front().tick)) {
|
||||
frame_resources.pop_front();
|
||||
}
|
||||
const std::array<u32, 2> block_dims{
|
||||
VideoCore::Surface::DefaultBlockWidth(image.info.format),
|
||||
VideoCore::Surface::DefaultBlockHeight(image.info.format),
|
||||
};
|
||||
RenderPassKey key{};
|
||||
key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||
key.color_formats[0] = decoded_format;
|
||||
key.depth_format = VideoCore::Surface::PixelFormat::Invalid;
|
||||
key.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
const VkRenderPass render_pass = render_pass_cache.Get(key);
|
||||
const VkPipeline pipeline = FindOrEmplacePipeline(render_pass);
|
||||
const VkFormat vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, decoded_format).format;
|
||||
const VkImage vk_image = image.Handle();
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
scheduler.Record([vk_image, aspect_mask, is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = static_cast<VkAccessFlags>(
|
||||
is_initialized ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_NONE),
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, barrier);
|
||||
});
|
||||
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
const u32 level = swizzle.level;
|
||||
const u32 width = std::max(1u, image.info.size.width >> level);
|
||||
const u32 height = std::max(1u, image.info.size.height >> level);
|
||||
const u32 layers = image.info.resources.layers;
|
||||
for (u32 layer = 0; layer < layers; ++layer) {
|
||||
vk::ImageView view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = vk_image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = vk_format,
|
||||
.components{},
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
});
|
||||
vk::Framebuffer framebuffer =
|
||||
device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = render_pass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = view.address(),
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = 1,
|
||||
});
|
||||
const AstcFragPushConstants pc{
|
||||
.blocks_dims = block_dims,
|
||||
.layer_stride = params.layer_stride,
|
||||
.block_size = params.block_size,
|
||||
.x_shift = params.x_shift,
|
||||
.block_height = params.block_height,
|
||||
.block_height_mask = params.block_height_mask,
|
||||
.dest_layer = layer,
|
||||
};
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 1);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
scheduler.Record([this, pipeline, render_pass, descriptor_data, pc, width, height,
|
||||
fb = *framebuffer](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
const VkRenderPassBeginInfo begin{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = render_pass,
|
||||
.framebuffer = fb,
|
||||
.renderArea{.offset = {0, 0}, .extent = {width, height}},
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
const VkViewport viewport{
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(width),
|
||||
.height = static_cast<float>(height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
const VkRect2D scissor{.offset = {0, 0}, .extent = {width, height}};
|
||||
cmdbuf.BeginRenderPass(begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, set,
|
||||
{});
|
||||
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, pc);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
frame_resources.push_back(FrameResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.view = std::move(view),
|
||||
.framebuffer = std::move(framebuffer),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
@@ -137,6 +140,44 @@ private:
|
||||
MemoryAllocator& memory_allocator;
|
||||
};
|
||||
|
||||
class RenderPassCache;
|
||||
|
||||
class ASTCDecoderFragmentPass final {
|
||||
public:
|
||||
explicit ASTCDecoderFragmentPass(const Device& device_, Scheduler& scheduler_,
|
||||
DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_,
|
||||
RenderPassCache& render_pass_cache_);
|
||||
~ASTCDecoderFragmentPass();
|
||||
|
||||
void Assemble(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||
VideoCore::Surface::PixelFormat decoded_format);
|
||||
|
||||
private:
|
||||
VkPipeline FindOrEmplacePipeline(VkRenderPass render_pass);
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
RenderPassCache& render_pass_cache;
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
vk::DescriptorUpdateTemplate descriptor_template;
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
DescriptorAllocator descriptor_allocator;
|
||||
vk::ShaderModule vertex_shader;
|
||||
vk::ShaderModule fragment_shader;
|
||||
std::vector<VkRenderPass> pipeline_keys;
|
||||
std::vector<vk::Pipeline> pipelines;
|
||||
|
||||
struct FrameResources {
|
||||
u64 tick;
|
||||
vk::ImageView view;
|
||||
vk::Framebuffer framebuffer;
|
||||
};
|
||||
std::deque<FrameResources> frame_resources;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle3DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle3DPass(const Device& device_, Scheduler& scheduler_,
|
||||
|
||||
@@ -167,6 +167,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
}
|
||||
const Shader::Stage stage{program.stage};
|
||||
const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough};
|
||||
const bool has_tessellation{key.unique_hashes[3] != 0};
|
||||
const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
|
||||
const float point_size{std::bit_cast<float>(key.state.point_size)};
|
||||
switch (stage) {
|
||||
@@ -185,7 +186,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
LOG_WARNING(Render_Vulkan, "XFB requested in pipeline key but device lacks VK_EXT_transform_feedback; ignoring XFB decorations");
|
||||
}
|
||||
}
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
if (!has_tessellation) {
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
}
|
||||
}
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
@@ -224,6 +227,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
ASSERT(false);
|
||||
return Shader::TessSpacing::Equal;
|
||||
}();
|
||||
if (!has_geometry) {
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
}
|
||||
break;
|
||||
case Shader::Stage::Geometry:
|
||||
if (program.output_topology == Shader::OutputTopology::PointList) {
|
||||
@@ -305,12 +311,8 @@ size_t GetTotalPipelineWorkers() {
|
||||
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
|
||||
#ifdef __ANDROID__
|
||||
const int configured = AndroidSettings::values.pipeline_worker_count.GetValue();
|
||||
const int clamped = std::clamp(configured, 4, 8);
|
||||
const size_t desired = static_cast<size_t>(clamped);
|
||||
if (desired == 0) {
|
||||
return 1ULL;
|
||||
}
|
||||
return std::min(max_core_threads, desired);
|
||||
const size_t desired = static_cast<size_t>(std::max(configured, 1));
|
||||
return std::min<size_t>(max_core_threads, desired);
|
||||
#else
|
||||
return max_core_threads;
|
||||
#endif
|
||||
|
||||
@@ -241,11 +241,13 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
if (!pipeline) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
// update engine as channel may be different.
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
if (!pipeline->Configure(is_indexed))
|
||||
return;
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
if (!pipeline->Configure(is_indexed)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDynamicStates();
|
||||
|
||||
@@ -673,7 +675,6 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
|
||||
texture_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::BufferCache))) {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::QueryCache))) {
|
||||
@@ -771,16 +772,22 @@ bool RasterizerVulkan::OnCPUWrite(DAddr addr, u64 size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP = true;
|
||||
static constexpr bool ENABLE_FINE_GRAINED_TRACKER_LOCK = true;
|
||||
|
||||
void RasterizerVulkan::OnCacheInvalidation(DAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
if (!ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP ||
|
||||
device_memory.IsRegionTextureCached(addr, size)) {
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.WriteMemory(addr, size);
|
||||
}
|
||||
{
|
||||
if (ENABLE_FINE_GRAINED_TRACKER_LOCK) {
|
||||
buffer_cache.CpuWriteInvalidate(addr, size);
|
||||
} else {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
if (info.storage) {
|
||||
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
if (IsPixelFormatASTC(format)) {
|
||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
@@ -911,6 +914,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
if (Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu) {
|
||||
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue, memory_allocator);
|
||||
if (device.IsTiler()) {
|
||||
astc_decoder_fragment_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue, render_pass_cache);
|
||||
}
|
||||
}
|
||||
if (!device.IsKhrImageFormatListSupported()) {
|
||||
return;
|
||||
@@ -2850,6 +2857,13 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
u32 z_start, u32 z_count) {
|
||||
|
||||
if (IsPixelFormatASTC(image.info.format)) {
|
||||
if (astc_decoder_fragment_pass) {
|
||||
const VideoCore::Surface::PixelFormat decoded_format =
|
||||
WillUseWidenedAstcFormat(device, image.info)
|
||||
? VideoCore::Surface::PixelFormat::R32G32B32A32_FLOAT
|
||||
: VideoCore::Surface::PixelFormat::A8B8G8R8_UNORM;
|
||||
return astc_decoder_fragment_pass->Assemble(image, map, swizzles, decoded_format);
|
||||
}
|
||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
|
||||
void AccelerateImageUpload(Image&, const StagingBufferRef&,
|
||||
std::span<const VideoCommon::SwizzleParameters>,
|
||||
u32 z_start, u32 z_count);
|
||||
u32 z_start = 0, u32 z_count = 0);
|
||||
|
||||
void InsertUploadMemoryBarrier() {}
|
||||
|
||||
@@ -147,6 +147,7 @@ public:
|
||||
BlitImageHelper& blit_image_helper;
|
||||
RenderPassCache& render_pass_cache;
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
std::optional<ASTCDecoderFragmentPass> astc_decoder_fragment_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
|
||||
@@ -2308,6 +2308,7 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
if (image.cpu_addr < ~(1ULL << 40)) {
|
||||
device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2320,12 +2321,14 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
|
||||
const DAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ForEachSparseSegment(image,
|
||||
[this]([[maybe_unused]] GPUVAddr gpu_addr, DAddr cpu_addr, size_t size) {
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2336,6 +2339,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
if (image.cpu_addr < ~(1ULL << 40)) {
|
||||
device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2348,6 +2352,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
|
||||
const DAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, -1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ public:
|
||||
/// Prepare an image to be used
|
||||
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
std::mutex mutex;
|
||||
|
||||
private:
|
||||
/// Iterate over all page indices in a range
|
||||
|
||||
@@ -517,16 +517,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls.");
|
||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken workgroup memory explicit layout.");
|
||||
RemoveExtensionFeature(extensions.workgroup_memory_explicit_layout,
|
||||
features.workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken conservative rasterization.");
|
||||
RemoveExtension(extensions.conservative_rasterization,
|
||||
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken depth clip control.");
|
||||
RemoveExtensionFeature(extensions.depth_clip_control, features.depth_clip_control,
|
||||
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64.");
|
||||
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
|
||||
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
|
||||
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
|
||||
features.shader_atomic_int64.shaderSharedInt64Atomics = false;
|
||||
features.features.shaderInt64 = false;
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken workgroup memory explicit layout.");
|
||||
RemoveExtensionFeature(extensions.workgroup_memory_explicit_layout,
|
||||
features.workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
|
||||
|
||||
#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64)
|
||||
// BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load.
|
||||
|
||||
@@ -230,6 +230,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkMapMemory);
|
||||
X(vkQueueSubmit);
|
||||
X(vkQueueSubmit2);
|
||||
X(vkResetCommandPool);
|
||||
X(vkResetFences);
|
||||
X(vkResetQueryPool);
|
||||
X(vkSetDebugUtilsObjectNameEXT);
|
||||
|
||||
@@ -346,6 +346,7 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkMapMemory vkMapMemory{};
|
||||
PFN_vkQueueSubmit vkQueueSubmit{};
|
||||
PFN_vkQueueSubmit2 vkQueueSubmit2{};
|
||||
PFN_vkResetCommandPool vkResetCommandPool{};
|
||||
PFN_vkResetFences vkResetFences{};
|
||||
PFN_vkResetQueryPool vkResetQueryPool{};
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
|
||||
@@ -925,6 +926,10 @@ public:
|
||||
CommandBuffers Allocate(std::size_t num_buffers,
|
||||
VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const;
|
||||
|
||||
void Reset(VkCommandPoolResetFlags flags = 0) const {
|
||||
Check(dld->vkResetCommandPool(owner, handle, flags));
|
||||
}
|
||||
|
||||
/// Set object name.
|
||||
void SetObjectNameEXT(const char* name) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user