early-access version 4084
This commit is contained in:
parent
9b38babf6f
commit
9ee9c111ff
@ -1,7 +1,7 @@
|
|||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 4083.
|
This is the source code for early-access 4084.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ add_library(common STATIC
|
|||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
quaternion.h
|
quaternion.h
|
||||||
range_map.h
|
range_map.h
|
||||||
|
range_mutex.h
|
||||||
reader_writer_queue.h
|
reader_writer_queue.h
|
||||||
ring_buffer.h
|
ring_buffer.h
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp
|
||||||
|
93
src/common/range_mutex.h
Executable file
93
src/common/range_mutex.h
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "common/intrusive_list.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
class ScopedRangeLock;
|
||||||
|
|
||||||
|
class RangeMutex {
|
||||||
|
public:
|
||||||
|
explicit RangeMutex() = default;
|
||||||
|
~RangeMutex() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ScopedRangeLock;
|
||||||
|
|
||||||
|
void Lock(ScopedRangeLock& l);
|
||||||
|
void Unlock(ScopedRangeLock& l);
|
||||||
|
bool HasIntersectionLocked(ScopedRangeLock& l);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::condition_variable m_cv;
|
||||||
|
|
||||||
|
using LockList = Common::IntrusiveListBaseTraits<ScopedRangeLock>::ListType;
|
||||||
|
LockList m_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedRangeLock : public Common::IntrusiveListBaseNode<ScopedRangeLock> {
|
||||||
|
public:
|
||||||
|
explicit ScopedRangeLock(RangeMutex& mutex, u64 address, u64 size)
|
||||||
|
: m_mutex(mutex), m_address(address), m_size(size) {
|
||||||
|
if (m_size > 0) {
|
||||||
|
m_mutex.Lock(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~ScopedRangeLock() {
|
||||||
|
if (m_size > 0) {
|
||||||
|
m_mutex.Unlock(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetAddress() const {
|
||||||
|
return m_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetSize() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RangeMutex& m_mutex;
|
||||||
|
const u64 m_address{};
|
||||||
|
const u64 m_size{};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void RangeMutex::Lock(ScopedRangeLock& l) {
|
||||||
|
std::unique_lock lk{m_mutex};
|
||||||
|
m_cv.wait(lk, [&] { return !HasIntersectionLocked(l); });
|
||||||
|
m_list.push_back(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RangeMutex::Unlock(ScopedRangeLock& l) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{m_mutex};
|
||||||
|
m_list.erase(m_list.iterator_to(l));
|
||||||
|
}
|
||||||
|
m_cv.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool RangeMutex::HasIntersectionLocked(ScopedRangeLock& l) {
|
||||||
|
const auto cur_begin = l.GetAddress();
|
||||||
|
const auto cur_last = l.GetAddress() + l.GetSize() - 1;
|
||||||
|
|
||||||
|
for (const auto& other : m_list) {
|
||||||
|
const auto other_begin = other.GetAddress();
|
||||||
|
const auto other_last = other.GetAddress() + other.GetSize() - 1;
|
||||||
|
|
||||||
|
if (cur_begin <= other_last && other_begin <= cur_last) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
@ -420,8 +420,15 @@ struct Values {
|
|||||||
SwitchableSetting<s64> custom_rtc{
|
SwitchableSetting<s64> custom_rtc{
|
||||||
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
|
linkage, 0, "custom_rtc", Category::System, Specialization::Time,
|
||||||
false, true, &custom_rtc_enabled};
|
false, true, &custom_rtc_enabled};
|
||||||
SwitchableSetting<s64, false> custom_rtc_offset{
|
SwitchableSetting<s64, true> custom_rtc_offset{linkage,
|
||||||
linkage, 0, "custom_rtc_offset", Category::System, Specialization::Countable, true, true};
|
0,
|
||||||
|
std::numeric_limits<int>::min(),
|
||||||
|
std::numeric_limits<int>::max(),
|
||||||
|
"custom_rtc_offset",
|
||||||
|
Category::System,
|
||||||
|
Specialization::Countable,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
SwitchableSetting<bool> rng_seed_enabled{
|
SwitchableSetting<bool> rng_seed_enabled{
|
||||||
linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
|
linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
|
||||||
SwitchableSetting<u32> rng_seed{
|
SwitchableSetting<u32> rng_seed{
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/range_mutex.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
@ -204,7 +205,7 @@ private:
|
|||||||
(1ULL << (device_virtual_bits - page_bits)) / subentries;
|
(1ULL << (device_virtual_bits - page_bits)) / subentries;
|
||||||
using CachedPages = std::array<CounterEntry, num_counter_entries>;
|
using CachedPages = std::array<CounterEntry, num_counter_entries>;
|
||||||
std::unique_ptr<CachedPages> cached_pages;
|
std::unique_ptr<CachedPages> cached_pages;
|
||||||
std::mutex counter_guard;
|
Common::RangeMutex counter_guard;
|
||||||
std::mutex mapping_guard;
|
std::mutex mapping_guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,9 +31,8 @@ public:
|
|||||||
buffer.resize(0);
|
buffer.resize(0);
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
const auto add_value = [&](u32 value) {
|
const auto add_value = [&](u32 value) {
|
||||||
buffer[index] = value;
|
buffer.resize(index + 1);
|
||||||
index++;
|
buffer[index++] = value;
|
||||||
buffer.resize(index);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 iter_entry = start_entry;
|
u32 iter_entry = start_entry;
|
||||||
@ -509,12 +508,7 @@ void DeviceMemoryManager<Traits>::UnregisterProcess(Asid asid) {
|
|||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
|
void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size, s32 delta) {
|
||||||
std::unique_lock<std::mutex> lk(counter_guard, std::defer_lock);
|
Common::ScopedRangeLock lk(counter_guard, addr, size);
|
||||||
const auto Lock = [&] {
|
|
||||||
if (!lk) {
|
|
||||||
lk.lock();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
u64 uncache_begin = 0;
|
u64 uncache_begin = 0;
|
||||||
u64 cache_begin = 0;
|
u64 cache_begin = 0;
|
||||||
u64 uncache_bytes = 0;
|
u64 uncache_bytes = 0;
|
||||||
@ -549,7 +543,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
|||||||
}
|
}
|
||||||
uncache_bytes += Memory::YUZU_PAGESIZE;
|
uncache_bytes += Memory::YUZU_PAGESIZE;
|
||||||
} else if (uncache_bytes > 0) {
|
} else if (uncache_bytes > 0) {
|
||||||
Lock();
|
|
||||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
|
||||||
uncache_bytes, false);
|
uncache_bytes, false);
|
||||||
uncache_bytes = 0;
|
uncache_bytes = 0;
|
||||||
@ -560,7 +553,6 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
|||||||
}
|
}
|
||||||
cache_bytes += Memory::YUZU_PAGESIZE;
|
cache_bytes += Memory::YUZU_PAGESIZE;
|
||||||
} else if (cache_bytes > 0) {
|
} else if (cache_bytes > 0) {
|
||||||
Lock();
|
|
||||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
||||||
true);
|
true);
|
||||||
cache_bytes = 0;
|
cache_bytes = 0;
|
||||||
@ -568,12 +560,10 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
|||||||
vpage++;
|
vpage++;
|
||||||
}
|
}
|
||||||
if (uncache_bytes > 0) {
|
if (uncache_bytes > 0) {
|
||||||
Lock();
|
|
||||||
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
|
MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, uncache_bytes,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
if (cache_bytes > 0) {
|
if (cache_bytes > 0) {
|
||||||
Lock();
|
|
||||||
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, cache_bytes,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
|
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
|
||||||
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id)
|
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
|
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
|
||||||
|
|
||||||
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
|
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
|
||||||
|
|
||||||
if (desc.supplier == nullptr)
|
if (desc.supplier == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const auto dir = desc.supplier();
|
const auto dir = desc.supplier();
|
||||||
|
|
||||||
if (dir == nullptr)
|
if (dir == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const auto romfs = CreateRomFS(dir);
|
const auto romfs = CreateRomFS(dir);
|
||||||
|
|
||||||
if (romfs == nullptr)
|
if (romfs == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFO(Service_FS, " - System archive generation successful!");
|
LOG_INFO(Service_FS, " - System archive generation successful!");
|
||||||
return romfs;
|
return romfs;
|
||||||
|
@ -89,7 +89,8 @@ Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationN
|
|||||||
std::min(configured_name.name.size(), configured_zone.size()));
|
std::min(configured_name.name.size(), configured_zone.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone!");
|
ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
|
||||||
|
configured_name.name.data());
|
||||||
|
|
||||||
return configured_name;
|
return configured_name;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,16 @@ Result MountTimeZoneBinary(Core::System& system) {
|
|||||||
g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
|
g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_time_zone_binary_romfs) {
|
||||||
|
// Validate that the romfs is readable, using invalid firmware keys can cause this to get
|
||||||
|
// set but the files to be garbage. In that case, we want to hit the next path and
|
||||||
|
// synthesise them instead.
|
||||||
|
Service::PSC::Time::LocationName name{"Etc/GMT"};
|
||||||
|
if (!IsTimeZoneBinaryValid(name)) {
|
||||||
|
ResetTimeZoneBinary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!g_time_zone_binary_romfs) {
|
if (!g_time_zone_binary_romfs) {
|
||||||
g_time_zone_binary_romfs = FileSys::ExtractRomFS(
|
g_time_zone_binary_romfs = FileSys::ExtractRomFS(
|
||||||
FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
|
FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
|
||||||
@ -102,6 +112,7 @@ bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
|
|||||||
|
|
||||||
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
|
auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
|
||||||
if (!vfs_file) {
|
if (!vfs_file) {
|
||||||
|
LOG_INFO(Service_Time, "Could not find timezone file {}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return vfs_file->GetSize() != 0;
|
return vfs_file->GetSize() != 0;
|
||||||
|
@ -1093,6 +1093,20 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
|
|||||||
[&] { rasterizer = true; });
|
[&] { rasterizer = true; });
|
||||||
if (rasterizer) {
|
if (rasterizer) {
|
||||||
impl->InvalidateGPUMemory(ptr, size);
|
impl->InvalidateGPUMemory(ptr, size);
|
||||||
|
|
||||||
|
const auto type = impl->current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type();
|
||||||
|
if (type == Common::PageType::RasterizerCachedMemory) {
|
||||||
|
// Check if device mapped. If not, this bugged and we can unmark.
|
||||||
|
DAddr addr{};
|
||||||
|
Common::ScratchBuffer<u32> buffer;
|
||||||
|
impl->gpu_device_memory->ApplyOpOnPointer(ptr, buffer,
|
||||||
|
[&](DAddr address) { addr = address; });
|
||||||
|
|
||||||
|
if (addr == 0) {
|
||||||
|
LOG_ERROR(HW_Memory, "Fixing unmapped cached region {:#x}", GetInteger(vaddr));
|
||||||
|
impl->RasterizerMarkRegionCached(GetInteger(vaddr), size, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -1546,7 +1546,10 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
std::span<const u8> upload_span;
|
std::span<const u8> upload_span;
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||||
if (IsRangeGranular(device_addr, copy.size)) {
|
if (IsRangeGranular(device_addr, copy.size)) {
|
||||||
upload_span = std::span(device_memory.GetPointer<u8>(device_addr), copy.size);
|
auto* const ptr = device_memory.GetPointer<u8>(device_addr);
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
upload_span = std::span(ptr, copy.size);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (immediate_buffer.empty()) {
|
if (immediate_buffer.empty()) {
|
||||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||||
|
@ -243,10 +243,12 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
|
|||||||
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
|
const u64 size_in_bytes{Tegra::Texture::CalculateSize(
|
||||||
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)};
|
||||||
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
|
const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)};
|
||||||
const std::span<const u8> input_data(host_ptr, size_in_bytes);
|
if (host_ptr != nullptr) {
|
||||||
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
|
const std::span<const u8> input_data(host_ptr, size_in_bytes);
|
||||||
framebuffer.width, framebuffer.height, 1, block_height_log2,
|
Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel,
|
||||||
0);
|
framebuffer.width, framebuffer.height, 1,
|
||||||
|
block_height_log2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride));
|
||||||
|
@ -230,9 +230,11 @@ void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
|
|||||||
const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
|
const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
|
||||||
framebuffer.stride, framebuffer.height,
|
framebuffer.stride, framebuffer.height,
|
||||||
1, block_height_log2, 0)};
|
1, block_height_log2, 0)};
|
||||||
Tegra::Texture::UnswizzleTexture(
|
if (host_ptr != nullptr) {
|
||||||
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
Tegra::Texture::UnswizzleTexture(
|
||||||
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size),
|
||||||
|
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const VkBufferImageCopy copy{
|
const VkBufferImageCopy copy{
|
||||||
.bufferOffset = image_offset,
|
.bufferOffset = image_offset,
|
||||||
|
@ -1064,8 +1064,6 @@ public:
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
auto* ptr = device_memory.GetPointer<u8>(new_query->dependant_address);
|
|
||||||
ASSERT(ptr != nullptr);
|
|
||||||
|
|
||||||
new_query->dependant_manage = must_manage_dependance;
|
new_query->dependant_manage = must_manage_dependance;
|
||||||
pending_flush_queries.push_back(index);
|
pending_flush_queries.push_back(index);
|
||||||
@ -1104,9 +1102,11 @@ public:
|
|||||||
tfb_streamer.Free(query->dependant_index);
|
tfb_streamer.Free(query->dependant_index);
|
||||||
} else {
|
} else {
|
||||||
u8* pointer = device_memory.GetPointer<u8>(query->dependant_address);
|
u8* pointer = device_memory.GetPointer<u8>(query->dependant_address);
|
||||||
u32 result;
|
if (pointer != nullptr) {
|
||||||
std::memcpy(&result, pointer, sizeof(u32));
|
u32 result;
|
||||||
num_vertices = static_cast<u64>(result) / query->stride;
|
std::memcpy(&result, pointer, sizeof(u32));
|
||||||
|
num_vertices = static_cast<u64>(result) / query->stride;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
query->value = [&]() -> u64 {
|
query->value = [&]() -> u64 {
|
||||||
switch (query->topology) {
|
switch (query->topology) {
|
||||||
@ -1360,7 +1360,9 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
|
|||||||
const auto check_value = [&](DAddr address) {
|
const auto check_value = [&](DAddr address) {
|
||||||
u8* ptr = impl->device_memory.GetPointer<u8>(address);
|
u8* ptr = impl->device_memory.GetPointer<u8>(address);
|
||||||
u64 value{};
|
u64 value{};
|
||||||
std::memcpy(&value, ptr, sizeof(value));
|
if (ptr != nullptr) {
|
||||||
|
std::memcpy(&value, ptr, sizeof(value));
|
||||||
|
}
|
||||||
return value == 0;
|
return value == 0;
|
||||||
};
|
};
|
||||||
std::array<VideoCommon::LookupData*, 2> objects{&object_1, &object_2};
|
std::array<VideoCommon::LookupData*, 2> objects{&object_1, &object_2};
|
||||||
|
Loading…
Reference in New Issue
Block a user