Compare commits

..

23 Commits

Author SHA1 Message Date
CamilleLaVey f199597158 [TEST] DR Off 2026-07-17 21:03:17 -04:00
CamilleLaVey 9c313fb787 [TEST] Caching for texture + pages on NCE 2026-07-17 20:15:19 -04:00
CamilleLaVey eec29b83f3 [TEST] Coalesce NCE fault write. 2026-07-17 19:56:16 -04:00
CamilleLaVey a57041d62f [TEST] Discards on MSAA depth/stencil 2026-07-17 17:57:55 -04:00
CamilleLaVey 4956bc86c3 [TEST] Hunting down recursive mutex 7 2026-07-17 17:44:24 -04:00
CamilleLaVey e8b1dc7c0b [TEST] Adjustments on CommandPools + ResetQueryPool 2026-07-17 07:52:45 -04:00
CamilleLaVey e81d170458 [TEST] Remove unnecessary memory upload 2026-07-17 07:28:30 -04:00
CamilleLaVey 7ed7e5e31d [TEST] Remove unnecessary submit 2026-07-17 03:56:02 -04:00
CamilleLaVey eb32b8766a [TEST] Hunting down recursive mutex 6 2026-07-17 03:13:55 -04:00
CamilleLaVey a484e6c34b [TEST] Hunting down recursive mutex 5 2026-07-17 03:00:53 -04:00
CamilleLaVey 84490a7d6f [TEST] Hunting down recursive mutex 4 2026-07-17 02:32:14 -04:00
CamilleLaVey 933f79af95 [TEST] Hunting down recursive mutex 3 2026-07-17 02:15:10 -04:00
CamilleLaVey f532357793 [TEST] Hunting down recursive mutex 2 2026-07-17 01:55:28 -04:00
CamilleLaVey ab92e5fa52 [TEST] Hunting down recursive mutex 1 2026-07-17 01:13:15 -04:00
CamilleLaVey 791880f9bf [TEST] Adjust records/commandBuffer 2026-07-16 22:50:08 -04:00
CamilleLaVey e058a15074 [TEST] Wire MSAA resolve to dynamic rendering native resolve 2026-07-16 20:29:18 -04:00
CamilleLaVey 6313800aee [TEST] Check on color components for masks/blending 2026-07-16 20:02:59 -04:00
CamilleLaVey 5428dbbd14 [TEST] Adjustments on blit/clears per blending object 2026-07-16 17:04:32 -04:00
CamilleLaVey 9694216ad7 [TEST] 2nd stage on dynamic rendering implementation 2026-07-16 16:53:22 -04:00
CamilleLaVey 4fbdc133dd [vulkan] Add resume/supend bits on renderpass 2026-07-16 15:50:36 -04:00
CamilleLaVey f210f16e8c [TEST] Debug on clears made by driver 2026-07-16 15:47:23 -04:00
CamilleLaVey 20bf6bc282 [vulkan] Adjustment on the framebuffer use vs non used attachments 2026-07-16 15:45:49 -04:00
CamilleLaVey 99e95eebb1 [vulkan] Initial dynamic rendering implementation 2026-07-16 15:44:06 -04:00
40 changed files with 1161 additions and 548 deletions
@@ -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,
+3 -1
View File
@@ -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;
}
+5
View File
@@ -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;
+23
View File
@@ -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 {
+32 -33
View File
@@ -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 {
+5 -5
View File
@@ -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.
+1 -1
View File
@@ -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);
}
+69 -52
View File
@@ -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
+26 -14
View File
@@ -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 = {}) {
+100 -26
View File
@@ -175,9 +175,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 +276,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 +735,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>
+2 -5
View File
@@ -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();
}
+127 -84
View File
@@ -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);
}
+40 -19
View File
@@ -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))) {
+140 -58
View File
@@ -545,6 +545,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
const VkRenderPass render_pass = framebuffer->RenderPass();
if (!render_pass) {
framebuffer->BeginRendering(cmdbuf);
return;
}
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
const VkExtent2D render_area = framebuffer->RenderArea();
const VkRenderPassBeginInfo renderpass_bi{
@@ -561,6 +565,31 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer)
};
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
}
void EndRenderPass(vk::CommandBuffer& cmdbuf, const Framebuffer* framebuffer) {
if (framebuffer->RenderPass()) {
cmdbuf.EndRenderPass();
} else {
cmdbuf.EndRendering();
}
}
[[nodiscard]] VkPipelineRenderingCreateInfo MakePipelineRenderingCreateInfo(
const Framebuffer* framebuffer) {
return VkPipelineRenderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.pNext = nullptr,
.viewMask = 0,
.colorAttachmentCount = framebuffer->NumColorAttachments(),
.pColorAttachmentFormats = framebuffer->ColorAttachmentFormats().data(),
.depthAttachmentFormat = framebuffer->HasAspectDepthBit()
? framebuffer->DepthAttachmentFormat()
: VK_FORMAT_UNDEFINED,
.stencilAttachmentFormat = framebuffer->HasAspectStencilBit()
? framebuffer->DepthAttachmentFormat()
: VK_FORMAT_UNDEFINED,
};
}
} // Anonymous namespace
BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
@@ -623,10 +652,12 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
RecordShaderReadBarrier(scheduler, src_image_view);
@@ -651,9 +682,11 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key, dst_framebuffer);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
@@ -666,7 +699,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
nullptr);
BindBlitState(cmdbuf, layout, dst_region, src_region, src_size);
cmdbuf.Draw(3, 1, 0, 0);
cmdbuf.EndRenderPass();
EndRenderPass(cmdbuf, dst_framebuffer);
});
}
@@ -676,10 +709,12 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
const BlitMSAAPipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.samples = dst_framebuffer->Samples(),
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key, dst_framebuffer);
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
RecordShaderReadBarrier(scheduler, src_image_view);
@@ -703,7 +738,7 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
const bool resolve_stencil =
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
const VkPipeline pipeline =
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer, resolve_stencil);
const VkPipelineLayout layout =
resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
@@ -747,10 +782,12 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = operation,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipelineLayout layout = *two_textures_pipeline_layout;
const VkSampler sampler = *nearest_sampler;
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key, dst_framebuffer);
const VkImageView src_depth_view = src_image_view.DepthView();
const VkImageView src_stencil_view = src_image_view.StencilView();
@@ -772,25 +809,25 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->RenderPass());
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer);
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->RenderPass());
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer);
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->RenderPass());
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer);
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->RenderPass());
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer);
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
}
@@ -801,35 +838,35 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping");
return;
}
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer,
convert_abgr8_to_d24s8_frag);
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view) {
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(),
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer,
convert_abgr8_to_d32f_frag);
Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer,
convert_d32f_to_abgr8_frag);
ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer,
convert_d24s8_to_abgr8_frag);
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
ImageView& src_image_view) {
ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer,
convert_s8d24_to_abgr8_frag);
ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
}
@@ -840,8 +877,10 @@ void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_ma
const BlitImagePipelineKey key{
.renderpass = dst_framebuffer->RenderPass(),
.operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key, dst_framebuffer);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record(
@@ -867,8 +906,10 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
.stencil_mask = stencil_mask,
.stencil_compare_mask = stencil_compare_mask,
.stencil_ref = stencil_ref,
.color_formats = dst_framebuffer->ColorAttachmentFormats(),
.depth_format = dst_framebuffer->DepthAttachmentFormat(),
};
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key, dst_framebuffer);
const VkPipelineLayout layout = *clear_color_pipeline_layout;
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) {
@@ -1140,12 +1181,14 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
scheduler.InvalidateState();
}
VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key) {
VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
const auto it = std::ranges::find(blit_color_keys, key);
if (it != blit_color_keys.end()) {
return *blit_color_pipelines[std::distance(blit_color_keys.begin(), it)];
}
blit_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_color_to_color_frag);
const VkPipelineColorBlendAttachmentState blend_attachment{
@@ -1173,7 +1216,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1195,17 +1238,19 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
return *blit_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key) {
VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
const auto it = std::ranges::find(blit_depth_stencil_keys, key);
if (it != blit_depth_stencil_keys.end()) {
return *blit_depth_stencil_pipelines[std::distance(blit_depth_stencil_keys.begin(), it)];
}
blit_depth_stencil_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1227,38 +1272,46 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
return *blit_depth_stencil_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key) {
VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer) {
const auto it = std::ranges::find(clear_color_keys, key);
if (it != clear_color_keys.end()) {
return *clear_color_pipelines[std::distance(clear_color_keys.begin(), it)];
}
clear_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*clear_color_vert, *clear_color_frag);
const VkPipelineColorBlendAttachmentState color_blend_attachment_state{
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
.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,
};
const u32 num_color = framebuffer->NumColorAttachments();
constexpr VkColorComponentFlags full_write_mask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
std::array<VkPipelineColorBlendAttachmentState, VideoCommon::NUM_RT> blend_attachments{};
for (u32 index = 0; index < num_color; ++index) {
blend_attachments[index] = VkPipelineColorBlendAttachmentState{
.blendEnable = index == 0 ? VK_TRUE : VK_FALSE,
.srcColorBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_ALPHA,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA,
.alphaBlendOp = VK_BLEND_OP_ADD,
.colorWriteMask = index == 0 ? full_write_mask : VkColorComponentFlags{0},
};
}
const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
.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 = &color_blend_attachment_state,
.attachmentCount = num_color,
.pAttachments = blend_attachments.data(),
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1281,13 +1334,26 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
}
VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key) {
const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer) {
const auto it = std::ranges::find(clear_stencil_keys, key);
if (it != clear_stencil_keys.end()) {
return *clear_stencil_pipelines[std::distance(clear_stencil_keys.begin(), it)];
}
clear_stencil_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag);
const u32 num_color = framebuffer->NumColorAttachments();
std::array<VkPipelineColorBlendAttachmentState, VideoCommon::NUM_RT> blend_attachments{};
const VkPipelineColorBlendStateCreateInfo color_blend_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_CLEAR,
.attachmentCount = num_color,
.pAttachments = blend_attachments.data(),
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
};
const auto stencil = VkStencilOpState{
.failOp = VK_STENCIL_OP_KEEP,
.passOp = VK_STENCIL_OP_REPLACE,
@@ -1314,7 +1380,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1325,7 +1391,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.pDepthStencilState = &depth_stencil_ci,
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
.pColorBlendState = &color_blend_ci,
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.layout = *clear_color_pipeline_layout,
.renderPass = key.renderpass,
@@ -1336,12 +1402,14 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
return *clear_stencil_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key) {
VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key,
const Framebuffer* framebuffer) {
const auto it = std::ranges::find(blit_msaa_color_keys, key);
if (it != blit_msaa_color_keys.end()) {
return *blit_msaa_color_pipelines[std::distance(blit_msaa_color_keys.begin(), it)];
}
blit_msaa_color_keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *blit_color_msaa_frag);
const VkPipelineMultisampleStateCreateInfo multisample_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
@@ -1357,7 +1425,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
blit_msaa_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = key.renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1379,22 +1447,28 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
return *blit_msaa_color_pipelines.back();
}
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
bool resolve_stencil) {
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(
const Framebuffer* framebuffer, bool resolve_stencil) {
const VkRenderPass renderpass = framebuffer->RenderPass();
const ResolveDepthStencilPipelineKey key{
.renderpass = renderpass,
.depth_format = framebuffer->DepthAttachmentFormat(),
};
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
auto& pipelines = resolve_stencil ? resolve_depth_stencil_pipelines : resolve_depth_pipelines;
const auto it = std::ranges::find(keys, renderpass);
const auto it = std::ranges::find(keys, key);
if (it != keys.end()) {
return *pipelines[std::distance(keys.begin(), it)];
}
keys.push_back(renderpass);
keys.push_back(key);
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages =
MakeStages(*full_screen_vert,
resolve_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1462,25 +1536,29 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
return *msaa_copy_pipelines.back();
}
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, false);
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline,
const Framebuffer* framebuffer) {
ConvertPipeline(pipeline, framebuffer, false);
}
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
ConvertPipeline(pipeline, renderpass, true);
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline,
const Framebuffer* framebuffer) {
ConvertPipeline(pipeline, framebuffer, true);
}
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
vk::ShaderModule& module, bool single_texture,
bool is_target_depth) {
if (pipeline) {
return;
}
const VkRenderPass renderpass = framebuffer->RenderPass();
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
const std::array stages = MakeStages(*full_screen_vert, *module);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
@@ -1502,28 +1580,32 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass ren
});
}
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline,
const Framebuffer* framebuffer,
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, renderpass, module, false, false);
ConvertPipelineEx(pipeline, framebuffer, module, false, false);
}
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline,
const Framebuffer* framebuffer,
vk::ShaderModule& module) {
ConvertPipelineEx(pipeline, renderpass, module, true, true);
ConvertPipelineEx(pipeline, framebuffer, module, true, true);
}
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass,
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
bool is_target_depth) {
if (pipeline) {
return;
}
const VkRenderPass renderpass = framebuffer->RenderPass();
const VkPipelineRenderingCreateInfo rendering_ci = MakePipelineRenderingCreateInfo(framebuffer);
VkShaderModule frag_shader =
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = renderpass ? nullptr : &rendering_ci,
.flags = 0,
.stageCount = static_cast<u32>(stages.size()),
.pStages = stages.data(),
+32 -14
View File
@@ -33,6 +33,8 @@ struct BlitImagePipelineKey {
VkRenderPass renderpass;
Tegra::Engines::Fermi2D::Operation operation;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct BlitDepthStencilPipelineKey {
@@ -43,6 +45,8 @@ struct BlitDepthStencilPipelineKey {
u8 stencil_mask;
u32 stencil_compare_mask;
u32 stencil_ref;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct MSAACopyPipelineKey {
@@ -58,6 +62,15 @@ struct BlitMSAAPipelineKey {
VkRenderPass renderpass;
VkSampleCountFlagBits samples;
std::array<VkFormat, VideoCommon::NUM_RT> color_formats;
VkFormat depth_format;
};
struct ResolveDepthStencilPipelineKey {
constexpr auto operator<=>(const ResolveDepthStencilPipelineKey&) const noexcept = default;
VkRenderPass renderpass;
VkFormat depth_format;
};
class BlitImageHelper {
@@ -123,31 +136,36 @@ private:
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceDepthStencilPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceClearColorPipeline(const BlitImagePipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
const BlitDepthStencilPipelineKey& key);
const BlitDepthStencilPipelineKey& key, const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key,
const Framebuffer* framebuffer);
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(const Framebuffer* framebuffer,
bool resolve_stencil);
void ConvertPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass, bool is_target_depth);
void ConvertPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
bool is_target_depth);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass);
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, const Framebuffer* framebuffer);
void ConvertPipelineEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void ConvertPipelineEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
vk::ShaderModule& module, bool single_texture, bool is_target_depth);
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
vk::ShaderModule& module);
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, VkRenderPass renderpass,
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, const Framebuffer* framebuffer,
vk::ShaderModule& module);
const Device& device;
@@ -195,9 +213,9 @@ private:
std::vector<vk::Pipeline> msaa_copy_pipelines;
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
std::vector<VkRenderPass> resolve_depth_keys;
std::vector<ResolveDepthStencilPipelineKey> resolve_depth_keys;
std::vector<vk::Pipeline> resolve_depth_pipelines;
std::vector<VkRenderPass> resolve_depth_stencil_keys;
std::vector<ResolveDepthStencilPipelineKey> resolve_depth_stencil_keys;
std::vector<vk::Pipeline> resolve_depth_stencil_pipelines;
struct MSAACopyResources {
u64 tick;
@@ -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
@@ -290,7 +290,10 @@ GraphicsPipeline::GraphicsPipeline(
descriptor_update_template =
builder.CreateTemplate(set_layout, *pipeline_layout, uses_push_descriptor);
const VkRenderPass render_pass{render_pass_cache.Get(MakeRenderPassKey(key.state, device))};
VkRenderPass render_pass{};
if (!device.IsKhrDynamicRenderingSupported()) {
render_pass = render_pass_cache.Get(MakeRenderPassKey(key.state, device));
}
Validate();
try {
MakePipeline(render_pass);
@@ -996,9 +999,47 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR;
}
const RenderPassKey renderpass_key{MakeRenderPassKey(key.state, device)};
std::array<VkFormat, Maxwell::NumRenderTargets> color_attachment_formats{};
for (size_t index = 0; index < renderpass_key.color_formats.size(); ++index) {
const PixelFormat pixel_format{renderpass_key.color_formats[index]};
if (pixel_format == PixelFormat::Invalid) {
color_attachment_formats[index] = VK_FORMAT_UNDEFINED;
continue;
}
color_attachment_formats[index] =
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, pixel_format).format;
}
VkFormat depth_attachment_format{VK_FORMAT_UNDEFINED};
VkFormat stencil_attachment_format{VK_FORMAT_UNDEFINED};
if (renderpass_key.depth_format != PixelFormat::Invalid) {
const VkFormat format{
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true,
renderpass_key.depth_format)
.format};
const auto surface_type{VideoCore::Surface::GetFormatType(renderpass_key.depth_format)};
if (surface_type == VideoCore::Surface::SurfaceType::Depth ||
surface_type == VideoCore::Surface::SurfaceType::DepthStencil) {
depth_attachment_format = format;
}
if (surface_type == VideoCore::Surface::SurfaceType::Stencil ||
surface_type == VideoCore::Surface::SurfaceType::DepthStencil) {
stencil_attachment_format = format;
}
}
const VkPipelineRenderingCreateInfo rendering_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.pNext = nullptr,
.viewMask = 0,
.colorAttachmentCount = static_cast<u32>(NumAttachments(key.state)),
.pColorAttachmentFormats = color_attachment_formats.data(),
.depthAttachmentFormat = depth_attachment_format,
.stencilAttachmentFormat = stencil_attachment_format,
};
pipeline = device.GetLogical().CreateGraphicsPipeline({
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.pNext = device.IsKhrDynamicRenderingSupported() ? &rendering_ci : nullptr,
.flags = flags,
.stageCount = static_cast<u32>(shader_stages.size()),
.pStages = shader_stages.data(),
@@ -305,12 +305,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,12 +772,15 @@ bool RasterizerVulkan::OnCPUWrite(DAddr addr, u64 size) {
return false;
}
static constexpr bool ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP = 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);
}
+149 -5
View File
@@ -93,13 +93,53 @@ void Scheduler::DispatchWork() {
}
}
void Scheduler::BeginDynamicRendering(const Framebuffer* framebuffer, const DeferredClear* clear) {
const VkExtent2D render_area = framebuffer->RenderArea();
std::array<VkImageView, 9> attachment_views{};
const auto& color_views = framebuffer->ColorAttachments();
for (size_t index = 0; index < color_views.size(); ++index) {
attachment_views[index] = color_views[index];
}
attachment_views[8] = framebuffer->DepthAttachment();
state.renderpass = VkRenderPass{};
state.framebuffer = VkFramebuffer{};
state.attachment_views = attachment_views;
state.color_resolve_views = framebuffer->ColorResolveAttachments();
state.color_resolve_modes = framebuffer->ColorResolveModes();
state.discards_msaa_color = framebuffer->DiscardsMsaaColor();
state.discards_msaa_depth = framebuffer->DiscardsMsaaDepth();
state.render_area = render_area;
state.num_color = framebuffer->NumColorAttachments();
state.has_depth = framebuffer->HasAspectDepthBit();
state.has_stencil = framebuffer->HasAspectStencilBit();
state.layer_count = framebuffer->NumLayers();
state.rendering = true;
if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string render_pass_info =
fmt::format("renderArea={}x{}, numImages={}", render_area.width, render_area.height,
framebuffer->NumImages());
GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info);
}
RecordDynamicBegin(clear);
num_renderpass_images = framebuffer->NumImages();
renderpass_images = framebuffer->Images();
renderpass_image_ranges = framebuffer->ImageRanges();
}
void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass,
const VkClearValue* clear_values, u32 clear_value_count) {
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
if (device.IsKhrDynamicRenderingSupported()) {
BeginDynamicRendering(framebuffer, nullptr);
return;
}
const VkExtent2D render_area = framebuffer->RenderArea();
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
state.renderpass = renderpass;
state.framebuffer = framebuffer_handle;
state.render_area = render_area;
state.rendering = true;
if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string render_pass_info =
@@ -141,6 +181,12 @@ void Scheduler::RealizeDeferredClear() {
const DeferredClear dc = deferred_clear;
deferred_clear = {};
if (device.IsKhrDynamicRenderingSupported()) {
EndRenderPass();
BeginDynamicRendering(dc.framebuffer, &dc);
return;
}
std::array<VkClearValue, 9> clear_values{};
u32 count = 0;
const RenderPassKey& base = dc.framebuffer->RenderPassKeyBase();
@@ -195,9 +241,25 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
RealizeDeferredClear();
return;
}
const VkExtent2D render_area = framebuffer->RenderArea();
if (device.IsKhrDynamicRenderingSupported()) {
std::array<VkImageView, 9> attachment_views{};
const auto& color_views = framebuffer->ColorAttachments();
for (size_t index = 0; index < color_views.size(); ++index) {
attachment_views[index] = color_views[index];
}
attachment_views[8] = framebuffer->DepthAttachment();
if (state.rendering && attachment_views == state.attachment_views &&
render_area.width == state.render_area.width &&
render_area.height == state.render_area.height) {
return;
}
EndRenderPass();
BeginDynamicRendering(framebuffer, nullptr);
return;
}
const VkRenderPass renderpass = framebuffer->RenderPass();
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
const VkExtent2D render_area = framebuffer->RenderArea();
if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer &&
render_area.width == state.render_area.width &&
render_area.height == state.render_area.height) {
@@ -372,6 +434,80 @@ void Scheduler::InvalidateState() {
state_tracker.InvalidateCommandBufferState();
}
void Scheduler::RecordDynamicBegin(const DeferredClear* clear) {
const std::array<VkImageView, 9> views = state.attachment_views;
const std::array<VkImageView, 8> resolve_views = state.color_resolve_views;
const std::array<VkResolveModeFlagBits, 8> resolve_modes = state.color_resolve_modes;
const u32 num_color = state.num_color;
const bool has_depth = state.has_depth;
const bool has_stencil = state.has_stencil;
const u32 layers = state.layer_count;
const VkExtent2D render_area = state.render_area;
const u32 color_clear_mask = clear ? clear->color_clear_mask : 0u;
const u32 color_discard_mask =
clear != nullptr && state.discards_msaa_color ? clear->color_clear_mask : 0u;
const std::array<VkClearValue, 8> color_clear_values =
clear ? clear->color_values : std::array<VkClearValue, 8>{};
const bool ds_clear = clear != nullptr && clear->depth_stencil;
const VkClearValue ds_clear_value = clear ? clear->depth_stencil_value : VkClearValue{};
const bool ds_discard = state.discards_msaa_depth;
Record([views, resolve_views, resolve_modes, num_color, has_depth, has_stencil, layers,
render_area, color_clear_mask, color_discard_mask, color_clear_values, ds_clear,
ds_clear_value, ds_discard](vk::CommandBuffer cmdbuf) {
std::array<VkRenderingAttachmentInfo, VideoCommon::NUM_RT> color_infos{};
for (u32 index = 0; index < num_color; ++index) {
const bool clear_slot = ((color_clear_mask >> index) & 1u) != 0;
const VkImageView resolve_view = resolve_views[index];
const bool has_resolve = resolve_view != VK_NULL_HANDLE;
const bool discard_slot = has_resolve && ((color_discard_mask >> index) & 1u) != 0;
color_infos[index] = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = views[index],
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.resolveMode = has_resolve ? resolve_modes[index] : VK_RESOLVE_MODE_NONE,
.resolveImageView = resolve_view,
.resolveImageLayout =
has_resolve ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
.loadOp = clear_slot ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = discard_slot ? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = clear_slot ? color_clear_values[index] : VkClearValue{},
};
}
const VkRenderingAttachmentInfo depth_info{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = views[8],
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.resolveMode = VK_RESOLVE_MODE_NONE,
.resolveImageView = VK_NULL_HANDLE,
.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.loadOp = ds_clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = ds_discard ? VK_ATTACHMENT_STORE_OP_DONT_CARE
: VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = ds_clear ? ds_clear_value : VkClearValue{},
};
const VkRenderingInfo rendering_info{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.pNext = nullptr,
.flags = 0,
.renderArea =
{
.offset = {.x = 0, .y = 0},
.extent = render_area,
},
.layerCount = layers,
.viewMask = 0,
.colorAttachmentCount = num_color,
.pColorAttachments = color_infos.data(),
.pDepthAttachment = has_depth ? &depth_info : nullptr,
.pStencilAttachment = has_stencil ? &depth_info : nullptr,
};
cmdbuf.BeginRendering(rendering_info);
});
}
void Scheduler::EndPendingOperations() {
query_cache->CounterReset(VideoCommon::QueryType::ZPassPixelCount64);
EndRenderPass();
@@ -380,7 +516,7 @@ void Scheduler::EndPendingOperations() {
void Scheduler::EndRenderPass()
{
RealizeDeferredClear();
if (!state.renderpass) {
if (!state.rendering) {
return;
}
@@ -398,7 +534,8 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
has_transform_feedback = device.IsExtTransformFeedbackSupported(),
dynamic_rendering = device.IsKhrDynamicRenderingSupported()](
vk::CommandBuffer cmdbuf) {
std::array<VkImageMemoryBarrier, 9> barriers;
for (size_t i = 0; i < num_images; ++i) {
@@ -435,7 +572,11 @@ void Scheduler::EndRenderPass()
.subresourceRange = range,
};
}
cmdbuf.EndRenderPass();
if (dynamic_rendering) {
cmdbuf.EndRendering();
} else {
cmdbuf.EndRenderPass();
}
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
@@ -453,6 +594,9 @@ void Scheduler::EndRenderPass()
});
state.renderpass = VkRenderPass{};
state.framebuffer = VkFramebuffer{};
state.attachment_views = {};
state.rendering = false;
num_renderpass_images = 0;
}
+16 -1
View File
@@ -71,7 +71,7 @@ public:
/// Returns true when a render pass is currently active in the scheduler state.
bool IsRenderPassActive() const {
return state.renderpass != VK_NULL_HANDLE;
return state.rendering;
}
/// Update the pipeline to the current execution context.
@@ -250,8 +250,18 @@ private:
struct State {
VkRenderPass renderpass{};
VkFramebuffer framebuffer{};
std::array<VkImageView, 9> attachment_views{};
std::array<VkImageView, 8> color_resolve_views{};
std::array<VkResolveModeFlagBits, 8> color_resolve_modes{};
VkExtent2D render_area = {0, 0};
GraphicsPipeline* graphics_pipeline = nullptr;
bool rendering = false;
bool discards_msaa_color = false;
bool discards_msaa_depth = false;
u32 num_color = 0;
bool has_depth = false;
bool has_stencil = false;
u32 layer_count = 1;
bool is_rescaling = false;
bool rescaling_defined = false;
bool needs_state_enable_refresh = false;
@@ -269,6 +279,9 @@ private:
void BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass renderpass,
const VkClearValue* clear_values, u32 clear_value_count);
/// Begins a dynamic rendering pass, optionally realizing a deferred clear via load ops.
void BeginDynamicRendering(const Framebuffer* framebuffer, const DeferredClear* clear);
/// If a deferred clear is pending.
void RealizeDeferredClear();
@@ -282,6 +295,8 @@ private:
void EndPendingOperations();
void RecordDynamicBegin(const DeferredClear* clear);
void EndRenderPass();
void AcquireNewChunk();
@@ -54,6 +54,7 @@ using VideoCore::Surface::SurfaceType;
namespace {
constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true;
constexpr bool ENABLE_MSAA_COLOR_DISCARD = true;
constexpr bool ENABLE_MSAA_DEPTH_DISCARD = true;
constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
if (color == std::array<float, 4>{0, 0, 0, 0}) {
@@ -1041,6 +1042,32 @@ VkImageView TextureCacheRuntime::GetOrCreateResolveShadow(VkImage msaa_image, Vk
shadow.extent = extent;
shadow.layers = layers;
shadow.up_to_date = true;
if (device.IsKhrDynamicRenderingSupported()) {
scheduler.RecordWithUploadBuffer(
[image = *shadow.image, layers](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
const VkImageMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = layers,
},
};
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
barrier);
});
}
return *shadow.view;
}
@@ -1353,7 +1380,7 @@ void TextureCacheRuntime::BlitImage(Framebuffer* dst_framebuffer, ImageView& dst
}
void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, ImageView& src_view) {
if (!dst->RenderPass()) {
if (!dst->RenderPass() && !device.IsKhrDynamicRenderingSupported()) {
return;
}
@@ -2683,7 +2710,7 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
.height = key.size.height,
}} {
CreateFramebuffer(runtime, color_buffers, depth_buffer, key.is_rescaled);
if (runtime.device.HasDebuggingToolAttached()) {
if (runtime.device.HasDebuggingToolAttached() && framebuffer) {
framebuffer.SetObjectNameEXT(VideoCommon::Name(key).c_str());
}
}
@@ -2726,6 +2753,12 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
image_ranges[num_images] = MakeSubresourceRange(color_buffer);
rt_map[index] = num_images;
samples = color_buffer->Samples();
color_attachments[index] = color_buffer->RenderTarget();
color_attachment_formats[index] =
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true,
color_buffer->format)
.format;
num_color_attachments = static_cast<u32>(index + 1);
++num_images;
}
const size_t num_colors = attachments.size();
@@ -2741,6 +2774,11 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer);
image_ranges[num_images] = subresource_range;
samples = depth_buffer->Samples();
depth_attachment = depth_buffer->RenderTarget();
depth_attachment_format =
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true,
depth_buffer->format)
.format;
++num_images;
has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
@@ -2754,8 +2792,9 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
discard_msaa_color =
ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
discard_msaa_depth = ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_DEPTH_DISCARD &&
samples != VK_SAMPLE_COUNT_1_BIT && has_depth && runtime.device.IsTiler();
renderpass = runtime.render_pass_cache.Get(renderpass_key);
render_pass_key = renderpass_key;
render_pass_cache = &runtime.render_pass_cache;
render_area.width = (std::min)(render_area.width, width);
@@ -2770,10 +2809,15 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
}
const VkFormat vk_format =
MaxwellToVK::SurfaceFormat(runtime.device, FormatType::Optimal, true, format).format;
color_resolve_modes[index] = VideoCore::Surface::IsPixelFormatInteger(format)
? VK_RESOLVE_MODE_SAMPLE_ZERO_BIT
: VK_RESOLVE_MODE_AVERAGE_BIT;
if (ENABLE_MSAA_RESOLVE_CONSUME) {
const VkImage msaa_image = images[rt_map[index]];
attachments.push_back(runtime.GetOrCreateResolveShadow(msaa_image, vk_format,
render_area, layers));
const VkImageView shadow_view = runtime.GetOrCreateResolveShadow(
msaa_image, vk_format, render_area, layers);
color_resolve_attachments[index] = shadow_view;
attachments.push_back(shadow_view);
continue;
}
VkImageCreateInfo resolve_ci{
@@ -2812,6 +2856,33 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
.layerCount = layers,
},
});
if (runtime.device.IsKhrDynamicRenderingSupported()) {
runtime.scheduler.RecordWithUploadBuffer(
[image = *resolve_image, layers](vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) {
const VkImageMemoryBarrier barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = layers,
},
};
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, barrier);
});
}
color_resolve_attachments[index] = *resolve_view;
attachments.push_back(*resolve_view);
resolve_images.push_back(std::move(resolve_image));
resolve_image_views.push_back(std::move(resolve_view));
@@ -2819,6 +2890,11 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
}
num_color_buffers = static_cast<u32>(num_colors);
layer_count = static_cast<u32>((std::max)(num_layers, 1));
if (runtime.device.IsKhrDynamicRenderingSupported()) {
return;
}
renderpass = runtime.render_pass_cache.Get(renderpass_key);
framebuffer = runtime.device.GetLogical().CreateFramebuffer({
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -2828,7 +2904,7 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
.pAttachments = attachments.data(),
.width = render_area.width,
.height = render_area.height,
.layers = static_cast<u32>((std::max)(num_layers, 1)),
.layers = layer_count,
});
}
@@ -2844,6 +2920,53 @@ VkRenderPass Framebuffer::RenderPassVariant(u32 color_clear_mask, bool depth_ste
return render_pass_cache->Get(key);
}
void Framebuffer::BeginRendering(vk::CommandBuffer cmdbuf) const {
std::array<VkRenderingAttachmentInfo, NUM_RT> color_attachment_infos{};
for (size_t index = 0; index < num_color_attachments; ++index) {
color_attachment_infos[index] = VkRenderingAttachmentInfo{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = color_attachments[index],
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.resolveMode = VK_RESOLVE_MODE_NONE,
.resolveImageView = VK_NULL_HANDLE,
.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = {},
};
}
const VkRenderingAttachmentInfo depth_attachment_info{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.pNext = nullptr,
.imageView = depth_attachment,
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
.resolveMode = VK_RESOLVE_MODE_NONE,
.resolveImageView = VK_NULL_HANDLE,
.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.clearValue = {},
};
const VkRenderingInfo rendering_info{
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.pNext = nullptr,
.flags = 0,
.renderArea =
{
.offset = {.x = 0, .y = 0},
.extent = render_area,
},
.layerCount = layer_count,
.viewMask = 0,
.colorAttachmentCount = num_color_attachments,
.pColorAttachments = color_attachment_infos.data(),
.pDepthAttachment = has_depth ? &depth_attachment_info : nullptr,
.pStencilAttachment = has_stencil ? &depth_attachment_info : nullptr,
};
cmdbuf.BeginRendering(rendering_info);
}
void TextureCacheRuntime::AccelerateImageUpload(
Image& image, const StagingBufferRef& map,
std::span<const VideoCommon::SwizzleParameters> swizzles,
@@ -178,6 +178,8 @@ public:
std::span<ImageView*, NUM_RT> color_buffers, ImageView* depth_buffer,
bool is_rescaled = false);
void BeginRendering(vk::CommandBuffer cmdbuf) const;
[[nodiscard]] VkFramebuffer Handle() const noexcept {
return *framebuffer;
}
@@ -193,6 +195,39 @@ public:
[[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
u32 color_discard_mask) const;
[[nodiscard]] u32 NumColorAttachments() const noexcept {
return num_color_attachments;
}
[[nodiscard]] const std::array<VkImageView, NUM_RT>& ColorAttachments() const noexcept {
return color_attachments;
}
[[nodiscard]] const std::array<VkFormat, NUM_RT>& ColorAttachmentFormats() const noexcept {
return color_attachment_formats;
}
[[nodiscard]] const std::array<VkImageView, NUM_RT>& ColorResolveAttachments() const noexcept {
return color_resolve_attachments;
}
[[nodiscard]] const std::array<VkResolveModeFlagBits, NUM_RT>& ColorResolveModes()
const noexcept {
return color_resolve_modes;
}
[[nodiscard]] VkImageView DepthAttachment() const noexcept {
return depth_attachment;
}
[[nodiscard]] VkFormat DepthAttachmentFormat() const noexcept {
return depth_attachment_format;
}
[[nodiscard]] u32 NumLayers() const noexcept {
return layer_count;
}
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
return render_area;
}
@@ -245,6 +280,10 @@ public:
return discard_msaa_color;
}
[[nodiscard]] bool DiscardsMsaaDepth() const noexcept {
return discard_msaa_depth;
}
private:
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
@@ -255,6 +294,14 @@ private:
std::array<VkImage, 9> images{};
std::array<VkImageSubresourceRange, 9> image_ranges{};
std::array<size_t, NUM_RT> rt_map{};
std::array<VkImageView, NUM_RT> color_attachments{};
std::array<VkFormat, NUM_RT> color_attachment_formats{};
std::array<VkImageView, NUM_RT> color_resolve_attachments{};
std::array<VkResolveModeFlagBits, NUM_RT> color_resolve_modes{};
VkImageView depth_attachment{};
VkFormat depth_attachment_format = VK_FORMAT_UNDEFINED;
u32 num_color_attachments = 0;
u32 layer_count = 1;
bool has_depth{};
bool has_stencil{};
bool is_rescaled{};
@@ -263,6 +310,7 @@ private:
RenderPassKey render_pass_key{};
RenderPassCache* render_pass_cache{nullptr};
bool discard_msaa_color{};
bool discard_msaa_depth{};
};
class Image : public VideoCommon::ImageBase {
@@ -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
@@ -1373,6 +1373,11 @@ void Device::RemoveUnsuitableExtensions() {
extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME);
// VK_KHR_dynamic_rendering
extensions.dynamic_rendering = features.dynamic_rendering.dynamicRendering;
RemoveExtensionFeatureIfUnsuitable(extensions.dynamic_rendering, features.dynamic_rendering,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
// VK_KHR_maintenance4
extensions.maintenance4 = features.maintenance4.maintenance4;
RemoveExtensionFeatureIfUnsuitable(extensions.maintenance4, features.maintenance4,
@@ -43,6 +43,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \
shader_demote_to_helper_invocation) \
FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \
FEATURE(KHR, DynamicRendering, DYNAMIC_RENDERING, dynamic_rendering) \
FEATURE(KHR, Maintenance4, MAINTENANCE_4, maintenance4) \
FEATURE(KHR, Synchronization2, SYNCHRONIZATION_2, synchronization2)
@@ -941,6 +942,13 @@ FN_MAX_LIMIT_LIST
return extensions.maintenance3;
}
static constexpr bool ENABLE_DYNAMIC_RENDERING = false;
/// Returns true if the device supports VK_KHR_dynamic_rendering.
bool IsKhrDynamicRenderingSupported() const {
return ENABLE_DYNAMIC_RENDERING && extensions.dynamic_rendering;
}
/// Returns true if the device supports VK_KHR_maintenance4.
bool IsKhrMaintenance4Supported() const {
return extensions.maintenance4;
@@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdBeginConditionalRenderingEXT);
X(vkCmdBeginQuery);
X(vkCmdBeginRenderPass);
X(vkCmdBeginRendering);
X(vkCmdBeginTransformFeedbackEXT);
X(vkCmdBeginDebugUtilsLabelEXT);
X(vkCmdBindDescriptorSets);
@@ -119,6 +120,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkCmdEndConditionalRenderingEXT);
X(vkCmdEndQuery);
X(vkCmdEndRenderPass);
X(vkCmdEndRendering);
X(vkCmdEndTransformFeedbackEXT);
X(vkCmdEndDebugUtilsLabelEXT);
X(vkCmdFillBuffer);
@@ -230,6 +232,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkMapMemory);
X(vkQueueSubmit);
X(vkQueueSubmit2);
X(vkResetCommandPool);
X(vkResetFences);
X(vkResetQueryPool);
X(vkSetDebugUtilsObjectNameEXT);
@@ -257,6 +260,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device);
}
// Support for dynamic rendering is optional until Vulkan 1.3
if (!dld.vkCmdBeginRendering) {
Proc(dld.vkCmdBeginRendering, dld, "vkCmdBeginRenderingKHR", device);
Proc(dld.vkCmdEndRendering, dld, "vkCmdEndRenderingKHR", device);
}
// Synchronization2 is core in Vulkan 1.3, otherwise requires VK_KHR_synchronization2
if (!dld.vkCmdPipelineBarrier2) {
Proc(dld.vkCmdPipelineBarrier2, dld, "vkCmdPipelineBarrier2KHR", device);
@@ -208,6 +208,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT{};
PFN_vkCmdBeginQuery vkCmdBeginQuery{};
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass{};
PFN_vkCmdBeginRendering vkCmdBeginRendering{};
PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT{};
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets{};
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer{};
@@ -236,6 +237,7 @@ struct DeviceDispatch : InstanceDispatch {
PFN_vkCmdEndDebugUtilsLabelEXT vkCmdEndDebugUtilsLabelEXT{};
PFN_vkCmdEndQuery vkCmdEndQuery{};
PFN_vkCmdEndRenderPass vkCmdEndRenderPass{};
PFN_vkCmdEndRendering vkCmdEndRendering{};
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{};
PFN_vkCmdFillBuffer vkCmdFillBuffer{};
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{};
@@ -346,6 +348,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 +928,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;
};
@@ -1186,6 +1193,14 @@ public:
dld->vkCmdEndRenderPass(handle);
}
void BeginRendering(const VkRenderingInfo& rendering_info) const noexcept {
dld->vkCmdBeginRendering(handle, &rendering_info);
}
void EndRendering() const noexcept {
dld->vkCmdEndRendering(handle);
}
void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept {
dld->vkCmdBeginQuery(handle, query_pool, query, flags);
}