mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 21:36:54 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffc81377b2 | |||
| 0685bffb2f |
+30
-30
@@ -488,16 +488,15 @@ void TextureCache::RefreshCopySource(ImageId id) {
|
||||
}
|
||||
|
||||
bool TextureCache::CopyD16(Image& destination, Image& source) {
|
||||
const bool source_depth = source.info.IsDepth();
|
||||
const bool destination_depth = destination.info.IsDepth();
|
||||
if (source_depth == destination_depth) {
|
||||
// Converts guest D16 between a four-byte host depth aspect and two-byte color or D16 depth.
|
||||
const bool source_wide = DepthAspectTransferBytes(source.backing.format) == sizeof(uint32_t);
|
||||
if (source_wide == (DepthAspectTransferBytes(destination.backing.format) == sizeof(uint32_t))) {
|
||||
return false;
|
||||
}
|
||||
auto& depth = source_depth ? source : destination;
|
||||
auto& color = source_depth ? destination : source;
|
||||
const auto transfer_bytes = DepthAspectTransferBytes(depth.backing.format);
|
||||
auto& depth = source_wide ? source : destination;
|
||||
auto& narrow = source_wide ? destination : source;
|
||||
if (depth.info.bytes_per_block != sizeof(uint16_t) ||
|
||||
color.info.bytes_per_block != sizeof(uint16_t) || transfer_bytes != sizeof(uint32_t)) {
|
||||
narrow.info.bytes_per_block != sizeof(uint16_t)) {
|
||||
return false;
|
||||
}
|
||||
EXIT_IF(source.backing.samples != 1 || destination.backing.samples != 1 ||
|
||||
@@ -507,42 +506,42 @@ bool TextureCache::CopyD16(Image& destination, Image& source) {
|
||||
|
||||
const auto layers = depth.info.resources.layers;
|
||||
const uint64_t depth_slice =
|
||||
static_cast<uint64_t>(depth.info.pitch) * depth.info.extent.height * transfer_bytes;
|
||||
const uint64_t color_slice =
|
||||
static_cast<uint64_t>(color.info.pitch) * color.info.extent.height * sizeof(uint16_t);
|
||||
EXIT_IF(layers == 0 || depth_slice > UINT64_MAX / layers || color_slice > UINT64_MAX / layers);
|
||||
const auto depth_size = depth_slice * layers;
|
||||
const auto color_size = color_slice * layers;
|
||||
const std::array depth_copies {
|
||||
static_cast<uint64_t>(depth.info.pitch) * depth.info.extent.height * sizeof(uint32_t);
|
||||
const uint64_t narrow_slice =
|
||||
static_cast<uint64_t>(narrow.info.pitch) * narrow.info.extent.height * sizeof(uint16_t);
|
||||
EXIT_IF(layers == 0 || depth_slice > UINT64_MAX / layers || narrow_slice > UINT64_MAX / layers);
|
||||
const std::array depth_copies {
|
||||
vk::BufferImageCopy {0,
|
||||
depth.info.pitch,
|
||||
depth.info.extent.height,
|
||||
{vk::ImageAspectFlagBits::eDepth, 0, 0, layers},
|
||||
{},
|
||||
depth.info.extent}};
|
||||
const std::array color_copies {
|
||||
const std::array narrow_copies {
|
||||
vk::BufferImageCopy {0,
|
||||
color.info.pitch,
|
||||
color.info.extent.height,
|
||||
{vk::ImageAspectFlagBits::eColor, 0, 0, layers},
|
||||
narrow.info.pitch,
|
||||
narrow.info.extent.height,
|
||||
{narrow.info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
|
||||
: vk::ImageAspectFlagBits::eColor,
|
||||
0, 0, layers},
|
||||
{},
|
||||
color.info.extent}};
|
||||
narrow.info.extent}};
|
||||
|
||||
auto depth_buffer = m_tiler.GetScratchBuffer(depth_size);
|
||||
auto color_buffer = m_tiler.GetScratchBuffer(color_size, depth_buffer.buffer);
|
||||
auto depth_buffer = m_tiler.GetScratchBuffer(depth_slice * layers);
|
||||
auto narrow_buffer = m_tiler.GetScratchBuffer(narrow_slice * layers, depth_buffer.buffer);
|
||||
const TileManager::D16Layout promote_layout {
|
||||
.width = depth.info.extent.width,
|
||||
.height = depth.info.extent.height,
|
||||
.layers = layers,
|
||||
.source_row_stride = static_cast<uint64_t>(color.info.pitch) * sizeof(uint16_t),
|
||||
.target_row_stride = static_cast<uint64_t>(depth.info.pitch) * transfer_bytes,
|
||||
.source_slice_stride = color_slice,
|
||||
.source_row_stride = static_cast<uint64_t>(narrow.info.pitch) * sizeof(uint16_t),
|
||||
.target_row_stride = static_cast<uint64_t>(depth.info.pitch) * sizeof(uint32_t),
|
||||
.source_slice_stride = narrow_slice,
|
||||
.target_slice_stride = depth_slice,
|
||||
};
|
||||
const bool d32 = DepthAspectTransferFormat(depth.backing.format) == vk::Format::eD32Sfloat;
|
||||
if (source_depth) {
|
||||
if (source_wide) {
|
||||
source.Download(depth_copies, depth_buffer.buffer, depth_buffer.offset, depth_buffer.size);
|
||||
m_tiler.ConvertD16(depth_buffer, color_buffer, TileManager::D16Direction::Demote, d32,
|
||||
m_tiler.ConvertD16(depth_buffer, narrow_buffer, TileManager::D16Direction::Demote, d32,
|
||||
{.width = promote_layout.width,
|
||||
.height = promote_layout.height,
|
||||
.layers = promote_layout.layers,
|
||||
@@ -550,11 +549,12 @@ bool TextureCache::CopyD16(Image& destination, Image& source) {
|
||||
.target_row_stride = promote_layout.source_row_stride,
|
||||
.source_slice_stride = promote_layout.target_slice_stride,
|
||||
.target_slice_stride = promote_layout.source_slice_stride});
|
||||
destination.Upload(color_copies, color_buffer.buffer, color_buffer.offset,
|
||||
color_buffer.size);
|
||||
destination.Upload(narrow_copies, narrow_buffer.buffer, narrow_buffer.offset,
|
||||
narrow_buffer.size);
|
||||
} else {
|
||||
source.Download(color_copies, color_buffer.buffer, color_buffer.offset, color_buffer.size);
|
||||
m_tiler.ConvertD16(color_buffer, depth_buffer, TileManager::D16Direction::Promote, d32,
|
||||
source.Download(narrow_copies, narrow_buffer.buffer, narrow_buffer.offset,
|
||||
narrow_buffer.size);
|
||||
m_tiler.ConvertD16(narrow_buffer, depth_buffer, TileManager::D16Direction::Promote, d32,
|
||||
promote_layout);
|
||||
destination.Upload(depth_copies, depth_buffer.buffer, depth_buffer.offset,
|
||||
depth_buffer.size);
|
||||
|
||||
+445
-1077
File diff suppressed because it is too large
Load Diff
@@ -225,6 +225,11 @@ bool TestPlaceholderRangeIsFree(uint64_t vaddr, uint64_t size);
|
||||
bool TestGuestAddressRangeIsOwned(uint64_t vaddr, uint64_t size);
|
||||
bool TestGuestBackingOutsideAddressSpace();
|
||||
uint64_t TestGuestBackingSize();
|
||||
uint64_t TestFlexibleBackingHighWater();
|
||||
uint64_t TestGuestFreeRangeVisits();
|
||||
uint64_t TestDirectMappingCount();
|
||||
uint64_t TestFlexibleMappingCount();
|
||||
uint64_t TestBackingBytesZeroed();
|
||||
bool TestGuestFreeRangeBounds();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -203,6 +203,9 @@ public:
|
||||
}
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::memset(m_backing_base + backing_offset, 0, size);
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
g_test_backing_bytes_zeroed += size;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -287,6 +290,18 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProtectViews(uint64_t vaddr, uint64_t size, VirtualMemory::Mode mode) {
|
||||
std::shared_lock lock(m_mutex);
|
||||
bool ok = true;
|
||||
ForEachSparseRange(
|
||||
m_maps, vaddr, size,
|
||||
[&](const auto&, uint64_t start, uint64_t bytes) {
|
||||
ok = ok && VirtualMemory::Protect(start, bytes, mode);
|
||||
},
|
||||
[&](uint64_t, uint64_t) { ok = false; });
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool MapFixed(uint64_t vaddr, uint64_t size, uint64_t backing_offset,
|
||||
VirtualMemory::Mode mode, FailureReason* failure_reason = nullptr) {
|
||||
if (!IsAvailable() || !RangeOk(backing_offset, size)) {
|
||||
@@ -324,15 +339,11 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Unmap(uint64_t vaddr, uint64_t size, bool* placeholder_preserved = nullptr) {
|
||||
bool Unmap(uint64_t vaddr, uint64_t size) {
|
||||
if (size == 0 || UINT64_MAX - vaddr < size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = false;
|
||||
}
|
||||
|
||||
const auto end = vaddr + size;
|
||||
std::vector<MapEntry> targets;
|
||||
{
|
||||
@@ -350,42 +361,36 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MapEntry> removed;
|
||||
removed.reserve(targets.size());
|
||||
size_t removed = 0;
|
||||
auto rollback = [&] {
|
||||
while (removed != 0) {
|
||||
const auto& previous = targets[--removed];
|
||||
EXIT_IF(!MapFixed(previous.vaddr, previous.size, previous.backing_offset,
|
||||
previous.mode));
|
||||
}
|
||||
};
|
||||
for (const auto& entry: targets) {
|
||||
bool part_placeholder_preserved = false;
|
||||
if (!UnmapOne(entry.vaddr, entry.size, &part_placeholder_preserved)) {
|
||||
for (auto it = removed.rbegin(); it != removed.rend(); ++it) {
|
||||
EXIT_IF(!MapFixed(it->vaddr, it->size, it->backing_offset, it->mode));
|
||||
}
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = false;
|
||||
}
|
||||
if (!UnmapOne(entry.vaddr, entry.size)) {
|
||||
rollback();
|
||||
return false;
|
||||
}
|
||||
EXIT_IF(!part_placeholder_preserved);
|
||||
removed.push_back(entry);
|
||||
++removed;
|
||||
}
|
||||
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
// Each unmapped file view becomes a separate Windows placeholder. The address-space owner
|
||||
// records this contiguous guest span as one free range, so normalize fragmented backing
|
||||
// views into one placeholder before publishing it there.
|
||||
if (removed.size() > 1 && !CoalescePlaceholders(vaddr, size)) {
|
||||
for (auto it = removed.rbegin(); it != removed.rend(); ++it) {
|
||||
EXIT_IF(!MapFixed(it->vaddr, it->size, it->backing_offset, it->mode));
|
||||
}
|
||||
if (removed > 1 && !CoalescePlaceholders(vaddr, size)) {
|
||||
rollback();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnmapOne(uint64_t vaddr, uint64_t size, bool* placeholder_preserved = nullptr) {
|
||||
bool UnmapOne(uint64_t vaddr, uint64_t size) {
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
if (g_test_backing_store_unmaps_before_failure == 0) {
|
||||
g_test_backing_store_unmaps_before_failure = UINT32_MAX;
|
||||
@@ -428,9 +433,7 @@ public:
|
||||
m_maps.emplace(old.vaddr, old);
|
||||
return false;
|
||||
}
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -451,9 +454,6 @@ public:
|
||||
ok;
|
||||
}
|
||||
if (ok) {
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -481,9 +481,6 @@ public:
|
||||
std::lock_guard lock(m_mutex);
|
||||
RemoveMappingsUnlocked(vaddr, size);
|
||||
|
||||
if (placeholder_preserved != nullptr) {
|
||||
*placeholder_preserved = true;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -634,8 +631,6 @@ private:
|
||||
|
||||
HANDLE m_handle = nullptr;
|
||||
#elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX
|
||||
[[nodiscard]] static uint64_t GetMappableTestSize() { return 0x4000; }
|
||||
|
||||
static int GetProtect(VirtualMemory::Mode mode) {
|
||||
switch (mode) {
|
||||
case VirtualMemory::Mode::Read: return PROT_READ;
|
||||
@@ -760,7 +755,7 @@ public:
|
||||
if (size == 0 || UINT64_MAX - vaddr < size) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
return std::any_of(m_owned.begin(), m_owned.end(), [vaddr, size](const auto& range) {
|
||||
return VirtualRangesOverlap(vaddr, size, range.first, range.second);
|
||||
});
|
||||
@@ -864,11 +859,7 @@ public:
|
||||
if (!ContainsMappedUnlocked(vaddr, size, MappingType::Backing)) {
|
||||
return false;
|
||||
}
|
||||
bool placeholder_preserved = false;
|
||||
if (!m_backing->Unmap(vaddr, size, &placeholder_preserved)) {
|
||||
return false;
|
||||
}
|
||||
if (!placeholder_preserved) {
|
||||
if (!m_backing->Unmap(vaddr, size)) {
|
||||
return false;
|
||||
}
|
||||
EXIT_IF(!RemoveMappedUnlocked(vaddr, size, MappingType::Backing));
|
||||
@@ -876,12 +867,13 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reservations live in VirtualRanges; these free-range checks only read the host placeholders.
|
||||
uint64_t ReserveAligned(uint64_t search_addr, uint64_t size, uint64_t alignment) {
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
alignment = std::max<uint64_t>(alignment != 0 ? alignment : PageSize(), PageSize());
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
if (search_addr == 0) {
|
||||
auto addr = FindFreeAlignedUnlocked(HOST_SYSTEM_MANAGED_MIN,
|
||||
HOST_SYSTEM_MANAGED_MAX + 1u, size, alignment);
|
||||
@@ -898,7 +890,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
alignment = std::max<uint64_t>(alignment, PageSize());
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
return FindFreeAlignedUnlocked(search_start, search_end, size, alignment);
|
||||
}
|
||||
|
||||
@@ -906,19 +898,19 @@ public:
|
||||
if (vaddr == 0 || size == 0 || !IsPageAligned(vaddr) || !IsPageAligned(size)) {
|
||||
return false;
|
||||
}
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
return FindContaining(vaddr, size) != m_free.end();
|
||||
}
|
||||
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
bool TestContainsFree(uint64_t vaddr, uint64_t size) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
return FindContaining(vaddr, size) != m_free.end();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ReleaseFree(uint64_t vaddr, uint64_t size) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
std::shared_lock lock(m_mutex);
|
||||
return FindContaining(vaddr, size) != m_free.end();
|
||||
}
|
||||
|
||||
@@ -1041,10 +1033,6 @@ private:
|
||||
return (value & (PageSize() - 1u)) == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsGranularityAligned(uint64_t value) const {
|
||||
return (value & (m_granularity - 1u)) == 0;
|
||||
}
|
||||
|
||||
bool ProtectMappedUnlocked(uint64_t vaddr, uint64_t size, VirtualMemory::Mode mode) {
|
||||
const auto end = vaddr + size;
|
||||
auto it = m_mapped.upper_bound(vaddr);
|
||||
@@ -1055,8 +1043,20 @@ private:
|
||||
const auto& region = it->second;
|
||||
const auto range_start = std::max(vaddr, region.vaddr);
|
||||
const auto range_end = std::min(end, region.vaddr + region.size);
|
||||
if (range_start < range_end &&
|
||||
!VirtualMemory::Protect(range_start, range_end - range_start, mode)) {
|
||||
if (range_start >= range_end) {
|
||||
continue;
|
||||
}
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
// Rollback can split a backing view without changing the owner interval.
|
||||
// Windows protection calls must stay within each actual view.
|
||||
if (region.type == MappingType::Backing) {
|
||||
if (!m_backing->ProtectViews(range_start, range_end - range_start, mode)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!VirtualMemory::Protect(range_start, range_end - range_start, mode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1312,12 +1312,21 @@ private:
|
||||
}
|
||||
|
||||
uint64_t FindFreeAlignedUnlocked(uint64_t search_start, uint64_t search_end, uint64_t size,
|
||||
uint64_t alignment) {
|
||||
uint64_t alignment) const {
|
||||
if (search_start >= search_end || size > search_end - search_start) {
|
||||
return 0;
|
||||
}
|
||||
for (const auto& [base, range_size]: m_free) {
|
||||
const auto candidate = AlignUp(std::max(base, search_start), alignment);
|
||||
// Include the predecessor because search_start can lie inside a free range.
|
||||
auto it = m_free.upper_bound(search_start);
|
||||
if (it != m_free.begin()) {
|
||||
--it;
|
||||
}
|
||||
for (; it != m_free.end() && it->first < search_end; ++it) {
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
g_test_free_range_visits.fetch_add(1, std::memory_order_relaxed);
|
||||
#endif
|
||||
const auto& [base, range_size] = *it;
|
||||
const auto candidate = AlignUp(std::max(base, search_start), alignment);
|
||||
if (candidate < search_end && size <= search_end - candidate &&
|
||||
GuestFreeRangeContains(base, range_size, candidate, size)) {
|
||||
return candidate;
|
||||
@@ -1331,9 +1340,8 @@ private:
|
||||
if (next == m_free.begin()) {
|
||||
return m_free.end();
|
||||
}
|
||||
auto it = std::prev(next);
|
||||
auto end = it->first + it->second;
|
||||
return (vaddr >= it->first && vaddr + size <= end ? it : m_free.end());
|
||||
auto it = std::prev(next);
|
||||
return GuestFreeRangeContains(it->first, it->second, vaddr, size) ? it : m_free.end();
|
||||
}
|
||||
|
||||
bool SplitUnlocked(std::map<uint64_t, uint64_t>::iterator it, uint64_t vaddr, uint64_t size) {
|
||||
|
||||
+28
-10
@@ -38,8 +38,8 @@ struct Global {
|
||||
};
|
||||
|
||||
struct Shadow {
|
||||
uintptr_t start = 0;
|
||||
int count = 0;
|
||||
uintptr_t start = 0;
|
||||
uint64_t covered_bytes = 0;
|
||||
};
|
||||
|
||||
struct AsanContext {
|
||||
@@ -50,6 +50,14 @@ struct AsanContext {
|
||||
static uint32_t g_fake_stack = 0;
|
||||
static AsanContext* g_ctx = nullptr;
|
||||
|
||||
// Allocation and release callbacks can partition the same guest range differently.
|
||||
static uint64_t ShadowCoverage(uintptr_t page, uintptr_t min_addr, uintptr_t max_addr) {
|
||||
const auto guest_start = (page - 0x10000000000ull) << 3u;
|
||||
const auto guest_end = guest_start + 0x80000ull - 1;
|
||||
return std::min<uintptr_t>(max_addr, guest_end) - std::max<uintptr_t>(min_addr, guest_start) +
|
||||
1;
|
||||
}
|
||||
|
||||
static void AllocShadow(uintptr_t min_addr, uintptr_t max_addr) {
|
||||
EXIT_IF(g_ctx == nullptr);
|
||||
|
||||
@@ -67,10 +75,11 @@ static void AllocShadow(uintptr_t min_addr, uintptr_t max_addr) {
|
||||
static_cast<uint64_t>(min_shadow), static_cast<uint64_t>(size));
|
||||
|
||||
for (uintptr_t page = min_shadow; page <= max_shadow; page += page_size) {
|
||||
const auto it = std::find_if(g_ctx->shadows.begin(), g_ctx->shadows.end(),
|
||||
[page](const Shadow& s) { return s.start == page; });
|
||||
const auto bytes = ShadowCoverage(page, min_addr, max_addr);
|
||||
const auto it = std::find_if(g_ctx->shadows.begin(), g_ctx->shadows.end(),
|
||||
[page](const Shadow& s) { return s.start == page; });
|
||||
if (it != g_ctx->shadows.end()) {
|
||||
it->count++;
|
||||
it->covered_bytes += bytes;
|
||||
} else {
|
||||
if (!Common::VirtualMemory::AllocFixed(page, page_size,
|
||||
Common::VirtualMemory::Mode::ReadWrite)) {
|
||||
@@ -78,8 +87,8 @@ static void AllocShadow(uintptr_t min_addr, uintptr_t max_addr) {
|
||||
}
|
||||
|
||||
Shadow s;
|
||||
s.start = page;
|
||||
s.count = 1;
|
||||
s.start = page;
|
||||
s.covered_bytes = bytes;
|
||||
g_ctx->shadows.push_back(s);
|
||||
}
|
||||
}
|
||||
@@ -105,14 +114,15 @@ static void FreeShadow(uintptr_t min_addr, uintptr_t max_addr) {
|
||||
const auto it = std::find_if(g_ctx->shadows.begin(), g_ctx->shadows.end(),
|
||||
[page](const Shadow& s) { return s.start == page; });
|
||||
if (it != g_ctx->shadows.end()) {
|
||||
it->count--;
|
||||
|
||||
if (it->count <= 0) {
|
||||
const auto bytes = ShadowCoverage(page, min_addr, max_addr);
|
||||
if (bytes >= it->covered_bytes) {
|
||||
if (!Common::VirtualMemory::Free(page)) {
|
||||
EXIT("can't free shadow memory\n");
|
||||
}
|
||||
|
||||
g_ctx->shadows.erase(it);
|
||||
} else {
|
||||
it->covered_bytes -= bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +136,14 @@ static void KernelFree(uintptr_t addr, size_t size) {
|
||||
FreeShadow(addr, addr + size - 1);
|
||||
}
|
||||
|
||||
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS)
|
||||
void TestRegisterMemoryCallbacks() {
|
||||
EXIT_IF(g_ctx != nullptr);
|
||||
g_ctx = new AsanContext;
|
||||
LibKernel::Memory::RegisterCallbacks(KernelAlloc, KernelFree);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void KYTY_SYSV_ABI asan_init() {
|
||||
PRINT_NAME();
|
||||
|
||||
|
||||
@@ -5686,13 +5686,13 @@ public:
|
||||
"unmapping the table kept its images pinned");
|
||||
// A smaller table enters once a second draw acquires it and leaves at the suspend point
|
||||
// after a frame that did not acquire it. Larger tables stay.
|
||||
const IR::ImageHeapRequest small{table, table_size / 2u, 32u, 0u, table_size / 64u, &sampled};
|
||||
const IR::ImageHeapRequest small_request{table, table_size / 2u, 32u, 0u, table_size / 64u, &sampled};
|
||||
Require(name, "first small draw",
|
||||
acquire(sampled, idle_table) && !heap.Acquire(small, view) &&
|
||||
!heap.Acquire(small, view),
|
||||
acquire(sampled, idle_table) && !heap.Acquire(small_request, view) &&
|
||||
!heap.Acquire(small_request, view),
|
||||
"a smaller table entered on its first draw");
|
||||
draw();
|
||||
Require(name, "second small draw", heap.Acquire(small, view),
|
||||
Require(name, "second small draw", heap.Acquire(small_request, view),
|
||||
"a smaller table was declined on its second draw");
|
||||
context.RunGarbageCollector();
|
||||
Require(name, "acquired frame", ImageHeapTestAccess::MemoryCount(heap) == 2u,
|
||||
@@ -9978,37 +9978,78 @@ public:
|
||||
d16_storage_desc.view_info.usage = vk::ImageUsageFlagBits::eStorage;
|
||||
const auto d16_storage_image =
|
||||
texture_cache.FindImage(d16_storage_desc, true);
|
||||
auto d16_storage_readback = CreateHostBuffer(
|
||||
name, sizeof(d16_fallback_values),
|
||||
vk::BufferUsageFlagBits::eTransferDst, std::vector<u32>(2, 0));
|
||||
auto &d16_storage_native = texture_cache.GetImage(d16_storage_image);
|
||||
d16_storage_native.Transit(vk::ImageLayout::eTransferSrcOptimal,
|
||||
vk::AccessFlagBits2::eTransferRead, {},
|
||||
scheduler.Current().Handle());
|
||||
vk::BufferImageCopy d16_storage_copy{};
|
||||
d16_storage_copy.bufferRowLength = 4;
|
||||
d16_storage_copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0,
|
||||
0, 1};
|
||||
d16_storage_copy.imageExtent = {4, 1, 1};
|
||||
scheduler.Current().Handle().copyImageToBuffer(
|
||||
d16_storage_native.backing.image,
|
||||
vk::ImageLayout::eTransferSrcOptimal, d16_storage_readback.buffer, 1,
|
||||
&d16_storage_copy);
|
||||
HostReadBarrier(d16_storage_readback.buffer, d16_storage_readback.size,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
scheduler.Finish();
|
||||
const auto d16_storage_words = ReadBuffer(name, d16_storage_readback, 2);
|
||||
std::array<uint16_t, 4> d16_storage_values{};
|
||||
std::memcpy(d16_storage_values.data(), d16_storage_words.data(),
|
||||
sizeof(d16_storage_values));
|
||||
const auto read_texels = [&](ImageId id, vk::ImageAspectFlagBits aspect) {
|
||||
auto readback = CreateHostBuffer(name, 4 * sizeof(u32),
|
||||
vk::BufferUsageFlagBits::eTransferDst,
|
||||
std::vector<u32>(4, 0));
|
||||
auto &image = texture_cache.GetImage(id);
|
||||
image.Transit(vk::ImageLayout::eTransferSrcOptimal,
|
||||
vk::AccessFlagBits2::eTransferRead, {},
|
||||
scheduler.Current().Handle());
|
||||
const vk::BufferImageCopy copy{0, 4, 0, {aspect, 0, 0, 1}, {}, {4, 1, 1}};
|
||||
scheduler.Current().Handle().copyImageToBuffer(
|
||||
image.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
readback.buffer, 1, ©);
|
||||
HostReadBarrier(readback.buffer, readback.size,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
scheduler.Finish();
|
||||
auto words = ReadBuffer(name, readback, 4);
|
||||
DestroyBuffer(&readback);
|
||||
return words;
|
||||
};
|
||||
const auto d16_storage_words =
|
||||
read_texels(d16_storage_image, vk::ImageAspectFlagBits::eColor);
|
||||
Require(
|
||||
name, "D16 fallback depth/storage copy",
|
||||
d16_storage_values == d16_fallback_values &&
|
||||
std::memcmp(d16_storage_words.data(), d16_fallback_values.data(),
|
||||
sizeof(d16_fallback_values)) == 0 &&
|
||||
d16_storage_image != d16_depth_image &&
|
||||
texture_cache.GetImage(d16_storage_image).IsGpuModified(),
|
||||
"D16 fallback reinterpreted four-byte host depth as two-byte color");
|
||||
DestroyBuffer(&d16_storage_readback);
|
||||
|
||||
// Dropping and re-adding the stencil plane keeps the D16 depth values.
|
||||
constexpr uint64_t d16_stenciled_offset = 0x33e000;
|
||||
constexpr std::array<uint16_t, 4> d16_stale_values{};
|
||||
std::memcpy(memory + d16_stenciled_offset, d16_fallback_values.data(),
|
||||
sizeof(d16_fallback_values));
|
||||
auto d16_stenciled_desc = d16_depth_desc;
|
||||
d16_stenciled_desc.info.data.address = base + d16_stenciled_offset;
|
||||
d16_stenciled_desc.info.stencil.address = base + 0x33f000;
|
||||
const auto d16_stenciled_image =
|
||||
texture_cache.FindImage(d16_stenciled_desc);
|
||||
(void)texture_cache.FindDepthTarget(d16_stenciled_image,
|
||||
d16_stenciled_desc);
|
||||
texture_cache.MarkGpuWritten(d16_stenciled_image);
|
||||
// Stale guest bytes: the values below can come only from image copies.
|
||||
Libs::LibKernel::Memory::WriteBacking(base + d16_stenciled_offset,
|
||||
d16_stale_values.data(),
|
||||
sizeof(d16_stale_values));
|
||||
auto d16_native_desc = d16_stenciled_desc;
|
||||
d16_native_desc.info.stencil = {};
|
||||
d16_native_desc.info.pixel_format = vk::Format::eD16Unorm;
|
||||
d16_native_desc.view_info.format = vk::Format::eD16Unorm;
|
||||
d16_native_desc.view_info.aspect = vk::ImageAspectFlagBits::eDepth;
|
||||
const auto d16_native_image = texture_cache.FindImage(d16_native_desc);
|
||||
const auto d16_native_words =
|
||||
read_texels(d16_native_image, vk::ImageAspectFlagBits::eDepth);
|
||||
Require(name, "D16 fallback depth/native depth copy",
|
||||
std::memcmp(d16_native_words.data(), d16_fallback_values.data(),
|
||||
sizeof(d16_fallback_values)) == 0 &&
|
||||
texture_cache.GetImage(d16_native_image).backing.format ==
|
||||
vk::Format::eD16Unorm,
|
||||
"D16 depth lost its values when its target dropped the stencil");
|
||||
const auto d16_restenciled_image =
|
||||
texture_cache.FindImage(d16_stenciled_desc);
|
||||
const auto d16_restenciled_words =
|
||||
read_texels(d16_restenciled_image, vk::ImageAspectFlagBits::eDepth);
|
||||
bool d16_promoted = d16_restenciled_image != d16_native_image;
|
||||
for (size_t i = 0; i < d16_fallback_values.size(); ++i) {
|
||||
d16_promoted &= (d16_restenciled_words[i] & 0x00ffffffu) ==
|
||||
EncodeD16AsD24(d16_fallback_values[i]);
|
||||
}
|
||||
Require(name, "D16 native depth/fallback depth copy", d16_promoted,
|
||||
"D16 depth lost its values when its target regained the stencil");
|
||||
|
||||
if (!texture_cache.GetImage(combined_destination_image).IsGpuModified()) {
|
||||
texture_cache.MarkGpuWritten(combined_destination_image);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user