Fix segfault with clear mask

This commit is contained in:
Lander Gallastegi 2025-07-13 23:27:07 +02:00
parent a6cdf2eac7
commit 2849e970cb
4 changed files with 62 additions and 30 deletions

View File

@ -3,23 +3,43 @@
#pragma once
#include <cstddef>
#include <tuple>
namespace Common {
template <class Func>
struct FuncTraits {};
template <class Func, class Enable = void>
struct FuncTraits;
// Function type
template <class ReturnType_, class... Args>
struct FuncTraits<ReturnType_ (*)(Args...)> {
struct FuncTraits<ReturnType_(Args...), void> {
using ReturnType = ReturnType_;
static constexpr size_t NUM_ARGS = sizeof...(Args);
template <size_t I>
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
};
// Function pointer
template <class ReturnType_, class... Args>
struct FuncTraits<ReturnType_ (*)(Args...), void> : FuncTraits<ReturnType_(Args...)> {};
// Member function pointer
template <class ClassType, class ReturnType_, class... Args>
struct FuncTraits<ReturnType_ (ClassType::*)(Args...), void> : FuncTraits<ReturnType_(Args...)> {};
template <class ClassType, class ReturnType_, class... Args>
struct FuncTraits<ReturnType_ (ClassType::*)(Args...) const, void>
: FuncTraits<ReturnType_(Args...)> {};
// Catch-all for callables
template <class Func>
struct FuncTraits<Func, std::void_t<decltype(&std::remove_reference_t<Func>::operator())>>
: FuncTraits<decltype(&std::remove_reference_t<Func>::operator())> {};
// For lambdas: for compat (may be removed)
template <typename Func>
struct LambdaTraits : LambdaTraits<decltype(&std::remove_reference_t<Func>::operator())> {};

View File

@ -1064,8 +1064,11 @@ void BufferCache::SynchronizeBuffersForDma() {
copies.clear();
};
mapped_ranges.ForEach([&](VAddr device_addr, u64 size) {
memory_tracker->ForEachUploadRange(device_addr, size, false, [&](u64 device_addr_out, u64 range_size) {
RENDERER_TRACE;
memory_tracker->ForEachUploadRange(device_addr, size, false, [&](u64 device_addr_out, u64 range_size, RegionBits& clear_mask) {
RENDERER_TRACE;
ForEachBufferInRange(device_addr_out, range_size, [&](BufferId buffer_id, Buffer& buffer) {
RENDERER_TRACE;
if (last_buffer_id != buffer_id) {
upload_pending();
last_buffer_id = buffer_id;
@ -1082,6 +1085,14 @@ void BufferCache::SynchronizeBuffersForDma() {
.dstOffset = copy_start - buffer.CpuAddr(),
.size = copy_size,
});
// We need to use tracker page size here, we are marking the clear mask
const u64 page_start = (copy_start & TRACKER_HIGHER_PAGE_MASK) >> TRACKER_PAGE_BITS;
const u64 page_end =
Common::DivCeil((copy_end - 1) & TRACKER_HIGHER_PAGE_MASK, TRACKER_BYTES_PER_PAGE);
ASSERT(page_start < page_end);
LOG_WARNING(Render_Vulkan, "Page start {:#x}, end {:#x}", page_start, page_end);
clear_mask.SetRange(page_start, page_end);
});
}, upload_pending);
});

View File

@ -18,11 +18,10 @@ constexpr u64 TRACKER_HIGHER_PAGE_MASK = TRACKER_HIGHER_PAGE_SIZE - 1ULL;
constexpr u64 NUM_PAGES_PER_REGION = TRACKER_HIGHER_PAGE_SIZE / TRACKER_BYTES_PER_PAGE;
enum class Type {
None = 0,
CPU = 1 << 0,
GPU = 1 << 1,
None,
CPU,
GPU,
};
DECLARE_ENUM_FLAG_OPERATORS(Type)
using RegionBits = Common::BitArray<NUM_PAGES_PER_REGION>;

View File

@ -5,6 +5,7 @@
#include "common/config.h"
#include "common/div_ceil.h"
#include "common/func_traits.h"
#ifdef __linux__
#include "common/adaptive_mutex.h"
@ -69,20 +70,6 @@ public:
}
}
template <Type type, bool enable>
void PerformDeferredProtections() {
bool was_deferred = True(deferred_protection & type);
if (!was_deferred) {
return;
}
deferred_protection &= ~type;
if constexpr (type == Type::CPU) {
UpdateProtection<!enable, false>();
} else if constexpr (type == Type::GPU) {
UpdateProtection<enable, true>();
}
}
/**
* Change the state of a range of pages
*
@ -121,9 +108,11 @@ public:
* @param size Size in bytes of the CPU range to loop over
* @param func Function to call for each turned off region
*/
template <Type type, bool clear>
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, auto&& func) {
template <Type type, bool clear, typename F>
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, F&& func) {
RENDERER_TRACE;
using FuncTraits = Common::FuncTraits<F>;
constexpr bool uses_clear_mask = FuncTraits::NUM_ARGS == 3;
const size_t offset = query_cpu_range - cpu_addr;
const size_t start_page = SanitizeAddress(offset) / TRACKER_BYTES_PER_PAGE;
const size_t end_page =
@ -135,18 +124,31 @@ public:
RegionBits& bits = GetRegionBits<type>();
RegionBits mask(bits, start_page, end_page);
if constexpr (uses_clear_mask) {
static_assert(clear, "Function must not use clear mask when not clearing");
RegionBits clear_mask;
for (const auto& [start, end] : mask) {
func(cpu_addr + start * TRACKER_BYTES_PER_PAGE,
(end - start) * TRACKER_BYTES_PER_PAGE, clear_mask);
}
bits &= ~clear_mask;
} else {
for (const auto& [start, end] : mask) {
func(cpu_addr + start * TRACKER_BYTES_PER_PAGE,
(end - start) * TRACKER_BYTES_PER_PAGE);
}
if constexpr (clear) {
bits &= ~mask;
}
}
if constexpr (clear) {
bits.UnsetRange(start_page, end_page);
if constexpr (type == Type::CPU) {
UpdateProtection<true, false>();
} else if (Config::readbacks()) {
UpdateProtection<false, true>();
}
}
for (const auto& [start, end] : mask) {
func(cpu_addr + start * TRACKER_BYTES_PER_PAGE, (end - start) * TRACKER_BYTES_PER_PAGE);
}
}
/**