mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02fef4418d | |||
| 064ab33c08 | |||
| 68e1048194 | |||
| 024f7d4918 | |||
| 96fc3378a4 | |||
| 4088d44789 | |||
| 8be2e5dfd8 | |||
| a042cedc6b | |||
| c65694e3ec | |||
| b94ec40989 | |||
| 357e2e7109 | |||
| 930989ab82 | |||
| 8cbe8d1ce9 | |||
| ff596b6e8a | |||
| 8d2d61bd8c | |||
| d0b135afb5 | |||
| 159841a6fc | |||
| 2ea449f71a | |||
| 602756e990 | |||
| 34f9c0fdec | |||
| faaf1bac64 |
-1
@@ -27,7 +27,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
||||||
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
||||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||||
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
|
|
||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
|
|||||||
-7
@@ -789,13 +789,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.renderer_asynchronous_shaders_description
|
descriptionId = R.string.renderer_asynchronous_shaders_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.RENDERER_UNIFIED_MEMORY,
|
|
||||||
titleId = R.string.renderer_unified_memory,
|
|
||||||
descriptionId = R.string.renderer_unified_memory_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SingleChoiceSetting(
|
SingleChoiceSetting(
|
||||||
IntSetting.FAST_GPU_TIME,
|
IntSetting.FAST_GPU_TIME,
|
||||||
|
|||||||
-1
@@ -344,7 +344,6 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||||
add(BooleanSetting.EMULATE_BGR565.key)
|
add(BooleanSetting.EMULATE_BGR565.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
|
|
||||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
||||||
|
|||||||
@@ -585,8 +585,6 @@
|
|||||||
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
|
||||||
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
|
||||||
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
|
||||||
<string name="renderer_unified_memory">Unified memory access</string>
|
|
||||||
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
|
|
||||||
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
|
||||||
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
|
||||||
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
|
||||||
|
|||||||
+28
-22
@@ -4,17 +4,34 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include "common/heap_tracker.h"
|
#include "common/heap_tracker.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/memory_detect.h"
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
s64 GetMaxPermissibleResidentMapCount() {
|
||||||
|
// Default value.
|
||||||
|
s64 value = 65530;
|
||||||
|
|
||||||
|
// Try to read how many mappings we can make.
|
||||||
|
std::ifstream s("/proc/sys/vm/max_map_count");
|
||||||
|
s >> value;
|
||||||
|
|
||||||
|
// Print, for debug.
|
||||||
|
LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
|
||||||
|
|
||||||
|
// Allow 20000 maps for other code and to account for split inaccuracy.
|
||||||
|
return std::max<s64>(value - 20000, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
HeapTracker::HeapTracker(Common::HostMemory& buffer)
|
||||||
: m_buffer(buffer),
|
: m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
|
||||||
m_has_hardware_buffer_backing(!buffer.BackingHardwareBuffers().empty()),
|
|
||||||
m_max_resident_map_count(static_cast<s64>(GetPermissibleMapCount())) {}
|
|
||||||
HeapTracker::~HeapTracker() = default;
|
HeapTracker::~HeapTracker() = default;
|
||||||
|
|
||||||
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
|
||||||
@@ -68,8 +85,7 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
|
|||||||
|
|
||||||
// If resident, erase from resident map.
|
// If resident, erase from resident map.
|
||||||
if (item->is_resident) {
|
if (item->is_resident) {
|
||||||
m_resident_map_count -= this->HostMapCount(item->paddr, item->size);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
ASSERT(m_resident_map_count >= 0);
|
|
||||||
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +191,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
|
|||||||
|
|
||||||
// This map is now resident.
|
// This map is now resident.
|
||||||
it->is_resident = true;
|
it->is_resident = true;
|
||||||
m_resident_map_count += this->HostMapCount(it->paddr, it->size);
|
m_resident_map_count++;
|
||||||
m_resident_mappings.insert(*it);
|
m_resident_mappings.insert(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,17 +213,17 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
|
|||||||
// Despite being worse in theory, this has proven to be better in practice than more
|
// Despite being worse in theory, this has proven to be better in practice than more
|
||||||
// regularly dumping a smaller amount, because it significantly reduces average case
|
// regularly dumping a smaller amount, because it significantly reduces average case
|
||||||
// lock contention.
|
// lock contention.
|
||||||
s64 const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
std::size_t const desired_count = (std::min)(m_resident_map_count, m_max_resident_map_count) / 2;
|
||||||
|
std::size_t const evict_count = m_resident_map_count - desired_count;
|
||||||
auto it = m_resident_mappings.begin();
|
auto it = m_resident_mappings.begin();
|
||||||
|
|
||||||
while (m_resident_map_count > desired_count && it != m_resident_mappings.end()) {
|
for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
|
||||||
// Unmark and unmap.
|
// Unmark and unmap.
|
||||||
it->is_resident = false;
|
it->is_resident = false;
|
||||||
m_buffer.Unmap(it->vaddr, it->size, false);
|
m_buffer.Unmap(it->vaddr, it->size, false);
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
m_resident_map_count -= this->HostMapCount(it->paddr, it->size);
|
ASSERT(--m_resident_map_count >= 0);
|
||||||
ASSERT(m_resident_map_count >= 0);
|
|
||||||
it = m_resident_mappings.erase(it);
|
it = m_resident_mappings.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +245,6 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
// Cache the original values.
|
// Cache the original values.
|
||||||
auto* const left = std::addressof(*it);
|
auto* const left = std::addressof(*it);
|
||||||
const size_t orig_size = left->size;
|
const size_t orig_size = left->size;
|
||||||
const s64 orig_host_map_count = this->HostMapCount(left->paddr, orig_size);
|
|
||||||
|
|
||||||
// Adjust the left map.
|
// Adjust the left map.
|
||||||
const size_t left_size = offset - left->vaddr;
|
const size_t left_size = offset - left->vaddr;
|
||||||
@@ -251,20 +266,11 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
|
|||||||
|
|
||||||
// If resident, also insert into resident map.
|
// If resident, also insert into resident map.
|
||||||
if (right->is_resident) {
|
if (right->is_resident) {
|
||||||
m_resident_map_count += this->HostMapCount(left->paddr, left->size) +
|
m_resident_map_count++;
|
||||||
this->HostMapCount(right->paddr, right->size) -
|
|
||||||
orig_host_map_count;
|
|
||||||
m_resident_mappings.insert(*right);
|
m_resident_mappings.insert(*right);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 HeapTracker::HostMapCount(PAddr paddr, size_t size) const {
|
|
||||||
if (!m_has_hardware_buffer_backing) {
|
|
||||||
return size != 0 ? 1 : 0;
|
|
||||||
}
|
|
||||||
return static_cast<s64>(m_buffer.BackingMapCount(paddr, size));
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
|
||||||
const SeparateHeapMap key{
|
const SeparateHeapMap key{
|
||||||
.vaddr = offset,
|
.vaddr = offset,
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -85,13 +82,10 @@ private:
|
|||||||
|
|
||||||
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
|
||||||
|
|
||||||
s64 HostMapCount(PAddr paddr, size_t size) const;
|
|
||||||
|
|
||||||
void RebuildSeparateHeapAddressSpace();
|
void RebuildSeparateHeapAddressSpace();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Common::HostMemory& m_buffer;
|
Common::HostMemory& m_buffer;
|
||||||
const bool m_has_hardware_buffer_backing;
|
|
||||||
const s64 m_max_resident_map_count;
|
const s64 m_max_resident_map_count;
|
||||||
|
|
||||||
std::shared_mutex m_rebuild_lock{};
|
std::shared_mutex m_rebuild_lock{};
|
||||||
|
|||||||
+6
-393
@@ -51,65 +51,14 @@
|
|||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/free_region_manager.h"
|
#include "common/free_region_manager.h"
|
||||||
#include "common/host_memory.h"
|
#include "common/host_memory.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/memory_detect.h"
|
|
||||||
#include "common/settings.h"
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include <cerrno>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <android/hardware_buffer.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct NativeHandle {
|
|
||||||
int version;
|
|
||||||
int numFds;
|
|
||||||
int numInts;
|
|
||||||
int data[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
|
|
||||||
|
|
||||||
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
|
|
||||||
void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
|
|
||||||
if (lib == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
|
|
||||||
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DmaBufSync {
|
|
||||||
u64 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u64 DmaBufSyncRead = 1ULL << 0;
|
|
||||||
constexpr u64 DmaBufSyncWrite = 1ULL << 1;
|
|
||||||
constexpr u64 DmaBufSyncStart = 0ULL << 2;
|
|
||||||
constexpr u64 DmaBufSyncEnd = 1ULL << 2;
|
|
||||||
|
|
||||||
void SyncDmaBufCpuAccess(int fd, u64 phase) {
|
|
||||||
DmaBufSync sync{.flags = phase | DmaBufSyncRead | DmaBufSyncWrite};
|
|
||||||
while (ioctl(fd, _IOW('b', 0, DmaBufSync), &sync) != 0) {
|
|
||||||
if (errno != EINTR) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
#if defined(__ANDROID__) && __ANDROID_API__ < 30
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
@@ -126,12 +75,6 @@ namespace Common {
|
|||||||
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
|
||||||
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
|
||||||
|
|
||||||
static std::atomic<u64> committed_backing_size{};
|
|
||||||
|
|
||||||
u64 GetCommittedBackingSize() noexcept {
|
|
||||||
return committed_backing_size.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
// Manually imported for MinGW compatibility
|
// Manually imported for MinGW compatibility
|
||||||
@@ -180,7 +123,7 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
, process{GetCurrentProcess()}
|
, process{GetCurrentProcess()}
|
||||||
@@ -286,10 +229,6 @@ public:
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
|
|
||||||
@@ -562,10 +501,9 @@ static int shm_open_anon(int flags, mode_t mode) {
|
|||||||
|
|
||||||
class HostMemory::Impl {
|
class HostMemory::Impl {
|
||||||
public:
|
public:
|
||||||
explicit Impl(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
explicit Impl(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size{backing_size_}
|
: backing_size{backing_size_}
|
||||||
, virtual_size{virtual_size_}
|
, virtual_size{virtual_size_}
|
||||||
, preferred_offset{preferred_offset_}
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool Init() {
|
bool Init() {
|
||||||
@@ -605,15 +543,10 @@ public:
|
|||||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||||
if (fd > 0) {
|
if (fd > 0) {
|
||||||
|
fd = -1;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
fd = -1;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (InitAhbBacking()) {
|
|
||||||
return InitVirtual();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||||
}
|
}
|
||||||
if (backing_base == MAP_FAILED) {
|
if (backing_base == MAP_FAILED) {
|
||||||
@@ -621,10 +554,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return InitVirtual();
|
// Virtual memory initialization
|
||||||
}
|
|
||||||
|
|
||||||
bool InitVirtual() {
|
|
||||||
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
|
||||||
if (virtual_base == MAP_FAILED) {
|
if (virtual_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||||
@@ -637,244 +567,6 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
|
|
||||||
return AHardwareBuffer_Desc{
|
|
||||||
.width = static_cast<u32>(len),
|
|
||||||
.height = 1,
|
|
||||||
.layers = 1,
|
|
||||||
.format = AHARDWAREBUFFER_FORMAT_BLOB,
|
|
||||||
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
|
||||||
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
|
|
||||||
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
|
|
||||||
.stride = 0,
|
|
||||||
.rfu0 = 0,
|
|
||||||
.rfu1 = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
|
|
||||||
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
|
|
||||||
AHardwareBuffer* buffer{};
|
|
||||||
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const NativeHandle* const handle = get_native_handle(buffer);
|
|
||||||
if (handle == nullptr || handle->numFds < 1) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const int probe_fd = handle->data[0];
|
|
||||||
bool ok = true;
|
|
||||||
const auto try_map = [&](int prot, off_t offset) {
|
|
||||||
if (!ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
|
|
||||||
if (ptr == MAP_FAILED) {
|
|
||||||
ok = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
munmap(ptr, PageAlignment);
|
|
||||||
};
|
|
||||||
try_map(PROT_READ | PROT_WRITE, 0);
|
|
||||||
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment));
|
|
||||||
#ifdef ARCHITECTURE_arm64
|
|
||||||
try_map(PROT_READ | PROT_EXEC, 0);
|
|
||||||
#endif
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ComputeAhbBudget(size_t window_size) const {
|
|
||||||
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
|
|
||||||
constexpr u64 BaselineFootprint = 6ULL << 30;
|
|
||||||
if (total_physical <= BaselineFootprint) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const u64 permissible_maps = Common::GetPermissibleMapCount();
|
|
||||||
if (permissible_maps == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
u64 budget = (total_physical - BaselineFootprint) / 2;
|
|
||||||
constexpr u64 MapSlotsPerWindow = 64;
|
|
||||||
const u64 affordable_windows = permissible_maps / MapSlotsPerWindow;
|
|
||||||
budget = (std::min)(budget, affordable_windows * window_size);
|
|
||||||
const u64 available = Common::GetAvailablePhysicalMemory();
|
|
||||||
if (available != 0) {
|
|
||||||
budget = (std::min)(budget, available / 2);
|
|
||||||
}
|
|
||||||
budget = (std::min)(budget, static_cast<u64>(backing_size));
|
|
||||||
budget = Common::AlignDown(budget, window_size);
|
|
||||||
constexpr u64 MinimumBudget = 256ULL << 20;
|
|
||||||
if (budget < MinimumBudget) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<size_t>(budget);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InitAhbBacking() {
|
|
||||||
if (!Settings::values.use_unified_memory.GetValue()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
|
|
||||||
ResolveGetNativeHandle();
|
|
||||||
if (get_native_handle == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
constexpr size_t window_size = 256ULL << 20;
|
|
||||||
const size_t budget = ComputeAhbBudget(window_size);
|
|
||||||
if (budget == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!ProbeAhbBacking(get_native_handle)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t aligned_backing = Common::AlignDown(backing_size, window_size);
|
|
||||||
const size_t max_windows = (std::min)(budget, aligned_backing) / window_size;
|
|
||||||
|
|
||||||
std::vector<AHardwareBuffer*> buffers;
|
|
||||||
std::vector<int> buffer_fds;
|
|
||||||
const auto cleanup = [&] {
|
|
||||||
for (AHardwareBuffer* buffer : buffers) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
}
|
|
||||||
buffers.clear();
|
|
||||||
buffer_fds.clear();
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < max_windows; ++i) {
|
|
||||||
const AHardwareBuffer_Desc desc = MakeBlobDesc(window_size);
|
|
||||||
AHardwareBuffer* buffer{};
|
|
||||||
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const NativeHandle* const handle = get_native_handle(buffer);
|
|
||||||
if (handle == nullptr || handle->numFds < 1) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const int buffer_fd = handle->data[0];
|
|
||||||
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
|
|
||||||
if (buffer_len < static_cast<off_t>(window_size)) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffers.push_back(buffer);
|
|
||||||
buffer_fds.push_back(buffer_fd);
|
|
||||||
}
|
|
||||||
const size_t num_windows = buffers.size();
|
|
||||||
if (num_windows == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t region_size = num_windows * window_size;
|
|
||||||
const size_t region_base = Common::AlignDown(
|
|
||||||
(std::min)(preferred_offset, aligned_backing - region_size), window_size);
|
|
||||||
u8* const base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
|
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
|
|
||||||
if (base == MAP_FAILED) {
|
|
||||||
cleanup();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto map_over_reservation = [&](size_t offset, size_t len, int map_fd,
|
|
||||||
off_t map_offset) {
|
|
||||||
if (len == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (mmap(base + offset, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, map_fd,
|
|
||||||
map_offset) == MAP_FAILED) {
|
|
||||||
munmap(base, backing_size);
|
|
||||||
cleanup();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if (!map_over_reservation(0, region_base, fd, 0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < num_windows; ++i) {
|
|
||||||
if (!map_over_reservation(region_base + i * window_size, window_size, buffer_fds[i],
|
|
||||||
0)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const size_t tail_offset = region_base + region_size;
|
|
||||||
if (!map_over_reservation(tail_offset, backing_size - tail_offset, fd,
|
|
||||||
static_cast<off_t>(tail_offset))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
backing_base = base;
|
|
||||||
ahb_windows = std::move(buffers);
|
|
||||||
ahb_fds = std::move(buffer_fds);
|
|
||||||
ahb_window_size = window_size;
|
|
||||||
ahb_base = region_base;
|
|
||||||
ahb_bytes = region_size;
|
|
||||||
committed_backing_size.store(region_size, std::memory_order_relaxed);
|
|
||||||
for (const int window_fd : ahb_fds) {
|
|
||||||
SyncDmaBufCpuAccess(window_fd, DmaBufSyncStart);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapBackingRange(size_t virtual_offset, size_t host_offset, size_t length, int prot_flags) {
|
|
||||||
while (length > 0) {
|
|
||||||
int map_fd = fd;
|
|
||||||
off_t map_offset = static_cast<off_t>(host_offset);
|
|
||||||
size_t chunk = length;
|
|
||||||
if (host_offset < ahb_base) {
|
|
||||||
chunk = (std::min)(chunk, ahb_base - host_offset);
|
|
||||||
} else if (host_offset < ahb_base + ahb_bytes) {
|
|
||||||
const size_t relative = host_offset - ahb_base;
|
|
||||||
const size_t window = relative / ahb_window_size;
|
|
||||||
const size_t local = relative % ahb_window_size;
|
|
||||||
map_fd = ahb_fds[window];
|
|
||||||
map_offset = static_cast<off_t>(local);
|
|
||||||
chunk = (std::min)(chunk, ahb_window_size - local);
|
|
||||||
}
|
|
||||||
void* const ret = mmap(virtual_base + virtual_offset, chunk, prot_flags,
|
|
||||||
MAP_SHARED | MAP_FIXED, map_fd, map_offset);
|
|
||||||
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
|
|
||||||
virtual_offset += chunk;
|
|
||||||
host_offset += chunk;
|
|
||||||
length -= chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
|
||||||
if (length == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (ahb_bytes == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t count = 0;
|
|
||||||
while (length > 0) {
|
|
||||||
size_t chunk = length;
|
|
||||||
if (host_offset < ahb_base) {
|
|
||||||
chunk = (std::min)(chunk, ahb_base - host_offset);
|
|
||||||
} else if (host_offset < ahb_base + ahb_bytes) {
|
|
||||||
const size_t local = (host_offset - ahb_base) % ahb_window_size;
|
|
||||||
chunk = (std::min)(chunk, ahb_window_size - local);
|
|
||||||
}
|
|
||||||
host_offset += chunk;
|
|
||||||
length -= chunk;
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
|
|
||||||
return ahb_windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AhbWindowSize() const noexcept {
|
|
||||||
return ahb_bytes != 0 ? ahb_window_size : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AhbBase() const noexcept {
|
|
||||||
return ahb_base;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
Release();
|
Release();
|
||||||
}
|
}
|
||||||
@@ -895,12 +587,6 @@ public:
|
|||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
if (True(perms & MemoryPermission::Execute))
|
if (True(perms & MemoryPermission::Execute))
|
||||||
prot_flags |= PROT_EXEC;
|
prot_flags |= PROT_EXEC;
|
||||||
#endif
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
MapBackingRange(virtual_offset, host_offset, length, prot_flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
|
||||||
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
|
||||||
@@ -946,18 +632,8 @@ public:
|
|||||||
virtual_base = nullptr;
|
virtual_base = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return fd >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t backing_size; ///< Size of the backing memory in bytes
|
const size_t backing_size; ///< Size of the backing memory in bytes
|
||||||
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
const size_t virtual_size; ///< Size of the virtual address placeholder in bytes
|
||||||
const size_t preferred_offset;
|
|
||||||
|
|
||||||
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* backing_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
u8* virtual_base{reinterpret_cast<u8*>(MAP_FAILED)};
|
||||||
@@ -980,21 +656,6 @@ private:
|
|||||||
int ret = close(fd);
|
int ret = close(fd);
|
||||||
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
for (const int window_fd : ahb_fds) {
|
|
||||||
SyncDmaBufCpuAccess(window_fd, DmaBufSyncEnd);
|
|
||||||
}
|
|
||||||
for (AHardwareBuffer* buffer : ahb_windows) {
|
|
||||||
AHardwareBuffer_release(buffer);
|
|
||||||
}
|
|
||||||
ahb_windows.clear();
|
|
||||||
ahb_fds.clear();
|
|
||||||
if (ahb_bytes != 0) {
|
|
||||||
committed_backing_size.store(0, std::memory_order_relaxed);
|
|
||||||
ahb_bytes = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
void AdjustMap(size_t* virtual_offset, size_t* length) {
|
||||||
@@ -1020,19 +681,11 @@ private:
|
|||||||
|
|
||||||
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
|
||||||
FreeRegionManager free_manager{};
|
FreeRegionManager free_manager{};
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
std::vector<AHardwareBuffer*> ahb_windows;
|
|
||||||
std::vector<int> ahb_fds;
|
|
||||||
size_t ahb_window_size{};
|
|
||||||
size_t ahb_base{};
|
|
||||||
size_t ahb_bytes{};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ^^^ POSIX ^^^
|
#endif // ^^^ POSIX ^^^
|
||||||
|
|
||||||
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_)
|
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||||
: backing_size(backing_size_)
|
: backing_size(backing_size_)
|
||||||
, virtual_size(virtual_size_)
|
, virtual_size(virtual_size_)
|
||||||
{
|
{
|
||||||
@@ -1044,7 +697,7 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_, size_t prefer
|
|||||||
#else
|
#else
|
||||||
// Try to allocate a fastmem arena.
|
// Try to allocate a fastmem arena.
|
||||||
// The implementation will fail with std::bad_alloc on errors.
|
// The implementation will fail with std::bad_alloc on errors.
|
||||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize, preferred_offset_);
|
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||||
if (impl->Init()) {
|
if (impl->Init()) {
|
||||||
backing_base = impl->backing_base;
|
backing_base = impl->backing_base;
|
||||||
virtual_base = impl->virtual_base;
|
virtual_base = impl->virtual_base;
|
||||||
@@ -1114,46 +767,6 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
|
|||||||
std::memset(backing_base + physical_offset, fill_value, length);
|
std::memset(backing_base + physical_offset, fill_value, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
|
|
||||||
#else
|
|
||||||
return {};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingMapCount(size_t host_offset, size_t length) const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->BackingMapCount(host_offset, length) : (length != 0 ? 1 : 0);
|
|
||||||
#else
|
|
||||||
return length != 0 ? 1 : 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbWindowSize() : 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HostMemory::IsBackingShared() const noexcept {
|
|
||||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
return impl && impl->IsBackingShared();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t HostMemory::BackingHardwareBufferBase() const noexcept {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return impl ? impl->AhbBase() : 0;
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void HostMemory::EnableDirectMappedAddress() {
|
void HostMemory::EnableDirectMappedAddress() {
|
||||||
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
#if !(defined(__OPENORBIS__) || defined(__managarm__))
|
||||||
if (impl) {
|
if (impl) {
|
||||||
|
|||||||
@@ -8,17 +8,12 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
struct AHardwareBuffer;
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
|
|
||||||
|
|
||||||
enum class MemoryPermission : u32 {
|
enum class MemoryPermission : u32 {
|
||||||
Read = 1 << 0,
|
Read = 1 << 0,
|
||||||
Write = 1 << 1,
|
Write = 1 << 1,
|
||||||
@@ -33,7 +28,7 @@ DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission)
|
|||||||
*/
|
*/
|
||||||
class HostMemory {
|
class HostMemory {
|
||||||
public:
|
public:
|
||||||
explicit HostMemory(size_t backing_size_, size_t virtual_size_, size_t preferred_offset_ = 0);
|
explicit HostMemory(size_t backing_size_, size_t virtual_size_);
|
||||||
~HostMemory();
|
~HostMemory();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,20 +62,6 @@ public:
|
|||||||
return backing_base;
|
return backing_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingSize() const noexcept {
|
|
||||||
return backing_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingMapCount(size_t host_offset, size_t length) const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] size_t BackingHardwareBufferBase() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsBackingShared() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
[[nodiscard]] u8* VirtualBasePointer() noexcept {
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ struct ColorConsoleBackend final : public Backend {
|
|||||||
auto const df = GetDirectFormatArgs(entry);
|
auto const df = GetDirectFormatArgs(entry);
|
||||||
// more restrictive, because take for example this simple prelude:
|
// more restrictive, because take for example this simple prelude:
|
||||||
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
||||||
char buffer[128];
|
char buffer[256];
|
||||||
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||||
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
|
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
|
||||||
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
std::fwrite(entry.message, 1, entry.message_len, stdout);
|
||||||
@@ -425,14 +425,14 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
|||||||
|
|
||||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
||||||
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
||||||
|
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||||
char buffer[BUFSIZ];
|
char buffer[BUFSIZ];
|
||||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||||
buffer[result.size] = '\0';
|
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
|
||||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||||
backend.Write(Entry{
|
backend.Write(Entry{
|
||||||
.message = buffer,
|
.message = buffer,
|
||||||
.message_len = (std::min)(sizeof(buffer) - 1, result.size),
|
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||||
.log_class = log_class,
|
.log_class = log_class,
|
||||||
.log_level = log_level,
|
.log_level = log_level,
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -20,10 +17,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "common/memory_detect.h"
|
#include "common/memory_detect.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
@@ -76,61 +69,4 @@ const MemoryInfo& GetMemInfo() {
|
|||||||
return mem_info;
|
return mem_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 GetPermissibleMapCount() {
|
|
||||||
constexpr u64 DefaultMapCount = 65530;
|
|
||||||
constexpr u64 ReservedMaps = 20000;
|
|
||||||
u64 count = DefaultMapCount;
|
|
||||||
#ifdef __linux__
|
|
||||||
if (std::FILE* const file = std::fopen("/proc/sys/vm/max_map_count", "re")) {
|
|
||||||
char line[32];
|
|
||||||
if (std::fgets(line, sizeof(line), file) != nullptr) {
|
|
||||||
const u64 parsed = std::strtoull(line, nullptr, 10);
|
|
||||||
if (parsed != 0) {
|
|
||||||
count = parsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::fclose(file);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (count <= ReservedMaps) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return count - ReservedMaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetAvailablePhysicalMemory() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
MEMORYSTATUSEX memorystatus;
|
|
||||||
memorystatus.dwLength = sizeof(memorystatus);
|
|
||||||
if (GlobalMemoryStatusEx(&memorystatus) == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return memorystatus.ullAvailPhys;
|
|
||||||
#elif defined(__linux__)
|
|
||||||
static constexpr char AvailableKey[] = "MemAvailable:";
|
|
||||||
if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) {
|
|
||||||
char line[256];
|
|
||||||
u64 available = 0;
|
|
||||||
while (std::fgets(line, sizeof(line), file) != nullptr) {
|
|
||||||
if (std::strncmp(line, AvailableKey, sizeof(AvailableKey) - 1) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
available = std::strtoull(line + sizeof(AvailableKey) - 1, nullptr, 10) * 1024;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::fclose(file);
|
|
||||||
if (available != 0) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct sysinfo meminfo;
|
|
||||||
if (sysinfo(&meminfo) != 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return static_cast<u64>(meminfo.freeram) * static_cast<u64>(meminfo.mem_unit);
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -21,8 +18,4 @@ struct MemoryInfo {
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
[[nodiscard]] const MemoryInfo& GetMemInfo();
|
||||||
|
|
||||||
[[nodiscard]] u64 GetPermissibleMapCount();
|
|
||||||
|
|
||||||
[[nodiscard]] u64 GetAvailablePhysicalMemory();
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -132,11 +132,9 @@ void LogSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LOG_INFO(Config, "Eden Configuration:");
|
||||||
std::string settings_str{};
|
|
||||||
for (auto const& e : settings_list)
|
for (auto const& e : settings_list)
|
||||||
settings_str += e;
|
LOG_INFO(Config, "{}", e);
|
||||||
LOG_INFO(Config, "Eden Configuration:\n{}", settings_str);
|
|
||||||
#define LOG_PATH(NAME) \
|
#define LOG_PATH(NAME) \
|
||||||
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
||||||
LOG_PATH(CacheDir);
|
LOG_PATH(CacheDir);
|
||||||
|
|||||||
@@ -645,9 +645,6 @@ struct Values {
|
|||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
|
|
||||||
Category::RendererHacks};
|
|
||||||
|
|
||||||
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
|
||||||
GpuUnswizzleSize::Large,
|
GpuUnswizzleSize::Large,
|
||||||
"gpu_unswizzle_texture_size",
|
"gpu_unswizzle_texture_size",
|
||||||
|
|||||||
+1
-5
@@ -119,7 +119,6 @@ struct System::Impl {
|
|||||||
|
|
||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
unified_memory = Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
core_timing.SetMulticore(is_multicore);
|
core_timing.SetMulticore(is_multicore);
|
||||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||||
@@ -147,8 +146,7 @@ struct System::Impl {
|
|||||||
!device_memory.has_value() ||
|
!device_memory.has_value() ||
|
||||||
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
is_multicore != Settings::values.use_multi_core.GetValue() ||
|
||||||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
|
||||||
Settings::MemoryLayout::Memory_4Gb) ||
|
Settings::MemoryLayout::Memory_4Gb);
|
||||||
unified_memory != Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
if (!must_reinitialize) {
|
if (!must_reinitialize) {
|
||||||
return;
|
return;
|
||||||
@@ -159,7 +157,6 @@ struct System::Impl {
|
|||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
extended_memory_layout =
|
extended_memory_layout =
|
||||||
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
|
||||||
unified_memory = Settings::values.use_unified_memory.GetValue();
|
|
||||||
|
|
||||||
Initialize(system);
|
Initialize(system);
|
||||||
}
|
}
|
||||||
@@ -506,7 +503,6 @@ struct System::Impl {
|
|||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
bool is_multicore : 1 = false;
|
bool is_multicore : 1 = false;
|
||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool unified_memory : 1 = false;
|
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
bool nvdec_active : 1 = false;
|
bool nvdec_active : 1 = false;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -15,18 +12,9 @@ constexpr size_t VirtualReserveSize = 1ULL << 38;
|
|||||||
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
constexpr size_t VirtualReserveSize = 1ULL << 39;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
|
||||||
size_t ApplicationPoolOffset() {
|
|
||||||
using Init = Kernel::Board::Nintendo::Nx::KSystemControl::Init;
|
|
||||||
const size_t dram_size = Init::GetIntendedMemorySize();
|
|
||||||
const size_t application_pool_size = Init::GetApplicationPoolSize();
|
|
||||||
return dram_size > application_pool_size ? dram_size - application_pool_size : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceMemory::DeviceMemory()
|
DeviceMemory::DeviceMemory()
|
||||||
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
: buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
|
||||||
VirtualReserveSize, ApplicationPoolOffset()} {}
|
VirtualReserveSize} {}
|
||||||
|
|
||||||
DeviceMemory::~DeviceMemory() = default;
|
DeviceMemory::~DeviceMemory() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
struct AHardwareBuffer;
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
constexpr size_t DEVICE_PAGEBITS = 12ULL;
|
||||||
@@ -97,34 +95,6 @@ public:
|
|||||||
ApplyOpOnPAddr(address, buffer, operation);
|
ApplyOpOnPAddr(address, buffer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* GetPhysicalBase() noexcept {
|
|
||||||
return reinterpret_cast<u8*>(physical_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u8* GetPhysicalBase() const noexcept {
|
|
||||||
return reinterpret_cast<const u8*>(physical_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetPhysicalSize() const noexcept {
|
|
||||||
return physical_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
|
|
||||||
return ahb_windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetBackingHardwareBufferWindowSize() const noexcept {
|
|
||||||
return ahb_window_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetBackingHardwareBufferBase() const noexcept {
|
|
||||||
return ahb_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsBackingShared() const noexcept {
|
|
||||||
return backing_is_shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
|
||||||
PAddr subbits = PAddr(address & page_mask);
|
PAddr subbits = PAddr(address & page_mask);
|
||||||
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
|
||||||
@@ -201,11 +171,6 @@ private:
|
|||||||
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
|
||||||
|
|
||||||
const uintptr_t physical_base;
|
const uintptr_t physical_base;
|
||||||
const size_t physical_size;
|
|
||||||
const std::span<AHardwareBuffer* const> ahb_windows;
|
|
||||||
const size_t ahb_window_size;
|
|
||||||
const size_t ahb_base;
|
|
||||||
const bool backing_is_shared;
|
|
||||||
DeviceInterface* device_inter;
|
DeviceInterface* device_inter;
|
||||||
|
|
||||||
struct TrackedEntry {
|
struct TrackedEntry {
|
||||||
|
|||||||
@@ -171,11 +171,6 @@ struct DeviceMemoryManagerAllocator {
|
|||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
|
||||||
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
|
||||||
, physical_size{device_memory_.buffer.BackingSize()}
|
|
||||||
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
|
|
||||||
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
|
|
||||||
, ahb_base{device_memory_.buffer.BackingHardwareBufferBase()}
|
|
||||||
, backing_is_shared{device_memory_.buffer.IsBackingShared()}
|
|
||||||
, device_inter{nullptr}
|
, device_inter{nullptr}
|
||||||
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
|
||||||
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
|
||||||
|
|||||||
@@ -227,8 +227,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||||
tr("May reduce shader stutter."));
|
tr("May reduce shader stutter."));
|
||||||
INSERT(Settings, use_unified_memory, tr("Enable unified memory access"),
|
|
||||||
tr("Lets the GPU write buffer readbacks directly into guest memory."));
|
|
||||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||||
tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering "
|
tr("Makes the game believe GPU work finishes faster than it does, so it stops lowering "
|
||||||
"resolution and render distance to fit the Switch's clocks."));
|
"resolution and render distance to fit the Switch's clocks."));
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
namespace Shader::Backend::SPIRV {
|
namespace Shader::Backend::SPIRV {
|
||||||
namespace {
|
namespace {
|
||||||
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 4 + index_offset * 4);
|
|
||||||
const Id shift_id{ctx.Const(2U)};
|
const Id shift_id{ctx.Const(2U)};
|
||||||
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||||
if (index_offset > 0) {
|
if (index_offset > 0) {
|
||||||
@@ -161,8 +160,7 @@ Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) {
|
|||||||
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
|
Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) {
|
||||||
if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) {
|
if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id shift_id{ctx.Const(3U)};
|
const Id shift_id{ctx.Const(3U)};
|
||||||
const Id index{
|
const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)};
|
||||||
ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.BoundSharedOffset(offset, 8), shift_id)};
|
|
||||||
const Id pointer{
|
const Id pointer{
|
||||||
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
ctx.OpAccessChain(ctx.shared_u64, ctx.shared_memory_u64, ctx.u32_zero_value, index)};
|
||||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ std::pair<Id, Id> ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count)
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 1);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{
|
const Id pointer{
|
||||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||||
@@ -43,7 +42,6 @@ Id EmitLoadSharedU8(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 1);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{
|
const Id pointer{
|
||||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||||
@@ -55,7 +53,6 @@ Id EmitLoadSharedS8(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 2);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||||
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||||
@@ -66,7 +63,6 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 2);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||||
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer));
|
||||||
@@ -77,7 +73,6 @@ Id EmitLoadSharedS16(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 4);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)};
|
||||||
return ctx.OpLoad(ctx.U32[1], pointer);
|
return ctx.OpLoad(ctx.U32[1], pointer);
|
||||||
@@ -87,7 +82,6 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 8);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||||
return ctx.OpLoad(ctx.U32[2], pointer);
|
return ctx.OpLoad(ctx.U32[2], pointer);
|
||||||
@@ -103,7 +97,6 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 16);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||||
return ctx.OpLoad(ctx.U32[4], pointer);
|
return ctx.OpLoad(ctx.U32[4], pointer);
|
||||||
@@ -120,7 +113,6 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 1);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{
|
const Id pointer{
|
||||||
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)};
|
||||||
@@ -131,7 +123,6 @@ void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 2);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)};
|
||||||
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value));
|
||||||
@@ -141,7 +132,6 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 4);
|
|
||||||
Id pointer{};
|
Id pointer{};
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2);
|
||||||
@@ -154,7 +144,6 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 8);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)};
|
||||||
ctx.OpStore(pointer, value);
|
ctx.OpStore(pointer, value);
|
||||||
@@ -170,7 +159,6 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) {
|
||||||
offset = ctx.BoundSharedOffset(offset, 16);
|
|
||||||
if (ctx.uses_explicit_workgroup_layout) {
|
if (ctx.uses_explicit_workgroup_layout) {
|
||||||
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)};
|
||||||
ctx.OpStore(pointer, value);
|
ctx.OpStore(pointer, value);
|
||||||
|
|||||||
@@ -600,16 +600,6 @@ void EmitContext::DefineLocalMemory(const IR::Program& program) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Id EmitContext::BoundSharedOffset(Id offset, u32 access_bytes) {
|
|
||||||
if (shared_memory_declared_bytes == 0) {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
const u32 last_valid{shared_memory_declared_bytes > access_bytes
|
|
||||||
? shared_memory_declared_bytes - access_bytes
|
|
||||||
: 0U};
|
|
||||||
return OpUMin(U32[1], offset, Const(last_valid));
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
||||||
uses_explicit_workgroup_layout =
|
uses_explicit_workgroup_layout =
|
||||||
profile.support_explicit_workgroup_layout &&
|
profile.support_explicit_workgroup_layout &&
|
||||||
@@ -618,15 +608,8 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
|||||||
if (program.shared_memory_size == 0) {
|
if (program.shared_memory_size == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u32 device_limit{profile.max_shared_memory_size};
|
|
||||||
const u32 shared_memory_size{device_limit != 0 && program.shared_memory_size > device_limit
|
|
||||||
? device_limit
|
|
||||||
: program.shared_memory_size};
|
|
||||||
if (shared_memory_size != program.shared_memory_size) {
|
|
||||||
shared_memory_declared_bytes = shared_memory_size;
|
|
||||||
}
|
|
||||||
const auto make{[&](Id element_type, u32 element_size) {
|
const auto make{[&](Id element_type, u32 element_size) {
|
||||||
const u32 num_elements{Common::DivCeil(shared_memory_size, element_size)};
|
const u32 num_elements{Common::DivCeil(program.shared_memory_size, element_size)};
|
||||||
const Id array_type{TypeArray(element_type, Const(num_elements))};
|
const Id array_type{TypeArray(element_type, Const(num_elements))};
|
||||||
Decorate(array_type, spv::Decoration::ArrayStride, element_size);
|
Decorate(array_type, spv::Decoration::ArrayStride, element_size);
|
||||||
|
|
||||||
@@ -661,7 +644,7 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) {
|
|||||||
std::tie(shared_memory_u32x4, shared_u32x4, std::ignore) = make(U32[4], 16);
|
std::tie(shared_memory_u32x4, shared_u32x4, std::ignore) = make(U32[4], 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u32 num_elements{Common::DivCeil(shared_memory_size, 4U)};
|
const u32 num_elements{Common::DivCeil(program.shared_memory_size, 4U)};
|
||||||
const Id type{TypeArray(U32[1], Const(num_elements))};
|
const Id type{TypeArray(U32[1], Const(num_elements))};
|
||||||
shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type);
|
shared_memory_u32_type = TypePointer(spv::StorageClass::Workgroup, type);
|
||||||
|
|
||||||
|
|||||||
@@ -312,8 +312,6 @@ public:
|
|||||||
Id local_memory{};
|
Id local_memory{};
|
||||||
|
|
||||||
bool uses_explicit_workgroup_layout{};
|
bool uses_explicit_workgroup_layout{};
|
||||||
u32 shared_memory_declared_bytes{};
|
|
||||||
[[nodiscard]] Id BoundSharedOffset(Id offset, u32 access_bytes);
|
|
||||||
Id shared_memory_u8{};
|
Id shared_memory_u8{};
|
||||||
Id shared_memory_u16{};
|
Id shared_memory_u16{};
|
||||||
Id shared_memory_u32{};
|
Id shared_memory_u32{};
|
||||||
|
|||||||
@@ -103,9 +103,6 @@ struct Profile {
|
|||||||
|
|
||||||
u32 gl_max_compute_smem_size{};
|
u32 gl_max_compute_smem_size{};
|
||||||
|
|
||||||
/// Largest workgroup shared memory allocation the device accepts, 0 when unconstrained
|
|
||||||
u32 max_shared_memory_size{};
|
|
||||||
|
|
||||||
/// Maxwell and earlier nVidia architectures have broken robust support
|
/// Maxwell and earlier nVidia architectures have broken robust support
|
||||||
bool has_broken_robust{};
|
bool has_broken_robust{};
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ namespace VideoCommon {
|
|||||||
|
|
||||||
enum class BufferFlagBits {
|
enum class BufferFlagBits {
|
||||||
Picked = 1 << 0,
|
Picked = 1 << 0,
|
||||||
CachedWrites = 1 << 1,
|
|
||||||
PreemtiveDownload = 1 << 2,
|
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
|
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
|
||||||
|
|
||||||
@@ -58,15 +56,6 @@ public:
|
|||||||
flags |= BufferFlagBits::Picked;
|
flags |= BufferFlagBits::Picked;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkPreemtiveDownload() noexcept {
|
|
||||||
flags |= BufferFlagBits::PreemtiveDownload;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmark buffer as picked
|
|
||||||
void Unpick() noexcept {
|
|
||||||
flags &= ~BufferFlagBits::Picked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Increases the likeliness of this being a stream buffer
|
/// Increases the likeliness of this being a stream buffer
|
||||||
void IncreaseStreamScore(int score) noexcept {
|
void IncreaseStreamScore(int score) noexcept {
|
||||||
stream_score += score;
|
stream_score += score;
|
||||||
@@ -87,15 +76,6 @@ public:
|
|||||||
return True(flags & BufferFlagBits::Picked);
|
return True(flags & BufferFlagBits::Picked);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true when the buffer has pending cached writes
|
|
||||||
[[nodiscard]] bool HasCachedWrites() const noexcept {
|
|
||||||
return True(flags & BufferFlagBits::CachedWrites);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPreemtiveDownload() const noexcept {
|
|
||||||
return True(flags & BufferFlagBits::PreemtiveDownload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the base CPU address of the buffer
|
/// Returns the base CPU address of the buffer
|
||||||
[[nodiscard]] VAddr CpuAddr() const noexcept {
|
[[nodiscard]] VAddr CpuAddr() const noexcept {
|
||||||
return cpu_addr;
|
return cpu_addr;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
|
||||||
@@ -121,25 +122,6 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
|||||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void BufferCache<P>::CachedWriteMemory(DAddr device_addr, u64 size) {
|
|
||||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
|
||||||
if (!is_dirty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DAddr aligned_start = Common::AlignDown(device_addr, DEVICE_PAGESIZE);
|
|
||||||
DAddr aligned_end = Common::AlignUp(device_addr + size, DEVICE_PAGESIZE);
|
|
||||||
if (!IsRegionGpuModified(aligned_start, aligned_end - aligned_start)) {
|
|
||||||
WriteMemory(device_addr, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp_buffer.resize_destructive(size);
|
|
||||||
device_memory.ReadBlockUnsafe(device_addr, tmp_buffer.data(), size);
|
|
||||||
|
|
||||||
InlineMemoryImplementation(device_addr, size, tmp_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
bool BufferCache<P>::OnCPUWrite(DAddr device_addr, u64 size) {
|
||||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||||
@@ -422,7 +404,7 @@ void BufferCache<P>::UnbindGraphicsStorageBuffers(size_t stage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
void BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index,
|
||||||
u32 cbuf_offset, bool is_written) {
|
u32 cbuf_offset, bool is_written) {
|
||||||
const bool already_enabled =
|
const bool already_enabled =
|
||||||
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
((channel_state->enabled_storage_buffers[stage] >> ssbo_index) & 1U) != 0;
|
||||||
@@ -433,7 +415,7 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
|||||||
LOG_WARNING(HW_GPU,
|
LOG_WARNING(HW_GPU,
|
||||||
"Skipping graphics storage buffer {} due to driver limit {}",
|
"Skipping graphics storage buffer {} due to driver limit {}",
|
||||||
ssbo_index, max_bindings);
|
ssbo_index, max_bindings);
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +431,6 @@ bool BufferCache<P>::BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index,
|
|||||||
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
const GPUVAddr ssbo_addr = cbufs.const_buffers[cbuf_index].address + cbuf_offset;
|
||||||
channel_state->storage_buffers[stage][ssbo_index] =
|
channel_state->storage_buffers[stage][ssbo_index] =
|
||||||
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
StorageBufferBinding(ssbo_addr, cbuf_index, is_written);
|
||||||
return (channel_state->storage_buffers[stage][ssbo_index].buffer_id != NULL_BUFFER_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -571,11 +552,7 @@ void BufferCache<P>::AccumulateFlushes() {
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
bool BufferCache<P>::ShouldWaitAsyncFlushes() const noexcept {
|
||||||
if (async_buffers.empty()) {
|
return (!async_buffers.empty() && async_buffers.front().has_value());
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return async_buffers.front().has_value() ||
|
|
||||||
!pending_downloads.front().unified_copies.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -583,7 +560,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
|||||||
AccumulateFlushes();
|
AccumulateFlushes();
|
||||||
|
|
||||||
if (committed_gpu_modified_ranges.empty()) {
|
if (committed_gpu_modified_ranges.empty()) {
|
||||||
pending_downloads.emplace_back();
|
|
||||||
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -643,84 +619,27 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
|
|||||||
}
|
}
|
||||||
committed_gpu_modified_ranges.clear();
|
committed_gpu_modified_ranges.clear();
|
||||||
if (downloads.empty()) {
|
if (downloads.empty()) {
|
||||||
pending_downloads.emplace_back();
|
|
||||||
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
async_buffers.emplace_back(std::optional<Async_Buffer>{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
|
||||||
struct QueuedUnifiedCopy {
|
boost::container::small_vector<BufferCopy, 4> normalized_copies;
|
||||||
u64 window;
|
|
||||||
BufferId buffer_id;
|
|
||||||
boost::container::small_vector<BufferCopy, 16> copies;
|
|
||||||
};
|
|
||||||
|
|
||||||
AsyncDownloadBatch batch;
|
|
||||||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 16> staging_downloads;
|
|
||||||
boost::container::small_vector<QueuedUnifiedCopy, 4> unified_copy_queue;
|
|
||||||
boost::container::small_vector<u64, 4> window_ids;
|
|
||||||
UnifiedWindowGroups groups;
|
|
||||||
u64 staging_size_bytes = 0;
|
|
||||||
for (auto& [copy, buffer_id] : downloads) {
|
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
|
||||||
const DAddr orig_device_addr = buffer.CpuAddr() + copy.src_offset;
|
|
||||||
bool unified = false;
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (runtime.HasUnifiedMemory()) {
|
|
||||||
window_ids.clear();
|
|
||||||
groups.clear();
|
|
||||||
unified = ResolveUnifiedWindows(orig_device_addr, copy.src_offset, copy.size,
|
|
||||||
window_ids, groups);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BufferCopy record{copy};
|
|
||||||
record.src_offset = static_cast<size_t>(orig_device_addr);
|
|
||||||
if (unified) {
|
|
||||||
async_downloads.Add(orig_device_addr, copy.size);
|
|
||||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
|
||||||
for (size_t i = 0; i < window_ids.size(); ++i) {
|
|
||||||
unified_copy_queue.push_back(
|
|
||||||
QueuedUnifiedCopy{window_ids[i], buffer_id, std::move(groups[i])});
|
|
||||||
}
|
|
||||||
batch.unified_copies.push_back(record);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
copy.dst_offset = staging_size_bytes;
|
|
||||||
constexpr u64 align = 64ULL;
|
|
||||||
staging_size_bytes += (copy.size + align - 1) & ~(align - 1ULL);
|
|
||||||
staging_downloads.push_back({copy, buffer_id});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Async_Buffer> download_staging;
|
|
||||||
if (!staging_downloads.empty()) {
|
|
||||||
download_staging = runtime.DownloadStagingBuffer(staging_size_bytes, true);
|
|
||||||
}
|
|
||||||
runtime.PreCopyBarrier();
|
runtime.PreCopyBarrier();
|
||||||
for (auto& [copy, buffer_id] : staging_downloads) {
|
for (auto& [copy, buffer_id] : downloads) {
|
||||||
copy.dst_offset += download_staging->offset;
|
copy.dst_offset += download_staging.offset;
|
||||||
const std::array copies{copy};
|
const std::array copies{copy};
|
||||||
|
BufferCopy second_copy{copy};
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
BufferCopy record{copy};
|
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
|
||||||
record.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
|
const DAddr orig_device_addr = static_cast<DAddr>(second_copy.src_offset);
|
||||||
const DAddr orig_device_addr = static_cast<DAddr>(record.src_offset);
|
|
||||||
async_downloads.Add(orig_device_addr, copy.size);
|
async_downloads.Add(orig_device_addr, copy.size);
|
||||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
buffer.MarkUsage(copy.src_offset, copy.size);
|
||||||
runtime.CopyBuffer(download_staging->buffer, buffer, copies, false);
|
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
||||||
batch.staging_copies.push_back(record);
|
normalized_copies.push_back(second_copy);
|
||||||
}
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
for (const auto& queued : unified_copy_queue) {
|
|
||||||
const std::span<const BufferCopy> group_span(queued.copies.data(),
|
|
||||||
queued.copies.size());
|
|
||||||
runtime.CopyToUnifiedMemory(queued.window, slot_buffers[queued.buffer_id], group_span);
|
|
||||||
}
|
|
||||||
if (!unified_copy_queue.empty()) {
|
|
||||||
runtime.FlushUnifiedMemoryCopies();
|
|
||||||
runtime.UnifiedMemoryHostBarrier();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
runtime.PostCopyBarrier();
|
runtime.PostCopyBarrier();
|
||||||
pending_downloads.emplace_back(std::move(batch));
|
pending_downloads.emplace_back(std::move(normalized_copies));
|
||||||
async_buffers.emplace_back(std::move(download_staging));
|
async_buffers.emplace_back(download_staging);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -735,49 +654,32 @@ void BufferCache<P>::PopAsyncFlushes() {
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::PopAsyncBuffers() {
|
void BufferCache<P>::PopAsyncBuffers() {
|
||||||
struct Writeback {
|
if (async_buffers.empty()) {
|
||||||
DAddr addr;
|
return;
|
||||||
const u8* src;
|
}
|
||||||
u64 size;
|
if (!async_buffers.front().has_value()) {
|
||||||
};
|
|
||||||
boost::container::small_vector<Writeback, 8> writebacks;
|
|
||||||
{
|
|
||||||
std::scoped_lock lock{mutex};
|
|
||||||
if (async_buffers.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& batch = pending_downloads.front();
|
|
||||||
auto& async_buffer = async_buffers.front();
|
|
||||||
if (async_buffer.has_value()) {
|
|
||||||
const u8* base = async_buffer->mapped_span.data();
|
|
||||||
const size_t base_offset = async_buffer->offset;
|
|
||||||
for (const auto& copy : batch.staging_copies) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
for (const auto& copy : batch.unified_copies) {
|
|
||||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
|
||||||
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
|
||||||
gpu_modified_ranges.Subtract(start, end - start);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async_buffers.pop_front();
|
async_buffers.pop_front();
|
||||||
pending_downloads.pop_front();
|
return;
|
||||||
}
|
}
|
||||||
for (const auto& wb : writebacks) {
|
auto& downloads = pending_downloads.front();
|
||||||
device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size);
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||||
|
async_buffers.pop_front();
|
||||||
|
pending_downloads.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -841,16 +743,6 @@ void BufferCache<P>::BindHostIndexBuffer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
void BufferCache<P>::BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
|
||||||
u32 stride) {
|
|
||||||
if constexpr (IS_OPENGL) {
|
|
||||||
runtime.BindVertexBuffer(index, buffer, offset, size, stride);
|
|
||||||
} else {
|
|
||||||
runtime.BindVertexBuffer(index, buffer.Handle(), offset, size, stride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
Binding& BufferCache<P>::VertexBufferSlot(u32 index) {
|
Binding& BufferCache<P>::VertexBufferSlot(u32 index) {
|
||||||
ASSERT(index < NUM_VERTEX_BUFFERS);
|
ASSERT(index < NUM_VERTEX_BUFFERS);
|
||||||
@@ -1085,25 +977,11 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
|
|||||||
Buffer& buffer = slot_buffers[binding.buffer_id];
|
Buffer& buffer = slot_buffers[binding.buffer_id];
|
||||||
TouchBuffer(buffer, binding.buffer_id);
|
TouchBuffer(buffer, binding.buffer_id);
|
||||||
const u32 size = binding.size;
|
const u32 size = binding.size;
|
||||||
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
|
||||||
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
const auto window = TryResolveUnifiedRange(binding.device_addr, size);
|
|
||||||
if (window && runtime.IsUnifiedStorageRange(size, window->offset)) {
|
|
||||||
if (is_written) {
|
|
||||||
memory_tracker.MarkRegionAsCpuModified(binding.device_addr, size);
|
|
||||||
}
|
|
||||||
runtime.BindStorageBuffer(runtime.UnifiedWindowBuffer(window->window),
|
|
||||||
runtime.UnifiedWindowAddress(window->window),
|
|
||||||
static_cast<u32>(window->offset), size, is_written);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SynchronizeBuffer(buffer, binding.device_addr, size);
|
SynchronizeBuffer(buffer, binding.device_addr, size);
|
||||||
|
|
||||||
const u32 offset = buffer.Offset(binding.device_addr);
|
const u32 offset = buffer.Offset(binding.device_addr);
|
||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0;
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||||
@@ -1344,9 +1222,14 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
|
const GPUVAddr gpu_addr_begin = index_buffer_ref.StartAddress();
|
||||||
const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
|
const GPUVAddr gpu_addr_end = index_buffer_ref.EndAddress();
|
||||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
u64 address_size = 0;
|
||||||
const u32 draw_size = (index_buffer_ref.count + index_buffer_ref.first) * u32(index_buffer_ref.FormatSizeInBytes());
|
if (gpu_addr_end > gpu_addr_begin) {
|
||||||
const u32 size = (std::min)(address_size, draw_size);
|
address_size = (std::min)(gpu_addr_end - gpu_addr_begin,
|
||||||
|
u64{(std::numeric_limits<u32>::max)()});
|
||||||
|
}
|
||||||
|
const u64 draw_size = (u64{index_buffer_ref.count} + u64{index_buffer_ref.first}) *
|
||||||
|
u64{index_buffer_ref.FormatSizeInBytes()};
|
||||||
|
const u32 size = static_cast<u32>((std::min)(address_size, draw_size));
|
||||||
if (size == 0 || !device_addr) {
|
if (size == 0 || !device_addr) {
|
||||||
channel_state->index_buffer = NULL_BINDING;
|
channel_state->index_buffer = NULL_BINDING;
|
||||||
return;
|
return;
|
||||||
@@ -1361,6 +1244,20 @@ void BufferCache<P>::UpdateIndexBuffer() {
|
|||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::UpdateVertexBuffers() {
|
void BufferCache<P>::UpdateVertexBuffers() {
|
||||||
auto& flags = maxwell3d->dirty.flags;
|
auto& flags = maxwell3d->dirty.flags;
|
||||||
|
const u32 base_instance = maxwell3d->draw_manager.draw_state.base_instance;
|
||||||
|
if (draw_instance_count != last_draw_instance_count ||
|
||||||
|
base_instance != last_draw_base_instance) {
|
||||||
|
last_draw_instance_count = draw_instance_count;
|
||||||
|
last_draw_base_instance = base_instance;
|
||||||
|
const auto& instances = maxwell3d->regs.vertex_stream_instances;
|
||||||
|
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
|
||||||
|
if (!instances.IsInstancingEnabled(index)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
flags[Dirty::VertexBuffer0 + index] = true;
|
||||||
|
flags[Dirty::VertexBuffers] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
|
if (!maxwell3d->dirty.flags[Dirty::VertexBuffers]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1381,15 +1278,37 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
|
|||||||
const GPUVAddr gpu_addr_begin = array.Address();
|
const GPUVAddr gpu_addr_begin = array.Address();
|
||||||
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
const GPUVAddr gpu_addr_end = limit.Address() + 1;
|
||||||
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr_begin);
|
||||||
const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
|
if (array.enable == 0 || !device_addr || gpu_addr_end <= gpu_addr_begin) {
|
||||||
u32 size = address_size; // TODO: Analyze stride and number of vertices
|
|
||||||
if (array.enable == 0 || size == 0 || !device_addr) {
|
|
||||||
channel_state->vertex_buffers[index] = NULL_BINDING;
|
channel_state->vertex_buffers[index] = NULL_BINDING;
|
||||||
UpdateVertexBufferSlot(index, NULL_BINDING);
|
UpdateVertexBufferSlot(index, NULL_BINDING);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || size >= 64_MiB) {
|
// TODO: Analyze stride and number of vertices
|
||||||
size = static_cast<u32>(gpu_memory->MaxContinuousRange(gpu_addr_begin, size));
|
constexpr u64 implausible_size = 64_MiB;
|
||||||
|
u64 address_size = gpu_addr_end - gpu_addr_begin;
|
||||||
|
if (address_size > u64{(std::numeric_limits<u32>::max)()}) {
|
||||||
|
address_size = implausible_size;
|
||||||
|
}
|
||||||
|
const bool is_instanced = maxwell3d->regs.vertex_stream_instances.IsInstancingEnabled(index);
|
||||||
|
const u64 stride = static_cast<u64>(array.stride);
|
||||||
|
if (is_instanced && stride != 0 && draw_instance_count != 0) {
|
||||||
|
const u64 base_instance = static_cast<u64>(maxwell3d->draw_manager.draw_state.base_instance);
|
||||||
|
u64 elements = base_instance + 1;
|
||||||
|
if (array.frequency != 0) {
|
||||||
|
elements = (base_instance + static_cast<u64>(draw_instance_count) - 1) /
|
||||||
|
static_cast<u64>(array.frequency) +
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
address_size = (std::min)(address_size, (elements + 1) * stride);
|
||||||
|
}
|
||||||
|
if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end) || address_size >= implausible_size) {
|
||||||
|
address_size = gpu_memory->MaxContinuousRange(gpu_addr_begin, address_size);
|
||||||
|
}
|
||||||
|
const u32 size = static_cast<u32>(address_size);
|
||||||
|
if (size == 0) {
|
||||||
|
channel_state->vertex_buffers[index] = NULL_BINDING;
|
||||||
|
UpdateVertexBufferSlot(index, NULL_BINDING);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
const BufferId buffer_id = FindBuffer(*device_addr, size);
|
||||||
const Binding binding{
|
const Binding binding{
|
||||||
@@ -1671,9 +1590,10 @@ template <class P>
|
|||||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
constexpr u64 max_buffer_size = u64{(std::numeric_limits<u32>::max)()};
|
||||||
|
wanted_size = static_cast<u32>((std::min)(device_addr_end - device_addr, max_buffer_size));
|
||||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
const u32 size = static_cast<u32>((std::min)(overlap.end - overlap.begin, max_buffer_size));
|
||||||
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
const BufferId new_buffer_id = slot_buffers.insert(runtime, overlap.begin, size);
|
||||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||||
const size_t size_bytes = new_buffer.SizeBytes();
|
const size_t size_bytes = new_buffer.SizeBytes();
|
||||||
@@ -1792,142 +1712,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
bool BufferCache<P>::ResolveUnifiedWindows(
|
|
||||||
[[maybe_unused]] DAddr device_addr, [[maybe_unused]] u64 buffer_offset,
|
|
||||||
[[maybe_unused]] u64 size, [[maybe_unused]] boost::container::small_vector<u64, 4>& window_ids,
|
|
||||||
[[maybe_unused]] UnifiedWindowGroups& groups) {
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
const u8* const physical_base = device_memory.GetPhysicalBase();
|
|
||||||
const u64 unified_base = runtime.UnifiedMemoryBase();
|
|
||||||
const u64 unified_size = runtime.UnifiedMemorySize();
|
|
||||||
const u64 window_size = runtime.UnifiedMemoryWindowSize();
|
|
||||||
if (window_size == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto group_for = [&](u64 window) -> boost::container::small_vector<BufferCopy, 16>& {
|
|
||||||
for (size_t i = 0; i < window_ids.size(); ++i) {
|
|
||||||
if (window_ids[i] == window) {
|
|
||||||
return groups[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
window_ids.push_back(window);
|
|
||||||
groups.emplace_back();
|
|
||||||
return groups.back();
|
|
||||||
};
|
|
||||||
u64 downloaded = 0;
|
|
||||||
while (downloaded < size) {
|
|
||||||
const DAddr page_addr = device_addr + downloaded;
|
|
||||||
const u8* const ptr = device_memory.GetPointer<u8>(page_addr);
|
|
||||||
if (ptr == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 page_offset = page_addr & Core::DEVICE_PAGEMASK;
|
|
||||||
u64 chunk = (std::min)(size - downloaded,
|
|
||||||
static_cast<u64>(Core::DEVICE_PAGESIZE) - page_offset);
|
|
||||||
const u64 phys_offset = static_cast<u64>(ptr - physical_base);
|
|
||||||
if (phys_offset < unified_base || phys_offset - unified_base + chunk > unified_size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 relative = phys_offset - unified_base;
|
|
||||||
const u64 window = relative / window_size;
|
|
||||||
const u64 local_offset = relative % window_size;
|
|
||||||
chunk = (std::min)(chunk, window_size - local_offset);
|
|
||||||
auto& group = group_for(window);
|
|
||||||
if (!group.empty()) {
|
|
||||||
BufferCopy& last = group.back();
|
|
||||||
if (last.src_offset + last.size == buffer_offset + downloaded &&
|
|
||||||
last.dst_offset + last.size == local_offset) {
|
|
||||||
last.size += chunk;
|
|
||||||
downloaded += chunk;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.push_back(BufferCopy{
|
|
||||||
.src_offset = buffer_offset + downloaded,
|
|
||||||
.dst_offset = local_offset,
|
|
||||||
.size = chunk,
|
|
||||||
});
|
|
||||||
downloaded += chunk;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
|
||||||
std::optional<typename BufferCache<P>::UnifiedWindowRange>
|
|
||||||
BufferCache<P>::TryResolveUnifiedRange([[maybe_unused]] DAddr device_addr,
|
|
||||||
[[maybe_unused]] u64 size) {
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (size == 0 || !runtime.IsUnifiedMemoryBindable()) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const u64 window_size = runtime.UnifiedMemoryWindowSize();
|
|
||||||
if (window_size == 0) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const u8* const first = device_memory.GetSpan(device_addr, size);
|
|
||||||
if (first == nullptr) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const u64 phys_offset = static_cast<u64>(first - device_memory.GetPhysicalBase());
|
|
||||||
const u64 unified_base = runtime.UnifiedMemoryBase();
|
|
||||||
if (phys_offset < unified_base) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const u64 relative = phys_offset - unified_base;
|
|
||||||
const u64 unified_size = runtime.UnifiedMemorySize();
|
|
||||||
if (relative >= unified_size || unified_size - relative < size) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const u64 local_offset = relative % window_size;
|
|
||||||
if (window_size - local_offset < size) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size) ||
|
|
||||||
IsRegionGpuModified(device_addr, size)) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return UnifiedWindowRange{
|
|
||||||
.window = static_cast<size_t>(relative / window_size),
|
|
||||||
.offset = local_offset,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
|
||||||
bool BufferCache<P>::TryUnifiedDownloadMemory([[maybe_unused]] Buffer& buffer,
|
|
||||||
[[maybe_unused]] std::span<BufferCopy> copies) {
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
boost::container::small_vector<u64, 4> window_ids;
|
|
||||||
UnifiedWindowGroups groups;
|
|
||||||
for (const BufferCopy& copy : copies) {
|
|
||||||
if (!ResolveUnifiedWindows(buffer.CpuAddr() + copy.src_offset, copy.src_offset,
|
|
||||||
copy.size, window_ids, groups)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const BufferCopy& copy : copies) {
|
|
||||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
|
||||||
}
|
|
||||||
runtime.PreCopyBarrier();
|
|
||||||
for (size_t i = 0; i < window_ids.size(); ++i) {
|
|
||||||
const std::span<const BufferCopy> group_span(groups[i].data(), groups[i].size());
|
|
||||||
runtime.CopyToUnifiedMemory(window_ids[i], buffer, group_span);
|
|
||||||
}
|
|
||||||
runtime.FlushUnifiedMemoryCopies();
|
|
||||||
runtime.UnifiedMemoryHostBarrier();
|
|
||||||
runtime.Finish();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||||
[[maybe_unused]] u64 total_size_bytes,
|
[[maybe_unused]] u64 total_size_bytes,
|
||||||
@@ -2031,12 +1815,6 @@ void BufferCache<P>::DownloadBufferMemory(Buffer& buffer, DAddr device_addr, u64
|
|||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (USE_MEMORY_MAPS) {
|
if constexpr (USE_MEMORY_MAPS) {
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (runtime.HasUnifiedMemory() &&
|
|
||||||
TryUnifiedDownloadMemory(buffer, std::span(copies.data(), copies.size()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||||
const u8* const mapped_memory = download_staging.mapped_span.data();
|
const u8* const mapped_memory = download_staging.mapped_span.data();
|
||||||
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
|
const std::span<BufferCopy> copies_span(copies.data(), copies.data() + copies.size());
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -181,7 +180,6 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
|||||||
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
|
static constexpr bool USE_MEMORY_MAPS = P::USE_MEMORY_MAPS;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
|
static constexpr bool SEPARATE_IMAGE_BUFFERS_BINDINGS = P::SEPARATE_IMAGE_BUFFER_BINDINGS;
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = P::USE_UNIFIED_MEMORY;
|
|
||||||
|
|
||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
|
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
|
||||||
@@ -219,8 +217,6 @@ public:
|
|||||||
|
|
||||||
void WriteMemory(DAddr device_addr, u64 size);
|
void WriteMemory(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
|
||||||
|
|
||||||
bool OnCPUWrite(DAddr device_addr, u64 size);
|
bool OnCPUWrite(DAddr device_addr, u64 size);
|
||||||
|
|
||||||
void DownloadMemory(DAddr device_addr, u64 size);
|
void DownloadMemory(DAddr device_addr, u64 size);
|
||||||
@@ -250,7 +246,7 @@ public:
|
|||||||
|
|
||||||
void UnbindGraphicsStorageBuffers(size_t stage);
|
void UnbindGraphicsStorageBuffers(size_t stage);
|
||||||
|
|
||||||
bool BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
void BindGraphicsStorageBuffer(size_t stage, size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset,
|
||||||
bool is_written);
|
bool is_written);
|
||||||
|
|
||||||
void UnbindGraphicsTextureBuffers(size_t stage);
|
void UnbindGraphicsTextureBuffers(size_t stage);
|
||||||
@@ -311,6 +307,10 @@ public:
|
|||||||
current_draw_indirect = current_draw_indirect_;
|
current_draw_indirect = current_draw_indirect_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetDrawInstanceCount(u32 draw_instance_count_) {
|
||||||
|
draw_instance_count = draw_instance_count_;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectCount();
|
||||||
|
|
||||||
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
[[nodiscard]] std::pair<Buffer*, u32> GetDrawIndirectBuffer();
|
||||||
@@ -378,8 +378,6 @@ private:
|
|||||||
|
|
||||||
void BindHostTransformFeedbackBuffers();
|
void BindHostTransformFeedbackBuffers();
|
||||||
|
|
||||||
void BindHostVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
|
||||||
|
|
||||||
void BindHostComputeUniformBuffers();
|
void BindHostComputeUniformBuffers();
|
||||||
|
|
||||||
void BindHostComputeStorageBuffers();
|
void BindHostComputeStorageBuffers();
|
||||||
@@ -445,22 +443,6 @@ private:
|
|||||||
|
|
||||||
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
|
void MappedUploadMemory(Buffer& buffer, u64 total_size_bytes, std::span<BufferCopy> copies);
|
||||||
|
|
||||||
bool TryUnifiedDownloadMemory(Buffer& buffer, std::span<BufferCopy> copies);
|
|
||||||
|
|
||||||
struct UnifiedWindowRange {
|
|
||||||
size_t window;
|
|
||||||
u64 offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::optional<UnifiedWindowRange> TryResolveUnifiedRange(DAddr device_addr, u64 size);
|
|
||||||
|
|
||||||
using UnifiedWindowGroups =
|
|
||||||
boost::container::small_vector<boost::container::small_vector<BufferCopy, 16>, 4>;
|
|
||||||
|
|
||||||
bool ResolveUnifiedWindows(DAddr device_addr, u64 buffer_offset, u64 size,
|
|
||||||
boost::container::small_vector<u64, 4>& window_ids,
|
|
||||||
UnifiedWindowGroups& groups);
|
|
||||||
|
|
||||||
void DownloadBufferMemory(Buffer& buffer_id);
|
void DownloadBufferMemory(Buffer& buffer_id);
|
||||||
|
|
||||||
void DownloadBufferMemory(Buffer& buffer_id, DAddr device_addr, u64 size);
|
void DownloadBufferMemory(Buffer& buffer_id, DAddr device_addr, u64 size);
|
||||||
@@ -502,6 +484,10 @@ private:
|
|||||||
|
|
||||||
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
||||||
|
|
||||||
|
u32 draw_instance_count = 0;
|
||||||
|
u32 last_draw_instance_count = 0;
|
||||||
|
u32 last_draw_base_instance = 0;
|
||||||
|
|
||||||
u32 last_index_count = 0;
|
u32 last_index_count = 0;
|
||||||
|
|
||||||
u32 enabled_vertex_buffers_mask = 0;
|
u32 enabled_vertex_buffers_mask = 0;
|
||||||
@@ -516,14 +502,9 @@ private:
|
|||||||
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
|
std::deque<Common::RangeSet<DAddr>> committed_gpu_modified_ranges;
|
||||||
|
|
||||||
// Async Buffers
|
// Async Buffers
|
||||||
struct AsyncDownloadBatch {
|
|
||||||
boost::container::small_vector<BufferCopy, 4> staging_copies;
|
|
||||||
boost::container::small_vector<BufferCopy, 4> unified_copies;
|
|
||||||
};
|
|
||||||
|
|
||||||
Common::OverlapRangeSet<DAddr> async_downloads;
|
Common::OverlapRangeSet<DAddr> async_downloads;
|
||||||
std::deque<std::optional<Async_Buffer>> async_buffers;
|
std::deque<std::optional<Async_Buffer>> async_buffers;
|
||||||
std::deque<AsyncDownloadBatch> pending_downloads;
|
std::deque<boost::container::small_vector<BufferCopy, 4>> pending_downloads;
|
||||||
std::optional<Async_Buffer> current_buffer;
|
std::optional<Async_Buffer> current_buffer;
|
||||||
|
|
||||||
std::deque<Async_Buffer> async_buffers_death_ring;
|
std::deque<Async_Buffer> async_buffers_death_ring;
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ set(SHADER_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d_buffer.comp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_bcn.comp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d_buffer.comp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d24s8.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_abgr8_to_d32f.frag
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
${CMAKE_CURRENT_SOURCE_DIR}/convert_d32f_to_abgr8.frag
|
||||||
@@ -208,6 +206,40 @@ foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
|||||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
set(SHADER_DEFINE_VARIANTS
|
||||||
|
"block_linear_unswizzle_2d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||||
|
"pitch_unswizzle.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||||
|
"block_linear_unswizzle_3d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(VARIANT IN ITEMS ${SHADER_DEFINE_VARIANTS})
|
||||||
|
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
||||||
|
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
||||||
|
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
||||||
|
list(GET VARIANT_PARTS 2 VARIANT_DEFINE)
|
||||||
|
|
||||||
|
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
||||||
|
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
||||||
|
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
||||||
|
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
||||||
|
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
||||||
|
|
||||||
|
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
||||||
|
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT
|
||||||
|
${VARIANT_HEADER_FILE}
|
||||||
|
COMMAND
|
||||||
|
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
||||||
|
-D${VARIANT_DEFINE}
|
||||||
|
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
||||||
|
--target-env ${SPIR_V_VERSION}
|
||||||
|
MAIN_DEPENDENCY
|
||||||
|
${VARIANT_SOURCE}
|
||||||
|
)
|
||||||
|
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||||
|
|||||||
@@ -5,9 +5,13 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
|
#ifndef HAS_EXTENDED_TYPES
|
||||||
|
#define HAS_EXTENDED_TYPES 1
|
||||||
|
#endif
|
||||||
|
#if HAS_EXTENDED_TYPES
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#define HAS_EXTENDED_TYPES 1
|
#endif
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 430
|
|
||||||
|
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
|
||||||
|
|
||||||
#define BINDING_INPUT_BUFFER 0
|
|
||||||
#define BINDING_OUTPUT_BUFFER 1
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
uvec3 dim;
|
|
||||||
uint bytes_per_block_log2;
|
|
||||||
|
|
||||||
uvec3 origin;
|
|
||||||
uint layer_stride;
|
|
||||||
|
|
||||||
uint block_size;
|
|
||||||
uint x_shift;
|
|
||||||
uint block_height;
|
|
||||||
uint block_height_mask;
|
|
||||||
} pc;
|
|
||||||
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
|
||||||
|
|
||||||
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
|
||||||
uint out_u32[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(local_size_x = 16, local_size_y = 8, local_size_z = 1) in;
|
|
||||||
|
|
||||||
const uint GOB_SIZE_X = 64;
|
|
||||||
const uint GOB_SIZE_Y = 8;
|
|
||||||
|
|
||||||
const uint GOB_SIZE_X_SHIFT = 6;
|
|
||||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
|
||||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
|
||||||
|
|
||||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
|
||||||
|
|
||||||
uint SwizzleTable(uint pos) {
|
|
||||||
const uint t[8] = uint[](
|
|
||||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
|
||||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
|
||||||
);
|
|
||||||
const uint i = pos >> 4;
|
|
||||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
|
||||||
return (h << 4) | (pos & 0xf);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint SwizzleOffset(uvec2 pos) {
|
|
||||||
pos = pos & SWIZZLE_MASK;
|
|
||||||
return SwizzleTable(pos.y * 64u + pos.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec4 ReadTexel(uint offset) {
|
|
||||||
switch (pc.bytes_per_block_log2) {
|
|
||||||
case 2u:
|
|
||||||
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
|
||||||
case 3u:
|
|
||||||
return uvec4(u64data[offset / 8u], 0u, 0u);
|
|
||||||
case 4u:
|
|
||||||
return u128data[offset / 16u];
|
|
||||||
}
|
|
||||||
return uvec4(0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uvec3 coord = gl_GlobalInvocationID;
|
|
||||||
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec3 pos = coord + pc.origin;
|
|
||||||
pos.x <<= pc.bytes_per_block_log2;
|
|
||||||
|
|
||||||
uint swizzle = SwizzleOffset(pos.xy);
|
|
||||||
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
|
||||||
|
|
||||||
uint offset = 0u;
|
|
||||||
offset += pos.z * pc.layer_stride;
|
|
||||||
offset += (block_y >> pc.block_height) * pc.block_size;
|
|
||||||
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
|
||||||
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
|
||||||
offset += swizzle;
|
|
||||||
|
|
||||||
uvec4 texel = ReadTexel(offset);
|
|
||||||
|
|
||||||
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
|
||||||
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
|
||||||
uint out_idx = linear_index * words;
|
|
||||||
|
|
||||||
out_u32[out_idx] = texel.x;
|
|
||||||
if (words > 1u) {
|
|
||||||
out_u32[out_idx + 1u] = texel.y;
|
|
||||||
}
|
|
||||||
if (words > 2u) {
|
|
||||||
out_u32[out_idx + 2u] = texel.z;
|
|
||||||
out_u32[out_idx + 3u] = texel.w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,13 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
|
#ifndef HAS_EXTENDED_TYPES
|
||||||
|
#define HAS_EXTENDED_TYPES 1
|
||||||
|
#endif
|
||||||
|
#if HAS_EXTENDED_TYPES
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#define HAS_EXTENDED_TYPES 1
|
#endif
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#version 430
|
|
||||||
|
|
||||||
#define BINDING_INPUT_BUFFER 0
|
|
||||||
#define BINDING_OUTPUT_BUFFER 1
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
uvec3 dim;
|
|
||||||
uint bytes_per_block_log2;
|
|
||||||
|
|
||||||
uvec3 origin;
|
|
||||||
uint slice_size;
|
|
||||||
|
|
||||||
uint block_size;
|
|
||||||
uint x_shift;
|
|
||||||
uint block_height;
|
|
||||||
uint block_height_mask;
|
|
||||||
|
|
||||||
uint block_depth;
|
|
||||||
uint block_depth_mask;
|
|
||||||
} pc;
|
|
||||||
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
|
|
||||||
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; };
|
|
||||||
|
|
||||||
layout(binding = BINDING_OUTPUT_BUFFER, std430) writeonly buffer OutputBuffer {
|
|
||||||
uint out_u32[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 4) in;
|
|
||||||
|
|
||||||
const uint GOB_SIZE_X = 64;
|
|
||||||
const uint GOB_SIZE_Y = 8;
|
|
||||||
|
|
||||||
const uint GOB_SIZE_X_SHIFT = 6;
|
|
||||||
const uint GOB_SIZE_Y_SHIFT = 3;
|
|
||||||
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT;
|
|
||||||
|
|
||||||
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
|
|
||||||
|
|
||||||
uint SwizzleTable(uint pos) {
|
|
||||||
const uint t[8] = uint[](
|
|
||||||
0x12100200, 0x13110301, 0x16140604, 0x17150705,
|
|
||||||
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
|
|
||||||
);
|
|
||||||
const uint i = pos >> 4;
|
|
||||||
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
|
|
||||||
return (h << 4) | (pos & 0xf);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint SwizzleOffset(uvec2 pos) {
|
|
||||||
pos = pos & SWIZZLE_MASK;
|
|
||||||
return SwizzleTable(pos.y * 64u + pos.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec4 ReadTexel(uint offset) {
|
|
||||||
switch (pc.bytes_per_block_log2) {
|
|
||||||
case 2u:
|
|
||||||
return uvec4(u32data[offset / 4u], 0u, 0u, 0u);
|
|
||||||
case 3u:
|
|
||||||
return uvec4(u64data[offset / 8u], 0u, 0u);
|
|
||||||
case 4u:
|
|
||||||
return u128data[offset / 16u];
|
|
||||||
}
|
|
||||||
return uvec4(0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uvec3 coord = gl_GlobalInvocationID;
|
|
||||||
if (coord.x >= pc.dim.x || coord.y >= pc.dim.y || coord.z >= pc.dim.z) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uvec3 pos = coord + pc.origin;
|
|
||||||
pos.x <<= pc.bytes_per_block_log2;
|
|
||||||
|
|
||||||
uint swizzle = SwizzleOffset(pos.xy);
|
|
||||||
uint block_y = pos.y >> GOB_SIZE_Y_SHIFT;
|
|
||||||
|
|
||||||
uint offset = 0u;
|
|
||||||
offset += (pos.z >> pc.block_depth) * pc.slice_size;
|
|
||||||
offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height);
|
|
||||||
offset += (block_y >> pc.block_height) * pc.block_size;
|
|
||||||
offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT;
|
|
||||||
offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift;
|
|
||||||
offset += swizzle;
|
|
||||||
|
|
||||||
uvec4 texel = ReadTexel(offset);
|
|
||||||
|
|
||||||
uint words = 1u << (pc.bytes_per_block_log2 - 2u);
|
|
||||||
uint linear_index = coord.x + coord.y * pc.dim.x + coord.z * pc.dim.x * pc.dim.y;
|
|
||||||
uint out_idx = linear_index * words;
|
|
||||||
|
|
||||||
out_u32[out_idx] = texel.x;
|
|
||||||
if (words > 1u) {
|
|
||||||
out_u32[out_idx + 1u] = texel.y;
|
|
||||||
}
|
|
||||||
if (words > 2u) {
|
|
||||||
out_u32[out_idx + 2u] = texel.z;
|
|
||||||
out_u32[out_idx + 3u] = texel.w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,13 @@
|
|||||||
|
|
||||||
#ifdef VULKAN
|
#ifdef VULKAN
|
||||||
|
|
||||||
|
#ifndef HAS_EXTENDED_TYPES
|
||||||
|
#define HAS_EXTENDED_TYPES 1
|
||||||
|
#endif
|
||||||
|
#if HAS_EXTENDED_TYPES
|
||||||
#extension GL_EXT_shader_16bit_storage : require
|
#extension GL_EXT_shader_16bit_storage : require
|
||||||
#extension GL_EXT_shader_8bit_storage : require
|
#extension GL_EXT_shader_8bit_storage : require
|
||||||
#define HAS_EXTENDED_TYPES 1
|
#endif
|
||||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||||
#define END_PUSH_CONSTANTS };
|
#define END_PUSH_CONSTANTS };
|
||||||
#define UNIFORM(n)
|
#define UNIFORM(n)
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
precision highp float;
|
precision highp float;
|
||||||
precision highp int;
|
precision highp int;
|
||||||
|
|
||||||
|
// Operation modes: RGBA -> 1, RGBY -> 3, LERP -> 4
|
||||||
|
#define OPERATION_MODE 1
|
||||||
#define EDGE_THRESHOLD (8.0 / 255.0)
|
#define EDGE_THRESHOLD (8.0 / 255.0)
|
||||||
#define DIRECTION_EPSILON 6.5e-05
|
|
||||||
#define DEVIATION_FLOOR 6.0e-02
|
|
||||||
|
|
||||||
layout(push_constant) uniform constants {
|
layout(push_constant) uniform constants {
|
||||||
vec2 scale;
|
vec2 scale;
|
||||||
@@ -21,37 +21,20 @@ layout(set = 0, binding = 0) uniform sampler2D sampler0;
|
|||||||
layout(location=0) in vec2 texcoord;
|
layout(location=0) in vec2 texcoord;
|
||||||
layout(location=0) out vec4 frag_color;
|
layout(location=0) out vec4 frag_color;
|
||||||
|
|
||||||
mediump vec4 fastLanczos2(mediump vec4 x) {
|
vec4 weightY(vec4 dx, vec4 dy, vec4 std) {
|
||||||
mediump vec4 wA = x - 4.0f;
|
vec4 x = ((dx * dx) + (dy * dy)) * 0.55f + std;
|
||||||
mediump vec4 wB = x * wA - wA;
|
return (x - 1.f) * (x - 4.f) * 3.8125f; // approx. of (x - 1) * (x - 4)^3
|
||||||
wA *= wA;
|
|
||||||
return wB * wA;
|
|
||||||
}
|
|
||||||
|
|
||||||
mediump vec2 edgeDirection(mediump vec4 left, mediump vec4 right) {
|
|
||||||
mediump float RxLz = right.x - left.z;
|
|
||||||
mediump float RwLy = right.w - left.y;
|
|
||||||
mediump vec2 delta = vec2(RxLz + RwLy, RxLz - RwLy);
|
|
||||||
mediump float length_inv =
|
|
||||||
inversesqrt((delta.x * delta.x + DIRECTION_EPSILON) + delta.y * delta.y);
|
|
||||||
return delta * length_inv;
|
|
||||||
}
|
|
||||||
|
|
||||||
mediump vec4 weightY(mediump vec4 dx, mediump vec4 dy, mediump vec4 c, mediump float std,
|
|
||||||
mediump vec2 dir) {
|
|
||||||
mediump vec4 edge_dis = dx * dir.y + dy * dir.x;
|
|
||||||
mediump vec4 x = (dx * dx + dy * dy) +
|
|
||||||
(edge_dis * edge_dis) * (clamp((c * c) * std, 0.0f, 1.0f) * 0.7f - 1.0f);
|
|
||||||
return fastLanczos2(x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
mediump vec4 color = textureLod(sampler0, texcoord.xy, 0.0f);
|
vec4 color = textureLod(sampler0, texcoord.xy, 0.0f);
|
||||||
highp vec2 icoord = (texcoord * size + vec2(-0.5f, 0.5f));
|
// image coord
|
||||||
highp vec2 icoord_pixel = floor(icoord);
|
vec2 icoord = (texcoord * size + vec2(-0.5f, 0.5f));
|
||||||
highp vec2 coord = icoord_pixel * scale;
|
vec2 icoord_pixel = floor(icoord);
|
||||||
mediump vec2 pl = icoord - icoord_pixel;
|
vec2 coord = icoord_pixel * scale;
|
||||||
mediump mat3x4 dg = mat3x4(
|
vec2 pl = icoord - icoord_pixel;
|
||||||
|
// left: 0, right: 1, upDown: 2
|
||||||
|
mat3x4 dg = mat3x4(
|
||||||
textureGather(sampler0, coord, 1),
|
textureGather(sampler0, coord, 1),
|
||||||
textureGather(sampler0, coord + vec2(2.f * scale.x, 0.0f), 1),
|
textureGather(sampler0, coord + vec2(2.f * scale.x, 0.0f), 1),
|
||||||
vec4(
|
vec4(
|
||||||
@@ -59,40 +42,42 @@ void main() {
|
|||||||
textureGather(sampler0, coord + vec2(scale.x, +scale.y), 1).yx
|
textureGather(sampler0, coord + vec2(scale.x, +scale.y), 1).yx
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
mediump float edgeVote =
|
float edgeVote = abs(dg[0].z - dg[0].y) + abs(color.y - dg[0].y) + abs(color.y - dg[0].z);
|
||||||
abs(dg[0].z - dg[0].y) + abs(color.y - dg[0].y) + abs(color.y - dg[0].z);
|
|
||||||
if (edgeVote > EDGE_THRESHOLD) {
|
if (edgeVote > EDGE_THRESHOLD) {
|
||||||
mediump float mean = (dg[0].y + dg[0].z + dg[1].x + dg[1].w) * 0.25f;
|
float mean = (dg[0].y + dg[0].z + dg[1].x + dg[1].w) * 0.25f;
|
||||||
dg = dg - mean;
|
dg = dg - mean;
|
||||||
mediump float sum = dot(abs(dg[0]) + abs(dg[1]) + abs(dg[2]), vec4(1.0f));
|
vec4 sum = abs(dg[0]) + abs(dg[1]) + abs(dg[2]);
|
||||||
mediump float sum_mean = 1.014185e+01f / max(sum, DEVIATION_FLOOR);
|
float std = 2.181818f / (sum.x + sum.y + sum.z + sum.w);
|
||||||
mediump float std = sum_mean * sum_mean;
|
mat2x4 w = mat2x4(
|
||||||
mediump vec2 dir = edgeDirection(dg[0], dg[1]);
|
weightY(
|
||||||
mediump vec4 w0 = weightY(
|
pl.xxxx + vec4(+1.0f, +0.0f, +0.0f, +1.0f),
|
||||||
pl.xxxx + vec4(+1.0f, +0.0f, +0.0f, +1.0f),
|
pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f),
|
||||||
pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f),
|
clamp(abs(dg[0]) * std, 0.0f, 1.0f)
|
||||||
dg[0], std, dir
|
) + weightY(
|
||||||
|
pl.xxxx + vec4(-1.0f, -2.0f, -2.0f, -1.0f),
|
||||||
|
pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f),
|
||||||
|
clamp(abs(dg[1]) * std, 0.0f, 1.0f)
|
||||||
|
) + weightY(
|
||||||
|
pl.xxxx + vec4(+0.0f, -1.0f, -1.0f, +0.0f),
|
||||||
|
pl.yyyy + vec4(+1.0f, +1.0f, -2.0f, -2.0f),
|
||||||
|
clamp(abs(dg[2]) * std, 0.0f, 1.0f)
|
||||||
|
),
|
||||||
|
dg[0] + dg[1] + dg[2]
|
||||||
);
|
);
|
||||||
mediump vec4 w1 = weightY(
|
// compute final y with bounds
|
||||||
pl.xxxx + vec4(-1.0f, -2.0f, -2.0f, -1.0f),
|
vec2 yb = vec2(
|
||||||
pl.yyyy + vec4(-1.0f, -1.0f, +0.0f, +0.0f),
|
min(min(dg[0].y, dg[0].z), min(dg[1].x, dg[1].w)), // min
|
||||||
dg[1], std, dir
|
max(max(dg[0].y, dg[0].z), max(dg[1].x, dg[1].w)) // max
|
||||||
);
|
);
|
||||||
mediump vec4 w2 = weightY(
|
vec2 fvy = vec2(
|
||||||
pl.xxxx + vec4(+0.0f, -1.0f, -1.0f, +0.0f),
|
w[0].x + w[0].y + w[0].z + w[0].w,
|
||||||
pl.yyyy + vec4(+1.0f, +1.0f, -2.0f, -2.0f),
|
w[1].x + w[1].y + w[1].z + w[1].w
|
||||||
dg[2], std, dir
|
|
||||||
);
|
);
|
||||||
mediump float sum_w = dot(w0 + w1 + w2, vec4(1.0f));
|
float fy = clamp((fvy.y / fvy.x) * edge_sharpness, yb[0], yb[1]);
|
||||||
mediump float sum_wc = dot(w0 * dg[0] + w1 * dg[1] + w2 * dg[2], vec4(1.0f));
|
// Smooth high contrast input
|
||||||
mediump vec2 yb = vec2(
|
float dy = clamp(fy - color.y + mean, -23.0f / 255.0f, 23.0f / 255.0f);
|
||||||
min(min(dg[0].y, dg[0].z), min(dg[1].x, dg[1].w)),
|
|
||||||
max(max(dg[0].y, dg[0].z), max(dg[1].x, dg[1].w))
|
|
||||||
);
|
|
||||||
mediump float fy = clamp((sum_wc / sum_w) * edge_sharpness, yb[0], yb[1]);
|
|
||||||
mediump float dy = clamp(fy - color.y + mean, -23.0f / 255.0f, 23.0f / 255.0f);
|
|
||||||
color = clamp(color + dy, 0.0f, 1.0f);
|
color = clamp(color + dy, 0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
color.w = 1.0f;
|
color.w = 1.0f; //assume alpha channel is not used
|
||||||
frag_color.xyzw = color;
|
frag_color.xyzw = color;
|
||||||
}
|
}
|
||||||
@@ -226,22 +226,6 @@ void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
|
|
||||||
u32 stride) {
|
|
||||||
if (index >= max_attributes) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (has_unified_vertex_buffers) {
|
|
||||||
buffer.MakeResident(GL_READ_ONLY);
|
|
||||||
glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
|
|
||||||
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
|
|
||||||
buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
|
|
||||||
} else {
|
|
||||||
glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
|
|
||||||
static_cast<GLsizei>(stride));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
|
// TODO: Should HostBindings provide the correct runtime types to avoid these transforms?
|
||||||
std::array<GLuint, 32> buffer_handles;
|
std::array<GLuint, 32> buffer_handles;
|
||||||
|
|||||||
@@ -99,8 +99,6 @@ public:
|
|||||||
|
|
||||||
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size);
|
||||||
|
|
||||||
void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride);
|
|
||||||
|
|
||||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||||
|
|
||||||
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size);
|
||||||
@@ -261,7 +259,6 @@ struct BufferCacheParams {
|
|||||||
|
|
||||||
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
|
// TODO: Investigate why OpenGL seems to perform worse with persistently mapped buffer uploads
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = false;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
|
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
||||||
|
buffer_cache.SetDrawInstanceCount(instance_count);
|
||||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
|
PrepareDraw(is_indexed, [this, is_indexed, instance_count](GLenum primitive_mode) {
|
||||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||||
const GLuint base_instance = GLuint(draw_state.base_instance);
|
const GLuint base_instance = GLuint(draw_state.base_instance);
|
||||||
@@ -304,6 +305,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, u32 instance_count) {
|
|||||||
void RasterizerOpenGL::DrawIndirect() {
|
void RasterizerOpenGL::DrawIndirect() {
|
||||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||||
buffer_cache.SetDrawIndirect(¶ms);
|
buffer_cache.SetDrawIndirect(¶ms);
|
||||||
|
buffer_cache.SetDrawInstanceCount(0);
|
||||||
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
PrepareDraw(params.is_indexed, [this, ¶ms](GLenum primitive_mode) {
|
||||||
if (params.is_byte_count) {
|
if (params.is_byte_count) {
|
||||||
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
|
||||||
|
|||||||
@@ -238,7 +238,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
|
.has_gl_bool_ref_bug = device.HasBoolRefBug(),
|
||||||
.ignore_nan_fp_comparisons = true,
|
.ignore_nan_fp_comparisons = true,
|
||||||
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
|
||||||
.max_shared_memory_size = device.GetMaxComputeSharedMemorySize(),
|
|
||||||
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
|
||||||
// Use the host limit, but never more than the guest can produce. Maxwell exposes 8 clip
|
// Use the host limit, but never more than the guest can produce. Maxwell exposes 8 clip
|
||||||
// distances and the SPIR-V output array is sized for at most 8, so clamping here keeps a
|
// distances and the SPIR-V output array is sized for at most 8, so clamping here keeps a
|
||||||
|
|||||||
@@ -373,7 +373,6 @@ struct TextureCacheParams {
|
|||||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||||
static constexpr bool HAS_MSAA_DOWNLOADS = false;
|
static constexpr bool HAS_MSAA_DOWNLOADS = false;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = false;
|
|
||||||
|
|
||||||
using Runtime = OpenGL::TextureCacheRuntime;
|
using Runtime = OpenGL::TextureCacheRuntime;
|
||||||
using Image = OpenGL::Image;
|
using Image = OpenGL::Image;
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||||
@@ -34,32 +32,6 @@ VkBufferCopy MakeBufferCopy(const VideoCommon::BufferCopy& copy) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t MAX_WINDOW_BARRIER_RANGES = 8;
|
|
||||||
|
|
||||||
using WindowRange = std::pair<VkDeviceSize, VkDeviceSize>;
|
|
||||||
using WindowRanges = boost::container::small_vector<WindowRange, MAX_WINDOW_BARRIER_RANGES>;
|
|
||||||
|
|
||||||
void CoalesceWindowRanges(WindowRanges& ranges) {
|
|
||||||
if (ranges.size() < 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::sort(ranges.begin(), ranges.end());
|
|
||||||
size_t merged = 0;
|
|
||||||
for (size_t index = 1; index < ranges.size(); ++index) {
|
|
||||||
if (ranges[index].first <= ranges[merged].second) {
|
|
||||||
ranges[merged].second = (std::max)(ranges[merged].second, ranges[index].second);
|
|
||||||
} else {
|
|
||||||
ranges[++merged] = ranges[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ranges.resize(merged + 1);
|
|
||||||
if (ranges.size() > MAX_WINDOW_BARRIER_RANGES) {
|
|
||||||
const WindowRange bounding{ranges.front().first, ranges.back().second};
|
|
||||||
ranges.clear();
|
|
||||||
ranges.push_back(bounding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VkIndexType IndexTypeFromNumElements(const Device& device, u32 num_elements) {
|
VkIndexType IndexTypeFromNumElements(const Device& device, u32 num_elements) {
|
||||||
if (num_elements <= 0xff && device.IsExtIndexTypeUint8Supported()) {
|
if (num_elements <= 0xff && device.IsExtIndexTypeUint8Supported()) {
|
||||||
return VK_INDEX_TYPE_UINT8_EXT;
|
return VK_INDEX_TYPE_UINT8_EXT;
|
||||||
@@ -70,16 +42,6 @@ VkIndexType IndexTypeFromNumElements(const Device& device, u32 num_elements) {
|
|||||||
return VK_INDEX_TYPE_UINT32;
|
return VK_INDEX_TYPE_UINT32;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GrowIndexCount(u32 current, u32 requested) {
|
|
||||||
constexpr u32 MinimumIndices = 4096;
|
|
||||||
constexpr u32 GrowthLimit = (std::numeric_limits<u32>::max)() / 2;
|
|
||||||
u32 grown = (std::max)(requested, MinimumIndices);
|
|
||||||
if (current <= GrowthLimit) {
|
|
||||||
grown = (std::max)(grown, current * 2);
|
|
||||||
}
|
|
||||||
return grown;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t BytesPerIndex(VkIndexType index_type) {
|
size_t BytesPerIndex(VkIndexType index_type) {
|
||||||
switch (index_type) {
|
switch (index_type) {
|
||||||
case VK_INDEX_TYPE_UINT8_EXT:
|
case VK_INDEX_TYPE_UINT8_EXT:
|
||||||
@@ -196,12 +158,13 @@ public:
|
|||||||
virtual ~QuadIndexBuffer() = default;
|
virtual ~QuadIndexBuffer() = default;
|
||||||
|
|
||||||
void UpdateBuffer(u32 num_indices_) {
|
void UpdateBuffer(u32 num_indices_) {
|
||||||
ReleaseRetiredBuffers();
|
|
||||||
if (num_indices_ <= num_indices) {
|
if (num_indices_ <= num_indices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_indices = GrowIndexCount(num_indices, num_indices_);
|
scheduler.Finish();
|
||||||
|
|
||||||
|
num_indices = num_indices_;
|
||||||
index_type = IndexTypeFromNumElements(device, num_indices);
|
index_type = IndexTypeFromNumElements(device, num_indices);
|
||||||
|
|
||||||
const u32 num_quads = GetQuadsNum(num_indices);
|
const u32 num_quads = GetQuadsNum(num_indices);
|
||||||
@@ -219,12 +182,6 @@ public:
|
|||||||
.queueFamilyIndexCount = 0,
|
.queueFamilyIndexCount = 0,
|
||||||
.pQueueFamilyIndices = nullptr,
|
.pQueueFamilyIndices = nullptr,
|
||||||
};
|
};
|
||||||
if (buffer) {
|
|
||||||
retired_buffers.push_back(RetiredBuffer{
|
|
||||||
.buffer = std::move(buffer),
|
|
||||||
.tick = scheduler.CurrentTick(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
buffer = memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
buffer = memory_allocator.CreateBuffer(buffer_ci, MemoryUsage::DeviceLocal);
|
||||||
if (device.HasDebuggingToolAttached()) {
|
if (device.HasDebuggingToolAttached()) {
|
||||||
buffer.SetObjectNameEXT("Quad LUT");
|
buffer.SetObjectNameEXT("Quad LUT");
|
||||||
@@ -292,17 +249,6 @@ protected:
|
|||||||
|
|
||||||
virtual void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) = 0;
|
virtual void MakeAndUpdateIndices(u8* staging_data, size_t quad_size, u32 quad, u32 first) = 0;
|
||||||
|
|
||||||
struct RetiredBuffer {
|
|
||||||
vk::Buffer buffer;
|
|
||||||
u64 tick;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ReleaseRetiredBuffers() {
|
|
||||||
std::erase_if(retired_buffers, [this](const RetiredBuffer& entry) {
|
|
||||||
return scheduler.IsFree(entry.tick);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const Device& device;
|
const Device& device;
|
||||||
MemoryAllocator& memory_allocator;
|
MemoryAllocator& memory_allocator;
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
@@ -310,7 +256,6 @@ protected:
|
|||||||
|
|
||||||
vk::Buffer buffer{};
|
vk::Buffer buffer{};
|
||||||
MemoryCommit memory_commit{};
|
MemoryCommit memory_commit{};
|
||||||
std::vector<RetiredBuffer> retired_buffers;
|
|
||||||
VkIndexType index_type{};
|
VkIndexType index_type{};
|
||||||
u32 num_indices = 0;
|
u32 num_indices = 0;
|
||||||
};
|
};
|
||||||
@@ -420,138 +365,6 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
|
|||||||
scheduler_, staging_pool_);
|
scheduler_, staging_pool_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::TryEnableUnifiedMemory(void* base, size_t size,
|
|
||||||
std::span<AHardwareBuffer* const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window,
|
|
||||||
size_t hardware_buffer_base) {
|
|
||||||
unified_memory = memory_allocator.CreateHostMemoryImport(
|
|
||||||
base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::CopyToUnifiedMemory(
|
|
||||||
size_t window_index, VkBuffer src_buffer,
|
|
||||||
std::span<const VideoCommon::BufferCopy> copies) {
|
|
||||||
if (!unified_memory || src_buffer == VK_NULL_HANDLE || copies.empty() ||
|
|
||||||
window_index >= unified_memory->GetWindowCount() ||
|
|
||||||
unified_memory->GetWindowBuffer(window_index) == VK_NULL_HANDLE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PendingUnifiedCopy& pending = pending_unified_copies.emplace_back();
|
|
||||||
pending.window = window_index;
|
|
||||||
pending.buffer = src_buffer;
|
|
||||||
pending.copies.resize(copies.size());
|
|
||||||
std::ranges::transform(copies, pending.copies.begin(), MakeBufferCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::FlushUnifiedMemoryCopies() {
|
|
||||||
if (pending_unified_copies.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct UnifiedCopyCommand {
|
|
||||||
VkBuffer buffer;
|
|
||||||
boost::container::small_vector<VkBufferCopy, 8> copies;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::stable_sort(pending_unified_copies.begin(), pending_unified_copies.end(),
|
|
||||||
[](const PendingUnifiedCopy& lhs, const PendingUnifiedCopy& rhs) {
|
|
||||||
return lhs.window < rhs.window;
|
|
||||||
});
|
|
||||||
|
|
||||||
const bool foreign = unified_memory->NeedsForeignOwnershipTransfer();
|
|
||||||
const u32 queue_family = device.GetGraphicsFamily();
|
|
||||||
|
|
||||||
size_t group_begin = 0;
|
|
||||||
while (group_begin < pending_unified_copies.size()) {
|
|
||||||
const size_t window = pending_unified_copies[group_begin].window;
|
|
||||||
size_t group_end = group_begin;
|
|
||||||
while (group_end < pending_unified_copies.size() &&
|
|
||||||
pending_unified_copies[group_end].window == window) {
|
|
||||||
++group_end;
|
|
||||||
}
|
|
||||||
const VkBuffer window_buffer = unified_memory->GetWindowBuffer(window);
|
|
||||||
|
|
||||||
WindowRanges ranges;
|
|
||||||
for (size_t index = group_begin; index < group_end; ++index) {
|
|
||||||
for (const VkBufferCopy& copy : pending_unified_copies[index].copies) {
|
|
||||||
ranges.emplace_back(copy.dstOffset, copy.dstOffset + copy.size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CoalesceWindowRanges(ranges);
|
|
||||||
|
|
||||||
boost::container::small_vector<VkBufferMemoryBarrier, MAX_WINDOW_BARRIER_RANGES> acquire;
|
|
||||||
boost::container::small_vector<VkBufferMemoryBarrier, MAX_WINDOW_BARRIER_RANGES> release;
|
|
||||||
if (foreign) {
|
|
||||||
for (const WindowRange& range : ranges) {
|
|
||||||
acquire.push_back(VkBufferMemoryBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = 0,
|
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
|
|
||||||
.dstQueueFamilyIndex = queue_family,
|
|
||||||
.buffer = window_buffer,
|
|
||||||
.offset = range.first,
|
|
||||||
.size = range.second - range.first,
|
|
||||||
});
|
|
||||||
release.push_back(VkBufferMemoryBarrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.dstAccessMask = 0,
|
|
||||||
.srcQueueFamilyIndex = queue_family,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT,
|
|
||||||
.buffer = window_buffer,
|
|
||||||
.offset = range.first,
|
|
||||||
.size = range.second - range.first,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::container::small_vector<UnifiedCopyCommand, 4> commands;
|
|
||||||
commands.reserve(group_end - group_begin);
|
|
||||||
for (size_t index = group_begin; index < group_end; ++index) {
|
|
||||||
PendingUnifiedCopy& pending = pending_unified_copies[index];
|
|
||||||
commands.push_back(UnifiedCopyCommand{pending.buffer, std::move(pending.copies)});
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
scheduler.Record([window_buffer, acquire = std::move(acquire), release = std::move(release),
|
|
||||||
commands = std::move(commands)](vk::CommandBuffer cmdbuf) {
|
|
||||||
if (!acquire.empty()) {
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {},
|
|
||||||
VideoCommon::FixSmallVectorADL(acquire), {});
|
|
||||||
}
|
|
||||||
for (const UnifiedCopyCommand& command : commands) {
|
|
||||||
cmdbuf.CopyBuffer(command.buffer, window_buffer,
|
|
||||||
VideoCommon::FixSmallVectorADL(command.copies));
|
|
||||||
}
|
|
||||||
if (!release.empty()) {
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, {},
|
|
||||||
VideoCommon::FixSmallVectorADL(release), {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
group_begin = group_end;
|
|
||||||
}
|
|
||||||
pending_unified_copies.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::UnifiedMemoryHostBarrier() {
|
|
||||||
static constexpr VkMemoryBarrier HOST_BARRIER{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
|
|
||||||
};
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
|
||||||
scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0,
|
|
||||||
HOST_BARRIER);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
StagingBufferRef BufferCacheRuntime::UploadStagingBuffer(size_t size) {
|
||||||
return staging_pool.Request(size, MemoryUsage::Upload);
|
return staging_pool.Request(size, MemoryUsage::Upload);
|
||||||
}
|
}
|
||||||
@@ -589,7 +402,6 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
|
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
|
||||||
FlushUnifiedMemoryCopies();
|
|
||||||
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
|
||||||
if (scheduler.IsFree(it->LastUsageTick())) {
|
if (scheduler.IsFree(it->LastUsageTick())) {
|
||||||
it->ResetUsageTracking();
|
it->ResetUsageTracking();
|
||||||
@@ -773,29 +585,6 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) {
|
|
||||||
if (index >= device.GetMaxVertexInputBindings()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (device.IsExtExtendedDynamicStateSupported()) {
|
|
||||||
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
|
|
||||||
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
|
|
||||||
const VkDeviceSize vk_stride = stride;
|
|
||||||
cmdbuf.BindVertexBuffers2EXT(index, 1, &buffer, &vk_offset, &vk_size, &vk_stride);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (!device.HasNullDescriptor() && buffer == VK_NULL_HANDLE) {
|
|
||||||
ReserveNullBuffer();
|
|
||||||
buffer = *null_buffer;
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
scheduler.Record([index, buffer, offset](vk::CommandBuffer cmdbuf) {
|
|
||||||
cmdbuf.BindVertexBuffer(index, buffer, offset);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
|
boost::container::static_vector<VkBuffer, VideoCommon::NUM_VERTEX_BUFFERS> buffer_handles(bindings.buffers.size());
|
||||||
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
|
for (u32 i = 0; i < bindings.buffers.size(); ++i) {
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
|
|
||||||
#include "video_core/buffer_cache/buffer_cache_base.h"
|
#include "video_core/buffer_cache/buffer_cache_base.h"
|
||||||
#include "video_core/buffer_cache/memory_tracker_base.h"
|
#include "video_core/buffer_cache/memory_tracker_base.h"
|
||||||
@@ -101,33 +97,6 @@ public:
|
|||||||
|
|
||||||
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
|
void TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept;
|
||||||
|
|
||||||
void TryEnableUnifiedMemory(void* base, size_t size,
|
|
||||||
std::span<AHardwareBuffer* const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window, size_t hardware_buffer_base);
|
|
||||||
|
|
||||||
[[nodiscard]] bool HasUnifiedMemory() const noexcept {
|
|
||||||
return unified_memory != nullptr && unified_memory->IsValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemorySize() const noexcept {
|
|
||||||
return unified_memory ? unified_memory->GetSize() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemoryBase() const noexcept {
|
|
||||||
return unified_memory ? unified_memory->GetBaseOffset() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept {
|
|
||||||
return unified_memory ? unified_memory->GetWindowSize() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CopyToUnifiedMemory(size_t window_index, VkBuffer src_buffer,
|
|
||||||
std::span<const VideoCommon::BufferCopy> copies);
|
|
||||||
|
|
||||||
void FlushUnifiedMemoryCopies();
|
|
||||||
|
|
||||||
void UnifiedMemoryHostBarrier();
|
|
||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
u64 KnownGpuTick();
|
u64 KnownGpuTick();
|
||||||
@@ -169,8 +138,6 @@ public:
|
|||||||
|
|
||||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||||
|
|
||||||
void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride);
|
|
||||||
|
|
||||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||||
|
|
||||||
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
|
||||||
@@ -195,29 +162,6 @@ public:
|
|||||||
BindBuffer(buffer, offset, size);
|
BindBuffer(buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindStorageBuffer(VkBuffer buffer, VkDeviceAddress address, u32 offset, u32 size,
|
|
||||||
[[maybe_unused]] bool is_written) {
|
|
||||||
guest_descriptor_queue.AddBuffer(buffer, address, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsUnifiedMemoryBindable() const noexcept {
|
|
||||||
return unified_memory != nullptr && unified_memory->IsValid() &&
|
|
||||||
unified_memory->IsBindable();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkBuffer UnifiedWindowBuffer(size_t index) const noexcept {
|
|
||||||
return unified_memory->GetWindowBuffer(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceAddress UnifiedWindowAddress(size_t index) const noexcept {
|
|
||||||
return unified_memory->GetWindowAddress(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsUnifiedStorageRange(u32 size, u64 offset) const {
|
|
||||||
return size <= device.GetMaxStorageBufferRange() &&
|
|
||||||
(offset % device.GetStorageBufferAlignment()) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||||
VideoCore::Surface::PixelFormat format) {
|
VideoCore::Surface::PixelFormat format) {
|
||||||
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format),
|
guest_descriptor_queue.AddTexelBuffer(buffer.View(offset, size, format),
|
||||||
@@ -234,12 +178,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct PendingUnifiedCopy {
|
|
||||||
size_t window;
|
|
||||||
VkBuffer buffer;
|
|
||||||
boost::container::small_vector<VkBufferCopy, 8> copies;
|
|
||||||
};
|
|
||||||
|
|
||||||
void BindBuffer(const Buffer& buffer, u32 offset, u32 size) {
|
void BindBuffer(const Buffer& buffer, u32 offset, u32 size) {
|
||||||
const VkBuffer handle = buffer.Handle();
|
const VkBuffer handle = buffer.Handle();
|
||||||
if (handle == VK_NULL_HANDLE) {
|
if (handle == VK_NULL_HANDLE) {
|
||||||
@@ -264,8 +202,6 @@ private:
|
|||||||
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
|
std::shared_ptr<QuadStripIndexBuffer> quad_strip_index_buffer;
|
||||||
|
|
||||||
vk::Buffer null_buffer;
|
vk::Buffer null_buffer;
|
||||||
HostMemoryImport* unified_memory{};
|
|
||||||
boost::container::small_vector<PendingUnifiedCopy, 8> pending_unified_copies;
|
|
||||||
|
|
||||||
std::unique_ptr<Uint8Pass> uint8_pass;
|
std::unique_ptr<Uint8Pass> uint8_pass;
|
||||||
QuadIndexedPass quad_index_pass;
|
QuadIndexedPass quad_index_pass;
|
||||||
@@ -288,7 +224,6 @@ struct BufferCacheParams {
|
|||||||
static constexpr bool USE_MEMORY_MAPS = true;
|
static constexpr bool USE_MEMORY_MAPS = true;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
|
using BufferCache = VideoCommon::BufferCache<BufferCacheParams>;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@@ -13,7 +12,6 @@
|
|||||||
|
|
||||||
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
||||||
|
|
||||||
#include "common/alignment.h"
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/div_ceil.h"
|
#include "common/div_ceil.h"
|
||||||
@@ -24,9 +22,13 @@
|
|||||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_buffer_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_2d_nonarrow_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_buffer_comp_spv.h"
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/block_linear_unswizzle_3d_nonarrow_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
|
||||||
|
#include "video_core/host_shaders/pitch_unswizzle_nonarrow_comp_spv.h"
|
||||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
@@ -877,536 +879,290 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr u32 BL2D_BINDING_INPUT_BUFFER = 0;
|
|
||||||
constexpr u32 BL2D_BINDING_OUTPUT_BUFFER = 1;
|
|
||||||
|
|
||||||
struct alignas(16) BlockLinearUnswizzle2DPushConstants {
|
constexpr u32 UNSWIZZLE_BINDING_INPUT_BUFFER = 0;
|
||||||
std::array<u32, 3> dim;
|
constexpr u32 UNSWIZZLE_BINDING_OUTPUT_IMAGE = 1;
|
||||||
u32 bytes_per_block_log2;
|
constexpr size_t UNSWIZZLE_NUM_BINDINGS = 2;
|
||||||
std::array<u32, 3> origin;
|
|
||||||
u32 layer_stride;
|
|
||||||
u32 block_size;
|
|
||||||
u32 x_shift;
|
|
||||||
u32 block_height;
|
|
||||||
u32 block_height_mask;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(BlockLinearUnswizzle2DPushConstants) <= 128);
|
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL2D_BINDINGS{{
|
constexpr std::array<VkDescriptorSetLayoutBinding, UNSWIZZLE_NUM_BINDINGS>
|
||||||
{
|
UNSWIZZLE_DESCRIPTOR_SET_BINDINGS{{
|
||||||
.binding = BL2D_BINDING_INPUT_BUFFER,
|
{
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
.binding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||||
.descriptorCount = 1,
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
.descriptorCount = 1,
|
||||||
.pImmutableSamplers = nullptr,
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
},
|
.pImmutableSamplers = nullptr,
|
||||||
{
|
},
|
||||||
.binding = BL2D_BINDING_OUTPUT_BUFFER,
|
{
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
.binding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||||
.descriptorCount = 1,
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
.descriptorCount = 1,
|
||||||
.pImmutableSamplers = nullptr,
|
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
},
|
.pImmutableSamplers = nullptr,
|
||||||
}};
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL2D_TEMPLATE{{
|
constexpr std::array<VkDescriptorUpdateTemplateEntry, UNSWIZZLE_NUM_BINDINGS>
|
||||||
{
|
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE{{
|
||||||
.dstBinding = BL2D_BINDING_INPUT_BUFFER,
|
{
|
||||||
.dstArrayElement = 0,
|
.dstBinding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||||
.descriptorCount = 1,
|
.dstArrayElement = 0,
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
.descriptorCount = 1,
|
||||||
.offset = BL2D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
.offset = UNSWIZZLE_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||||
},
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
{
|
},
|
||||||
.dstBinding = BL2D_BINDING_OUTPUT_BUFFER,
|
{
|
||||||
.dstArrayElement = 0,
|
.dstBinding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||||
.descriptorCount = 1,
|
.dstArrayElement = 0,
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
.descriptorCount = 1,
|
||||||
.offset = BL2D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
.offset = UNSWIZZLE_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry),
|
||||||
},
|
.stride = sizeof(DescriptorUpdateEntry),
|
||||||
}};
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
constexpr DescriptorBankInfo BL2D_BANK_INFO{
|
constexpr DescriptorBankInfo UNSWIZZLE_BANK_INFO{
|
||||||
.uniform_buffers = 0,
|
.uniform_buffers = 0,
|
||||||
.storage_buffers = 2,
|
.storage_buffers = 1,
|
||||||
.texture_buffers = 0,
|
.texture_buffers = 0,
|
||||||
.image_buffers = 0,
|
.image_buffers = 0,
|
||||||
.textures = 0,
|
.textures = 0,
|
||||||
.images = 0,
|
.images = 1,
|
||||||
.score = 2,
|
.score = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const u32> UnswizzleSpv(const Device& device,
|
||||||
|
std::span<const u32> extended,
|
||||||
|
std::span<const u32> narrow) {
|
||||||
|
if (device.IsStorageBuffer8BitAccessSupported() &&
|
||||||
|
device.IsStorageBuffer16BitAccessSupported()) {
|
||||||
|
return extended;
|
||||||
|
}
|
||||||
|
return narrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PitchUnswizzlePushConstants {
|
||||||
|
alignas(8) std::array<u32, 2> origin;
|
||||||
|
alignas(8) std::array<s32, 2> destination;
|
||||||
|
u32 bytes_per_block;
|
||||||
|
u32 pitch;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RecordUnswizzleEntryBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image,
|
||||||
|
VkImageAspectFlags aspect_mask, bool is_initialized) {
|
||||||
|
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||||
|
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||||
|
VkAccessFlags src_access = VK_ACCESS_NONE;
|
||||||
|
VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
if (is_initialized) {
|
||||||
|
src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||||
|
old_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = src_access,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.oldLayout = old_layout,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
if (is_initialized) {
|
||||||
|
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
|
||||||
|
}
|
||||||
|
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||||
|
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordUnswizzleExitBarrier(Scheduler& scheduler, VkImage vk_image,
|
||||||
|
VkImageAspectFlags aspect_mask) {
|
||||||
|
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkImageMemoryBarrier image_barrier{
|
||||||
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||||
|
.pNext = nullptr,
|
||||||
|
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
|
||||||
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
.image = vk_image,
|
||||||
|
.subresourceRange{
|
||||||
|
.aspectMask = aspect_mask,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
: ComputePass(device_, scheduler_, descriptor_pool_, BL2D_BINDINGS, BL2D_TEMPLATE,
|
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||||
BL2D_BANK_INFO,
|
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle2DPushConstants)>,
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(
|
||||||
BLOCK_LINEAR_UNSWIZZLE_2D_BUFFER_COMP_SPV),
|
VideoCommon::Accelerated::BlockLinearSwizzle2DParams)>,
|
||||||
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV,
|
||||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
BLOCK_LINEAR_UNSWIZZLE_2D_NONARROW_COMP_SPV)),
|
||||||
|
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
||||||
|
|
||||||
bool BlockLinearUnswizzle2DPass::IsSupported(const Device& device,
|
|
||||||
const VideoCommon::ImageInfo& info) {
|
|
||||||
if (info.type != VideoCommon::ImageType::e2D) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (info.num_samples > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (device.GetStorageBufferAlignment() > Tegra::Texture::GOB_SIZE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::GetFormatType(info.format) !=
|
|
||||||
VideoCore::Surface::SurfaceType::ColorTexture) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::IsPixelFormatASTC(info.format) && !device.IsOptimalAstcSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
|
||||||
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BlockLinearUnswizzle2DPass::Unswizzle(
|
void BlockLinearUnswizzle2DPass::Unswizzle(
|
||||||
Image& image, const StagingBufferRef& swizzled,
|
Image& image, const StagingBufferRef& map,
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
UnswizzleFrom(image, swizzled.buffer, swizzled.offset, swizzles);
|
using namespace VideoCommon::Accelerated;
|
||||||
}
|
|
||||||
|
|
||||||
void BlockLinearUnswizzle2DPass::UnswizzleFrom(
|
|
||||||
Image& image, VkBuffer source_buffer, VkDeviceSize source_offset,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
|
||||||
const VkImage dst_image = image.Handle();
|
|
||||||
if (swizzles.empty() || source_buffer == VK_NULL_HANDLE || dst_image == VK_NULL_HANDLE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const u32 layers = image.info.resources.layers;
|
|
||||||
const VkImageAspectFlags aspect = image.AspectMask();
|
|
||||||
|
|
||||||
const VkDeviceSize output_alignment =
|
|
||||||
(std::max)(device.GetStorageBufferAlignment(), VkDeviceSize{16});
|
|
||||||
VkDeviceSize total_output = 0;
|
|
||||||
for (const VideoCommon::SwizzleParameters& sw : swizzles) {
|
|
||||||
const auto params =
|
|
||||||
VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
|
|
||||||
const VkDeviceSize level_size = static_cast<VkDeviceSize>(sw.num_tiles.width) *
|
|
||||||
sw.num_tiles.height * layers *
|
|
||||||
(1ULL << params.bytes_per_block_log2);
|
|
||||||
if (level_size == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
total_output = Common::AlignUp(total_output, output_alignment) + level_size;
|
|
||||||
}
|
|
||||||
if (total_output == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const StagingBufferRef output =
|
|
||||||
staging_buffer_pool.Request(static_cast<size_t>(total_output), MemoryUsage::DeviceLocal);
|
|
||||||
const VkBuffer out_buffer = output.buffer;
|
|
||||||
if (out_buffer == VK_NULL_HANDLE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VkDeviceSize level_offset = 0;
|
|
||||||
|
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
|
const VkImage vk_image = image.Handle();
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||||
|
|
||||||
VkAccessFlags pre_access = VK_ACCESS_NONE;
|
const u32 num_layers = static_cast<u32>(image.info.resources.layers);
|
||||||
VkImageLayout pre_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
VkPipelineStageFlags pre_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
if (image.ExchangeInitialization()) {
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||||
pre_access = VK_ACCESS_SHADER_READ_BIT;
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||||
pre_layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
||||||
pre_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE;
|
|
||||||
}
|
|
||||||
scheduler.Record([dst_image, aspect, pre_access, pre_layout,
|
|
||||||
pre_stage](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkImageMemoryBarrier barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = pre_access,
|
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = pre_layout,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(pre_stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, {}, barrier);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const VideoCommon::SwizzleParameters& sw : swizzles) {
|
|
||||||
const auto params =
|
|
||||||
VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info);
|
|
||||||
const u32 width = sw.num_tiles.width;
|
|
||||||
const u32 height = sw.num_tiles.height;
|
|
||||||
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
|
||||||
const VkDeviceSize output_size =
|
|
||||||
static_cast<VkDeviceSize>(width) * height * layers * bytes_per_block;
|
|
||||||
if (output_size == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const u32 level = static_cast<u32>(sw.level);
|
|
||||||
const u32 texel_width = (std::max)(1u, image.info.size.width >> level);
|
|
||||||
const u32 texel_height = (std::max)(1u, image.info.size.height >> level);
|
|
||||||
|
|
||||||
level_offset = Common::AlignUp(level_offset, output_alignment);
|
|
||||||
const VkDeviceSize out_offset = output.offset + level_offset;
|
|
||||||
level_offset += output_size;
|
|
||||||
|
|
||||||
BlockLinearUnswizzle2DPushConstants pc{};
|
|
||||||
pc.dim = {width, height, layers};
|
|
||||||
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
|
||||||
pc.origin = params.origin;
|
|
||||||
pc.layer_stride = params.layer_stride;
|
|
||||||
pc.block_size = params.block_size;
|
|
||||||
pc.x_shift = params.x_shift;
|
|
||||||
pc.block_height = params.block_height;
|
|
||||||
pc.block_height_mask = params.block_height_mask;
|
|
||||||
|
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
compute_pass_descriptor_queue.AddBuffer(source_buffer, sw.buffer_offset + source_offset,
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
image.guest_size_bytes - sw.buffer_offset);
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
compute_pass_descriptor_queue.AddBuffer(out_buffer, out_offset, output_size);
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
|
|
||||||
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_layers, params,
|
||||||
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
const u32 gx = Common::DivCeil(width, 16u);
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
const u32 gy = Common::DivCeil(height, 8u);
|
|
||||||
|
|
||||||
scheduler.Record([this, set, descriptor_data, pc, gx, gy, layers, output_size, out_buffer,
|
|
||||||
out_offset, dst_image, aspect, texel_width, texel_height,
|
|
||||||
level](vk::CommandBuffer cmdbuf) {
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
cmdbuf.Dispatch(gx, gy, layers);
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_layers);
|
||||||
|
|
||||||
const VkBufferMemoryBarrier buffer_barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.buffer = out_buffer,
|
|
||||||
.offset = out_offset,
|
|
||||||
.size = output_size,
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, {});
|
|
||||||
|
|
||||||
const VkBufferImageCopy copy{
|
|
||||||
.bufferOffset = out_offset,
|
|
||||||
.bufferRowLength = 0,
|
|
||||||
.bufferImageHeight = 0,
|
|
||||||
.imageSubresource{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.mipLevel = level,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = layers,
|
|
||||||
},
|
|
||||||
.imageOffset = {0, 0, 0},
|
|
||||||
.imageExtent = {texel_width, texel_height, 1},
|
|
||||||
};
|
|
||||||
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
copy);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||||
scheduler.Record([dst_image, aspect](vk::CommandBuffer cmdbuf) {
|
|
||||||
const VkImageMemoryBarrier barrier{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, {}, {}, barrier);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
|
||||||
constexpr u32 BL3DB_BINDING_INPUT_BUFFER = 0;
|
|
||||||
constexpr u32 BL3DB_BINDING_OUTPUT_BUFFER = 1;
|
|
||||||
|
|
||||||
struct alignas(16) BlockLinearUnswizzle3DBufferPushConstants {
|
|
||||||
std::array<u32, 3> dim;
|
|
||||||
u32 bytes_per_block_log2;
|
|
||||||
std::array<u32, 3> origin;
|
|
||||||
u32 slice_size;
|
|
||||||
u32 block_size;
|
|
||||||
u32 x_shift;
|
|
||||||
u32 block_height;
|
|
||||||
u32 block_height_mask;
|
|
||||||
u32 block_depth;
|
|
||||||
u32 block_depth_mask;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(BlockLinearUnswizzle3DBufferPushConstants) <= 128);
|
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorSetLayoutBinding, 2> BL3DB_BINDINGS{{
|
|
||||||
{
|
|
||||||
.binding = BL3DB_BINDING_INPUT_BUFFER,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
.pImmutableSamplers = nullptr,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.binding = BL3DB_BINDING_OUTPUT_BUFFER,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
|
||||||
.pImmutableSamplers = nullptr,
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, 2> BL3DB_TEMPLATE{{
|
|
||||||
{
|
|
||||||
.dstBinding = BL3DB_BINDING_INPUT_BUFFER,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.offset = BL3DB_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.dstBinding = BL3DB_BINDING_OUTPUT_BUFFER,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
|
||||||
.offset = BL3DB_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
|
||||||
.stride = sizeof(DescriptorUpdateEntry),
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr DescriptorBankInfo BL3DB_BANK_INFO{
|
|
||||||
.uniform_buffers = 0,
|
|
||||||
.storage_buffers = 2,
|
|
||||||
.texture_buffers = 0,
|
|
||||||
.image_buffers = 0,
|
|
||||||
.textures = 0,
|
|
||||||
.images = 0,
|
|
||||||
.score = 2,
|
|
||||||
};
|
|
||||||
} // Anonymous namespace
|
|
||||||
|
|
||||||
BlockLinearUnswizzle3DBufferPass::BlockLinearUnswizzle3DBufferPass(
|
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
: ComputePass(device_, scheduler_, descriptor_pool_, BL3DB_BINDINGS, BL3DB_TEMPLATE,
|
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||||
BL3DB_BANK_INFO,
|
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DBufferPushConstants)>,
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearSwizzle3DParams)>,
|
||||||
BLOCK_LINEAR_UNSWIZZLE_3D_BUFFER_COMP_SPV),
|
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV,
|
||||||
scheduler{scheduler_}, staging_buffer_pool{staging_buffer_pool_},
|
BLOCK_LINEAR_UNSWIZZLE_3D_NONARROW_COMP_SPV)),
|
||||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
BlockLinearUnswizzle3DBufferPass::~BlockLinearUnswizzle3DBufferPass() = default;
|
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
|
||||||
|
|
||||||
bool BlockLinearUnswizzle3DBufferPass::IsSupported(const Device& device,
|
void BlockLinearUnswizzleImage3DPass::Unswizzle(
|
||||||
const VideoCommon::ImageInfo& info) {
|
Image& image, const StagingBufferRef& map,
|
||||||
if (info.type != VideoCommon::ImageType::e3D) {
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
return false;
|
using namespace VideoCommon::Accelerated;
|
||||||
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
|
const VkImage vk_image = image.Handle();
|
||||||
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||||
|
|
||||||
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
|
||||||
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||||
|
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
|
||||||
|
|
||||||
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
|
|
||||||
|
const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
|
||||||
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
||||||
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (info.resources.levels != 1 || info.resources.layers != 1) {
|
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (info.num_samples > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (info.size.depth <= 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::GetFormatType(info.format) !=
|
|
||||||
VideoCore::Surface::SurfaceType::ColorTexture) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::IsPixelFormatASTC(info.format)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (VideoCore::Surface::IsPixelFormatBCn(info.format) && !device.IsOptimalBcnSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(info.format);
|
|
||||||
return bytes_per_block == 4 || bytes_per_block == 8 || bytes_per_block == 16;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockLinearUnswizzle3DBufferPass::Unswizzle(
|
PitchUnswizzlePass::PitchUnswizzlePass(
|
||||||
Image& image, const StagingBufferRef& swizzled,
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||||
if (swizzles.empty()) {
|
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||||
return;
|
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||||
}
|
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(PitchUnswizzlePushConstants)>,
|
||||||
|
UnswizzleSpv(device_, PITCH_UNSWIZZLE_COMP_SPV,
|
||||||
|
PITCH_UNSWIZZLE_NONARROW_COMP_SPV)),
|
||||||
|
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||||
|
|
||||||
const VideoCommon::SwizzleParameters& sw = swizzles.front();
|
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
|
||||||
const auto params = VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(sw, image.info);
|
|
||||||
|
|
||||||
const u32 blocks_x = sw.num_tiles.width;
|
|
||||||
const u32 blocks_y = sw.num_tiles.height;
|
|
||||||
const u32 blocks_z = sw.num_tiles.depth;
|
|
||||||
const u32 bytes_per_block = 1u << params.bytes_per_block_log2;
|
|
||||||
const VkDeviceSize output_size =
|
|
||||||
static_cast<VkDeviceSize>(blocks_x) * blocks_y * blocks_z * bytes_per_block;
|
|
||||||
|
|
||||||
const StagingBufferRef output =
|
|
||||||
staging_buffer_pool.Request(static_cast<size_t>(output_size), MemoryUsage::DeviceLocal);
|
|
||||||
|
|
||||||
BlockLinearUnswizzle3DBufferPushConstants pc{};
|
|
||||||
pc.dim = {blocks_x, blocks_y, blocks_z};
|
|
||||||
pc.bytes_per_block_log2 = params.bytes_per_block_log2;
|
|
||||||
pc.origin = params.origin;
|
|
||||||
pc.slice_size = params.slice_size;
|
|
||||||
pc.block_size = params.block_size;
|
|
||||||
pc.x_shift = params.x_shift;
|
|
||||||
pc.block_height = params.block_height;
|
|
||||||
pc.block_height_mask = params.block_height_mask;
|
|
||||||
pc.block_depth = params.block_depth;
|
|
||||||
pc.block_depth_mask = params.block_depth_mask;
|
|
||||||
|
|
||||||
|
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||||
scheduler.RequestOutsideRenderPassOperationContext();
|
scheduler.RequestOutsideRenderPassOperationContext();
|
||||||
|
const VkPipeline vk_pipeline = *pipeline;
|
||||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||||
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, sw.buffer_offset + swizzled.offset,
|
const VkImage vk_image = image.Handle();
|
||||||
image.guest_size_bytes - sw.buffer_offset);
|
|
||||||
compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size);
|
|
||||||
|
|
||||||
const void* descriptor_data = compute_pass_descriptor_queue.UpdateData();
|
|
||||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
|
||||||
|
|
||||||
const u32 gx = Common::DivCeil(blocks_x, 8u);
|
|
||||||
const u32 gy = Common::DivCeil(blocks_y, 8u);
|
|
||||||
const u32 gz = Common::DivCeil(blocks_z, 4u);
|
|
||||||
const bool is_initialized = image.ExchangeInitialization();
|
const bool is_initialized = image.ExchangeInitialization();
|
||||||
|
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||||
|
|
||||||
const VkBuffer out_buffer = output.buffer;
|
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
||||||
const VkDeviceSize out_offset = output.offset;
|
const u32 pitch = image.info.pitch;
|
||||||
const VkImage dst_image = image.Handle();
|
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||||
const VkImageAspectFlags aspect = image.AspectMask();
|
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||||
const VkExtent3D extent{
|
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||||
.width = image.info.size.width,
|
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||||
.height = image.info.size.height,
|
|
||||||
.depth = image.info.size.depth,
|
|
||||||
};
|
|
||||||
|
|
||||||
scheduler.Record([this, set, descriptor_data, pc, gx, gy, gz, output_size, out_buffer,
|
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||||
out_offset, dst_image, aspect, extent,
|
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
image.guest_size_bytes - swizzle.buffer_offset);
|
||||||
if (dst_image == VK_NULL_HANDLE || out_buffer == VK_NULL_HANDLE) {
|
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||||
return;
|
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||||
}
|
|
||||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
|
||||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
|
|
||||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
|
||||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(pc), &pc);
|
|
||||||
cmdbuf.Dispatch(gx, gy, gz);
|
|
||||||
|
|
||||||
const VkBufferMemoryBarrier buffer_barrier{
|
const PitchUnswizzlePushConstants params{
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
.origin{0, 0},
|
||||||
.pNext = nullptr,
|
.destination{0, 0},
|
||||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
.bytes_per_block = bytes_per_block,
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
.pitch = pitch,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.buffer = out_buffer,
|
|
||||||
.offset = out_offset,
|
|
||||||
.size = output_size,
|
|
||||||
};
|
};
|
||||||
VkAccessFlags pre_copy_access = VK_ACCESS_NONE;
|
scheduler.Record([this, num_dispatches_x, num_dispatches_y, params,
|
||||||
VkImageLayout pre_copy_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||||
VkPipelineStageFlags pre_copy_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||||
if (is_initialized) {
|
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||||
pre_copy_access = VK_ACCESS_SHADER_READ_BIT;
|
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||||
pre_copy_layout = VK_IMAGE_LAYOUT_GENERAL;
|
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||||
pre_copy_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE;
|
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1);
|
||||||
}
|
});
|
||||||
const VkImageMemoryBarrier pre_copy{
|
}
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = pre_copy_access,
|
|
||||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.oldLayout = pre_copy_layout,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | pre_copy_stage,
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, {}, buffer_barrier, pre_copy);
|
|
||||||
|
|
||||||
const VkBufferImageCopy copy{
|
|
||||||
.bufferOffset = out_offset,
|
|
||||||
.bufferRowLength = 0,
|
|
||||||
.bufferImageHeight = 0,
|
|
||||||
.imageSubresource{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.imageOffset = {0, 0, 0},
|
|
||||||
.imageExtent = extent,
|
|
||||||
};
|
|
||||||
cmdbuf.CopyBufferToImage(out_buffer, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy);
|
|
||||||
|
|
||||||
const VkImageMemoryBarrier post_copy{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
||||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
||||||
.image = dst_image,
|
|
||||||
.subresourceRange{
|
|
||||||
.aspectMask = aspect,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
|
||||||
0, {}, {}, post_copy);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -166,43 +166,46 @@ private:
|
|||||||
|
|
||||||
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
||||||
public:
|
public:
|
||||||
explicit BlockLinearUnswizzle2DPass(const Device& device_, Scheduler& scheduler_,
|
explicit BlockLinearUnswizzle2DPass(
|
||||||
DescriptorPool& descriptor_pool_,
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
|
||||||
~BlockLinearUnswizzle2DPass();
|
~BlockLinearUnswizzle2DPass();
|
||||||
|
|
||||||
[[nodiscard]] static bool IsSupported(const Device& device,
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
const VideoCommon::ImageInfo& info);
|
|
||||||
|
|
||||||
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
void UnswizzleFrom(Image& image, VkBuffer source_buffer, VkDeviceSize source_offset,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
StagingBufferPool& staging_buffer_pool;
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BlockLinearUnswizzle3DBufferPass final : public ComputePass {
|
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
|
||||||
public:
|
public:
|
||||||
explicit BlockLinearUnswizzle3DBufferPass(
|
explicit BlockLinearUnswizzleImage3DPass(
|
||||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||||
StagingBufferPool& staging_buffer_pool_,
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
~BlockLinearUnswizzle3DBufferPass();
|
~BlockLinearUnswizzleImage3DPass();
|
||||||
|
|
||||||
[[nodiscard]] static bool IsSupported(const Device& device, const VideoCommon::ImageInfo& info);
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
void Unswizzle(Image& image, const StagingBufferRef& swizzled,
|
|
||||||
|
private:
|
||||||
|
Scheduler& scheduler;
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PitchUnswizzlePass final : public ComputePass {
|
||||||
|
public:
|
||||||
|
explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
||||||
|
DescriptorPool& descriptor_pool_,
|
||||||
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||||
|
~PitchUnswizzlePass();
|
||||||
|
|
||||||
|
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Scheduler& scheduler;
|
Scheduler& scheduler;
|
||||||
StagingBufferPool& staging_buffer_pool;
|
|
||||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -694,9 +694,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
const size_t num_vertex_arrays = (std::min)(
|
const size_t num_vertex_arrays = (std::min)(
|
||||||
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
|
||||||
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
for (size_t index = 0; index < num_vertex_arrays; ++index) {
|
||||||
const bool instanced = key.state.binding_divisors[index] != 0;
|
const bool instanced = ((key.state.enabled_divisors >> index) & 1) != 0;
|
||||||
const auto rate =
|
auto rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
|
if (instanced) {
|
||||||
|
rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||||
|
}
|
||||||
vertex_bindings.push_back({
|
vertex_bindings.push_back({
|
||||||
.binding = static_cast<u32>(index),
|
.binding = static_cast<u32>(index),
|
||||||
.stride = key.state.vertex_strides[index],
|
.stride = key.state.vertex_strides[index],
|
||||||
@@ -705,7 +707,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
|
|||||||
if (instanced) {
|
if (instanced) {
|
||||||
vertex_binding_divisors.push_back({
|
vertex_binding_divisors.push_back({
|
||||||
.binding = static_cast<u32>(index),
|
.binding = static_cast<u32>(index),
|
||||||
.divisor = key.state.binding_divisors[index],
|
.divisor = device.GetVertexAttribDivisor(key.state.binding_divisors[index]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -447,7 +447,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
|||||||
.has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
|
.has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
|
||||||
.ignore_nan_fp_comparisons = false,
|
.ignore_nan_fp_comparisons = false,
|
||||||
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
|
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
|
||||||
.max_shared_memory_size = device.GetMaxComputeSharedMemorySize(),
|
|
||||||
.has_broken_robust =
|
.has_broken_robust =
|
||||||
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
|
||||||
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
|
||||||
@@ -923,6 +922,19 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
|
||||||
|
const VkDriverIdKHR driver_id = device.GetDriverID();
|
||||||
|
const bool needs_shared_mem_clamp =
|
||||||
|
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
|
||||||
|
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
|
||||||
|
const u32 max_shared_memory = device.GetMaxComputeSharedMemorySize();
|
||||||
|
if (needs_shared_mem_clamp && program.shared_memory_size > max_shared_memory) {
|
||||||
|
LOG_WARNING(Render_Vulkan,
|
||||||
|
"Compute shader {:#016x} requests {}KB shared memory but device max is {}KB - clamping",
|
||||||
|
key.unique_hash,
|
||||||
|
program.shared_memory_size / 1024,
|
||||||
|
max_shared_memory / 1024);
|
||||||
|
program.shared_memory_size = max_shared_memory;
|
||||||
|
}
|
||||||
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
const std::vector<u32> code{EmitSPIRV(profile, program)};
|
||||||
device.SaveShader(code);
|
device.SaveShader(code);
|
||||||
vk::ShaderModule spv_module{BuildShader(device, code)};
|
vk::ShaderModule spv_module{BuildShader(device, code)};
|
||||||
|
|||||||
@@ -225,13 +225,6 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
|
|||||||
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
|
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
|
||||||
wfi_event(device.GetLogical().CreateEvent()) {
|
wfi_event(device.GetLogical().CreateEvent()) {
|
||||||
scheduler.SetQueryCache(query_cache);
|
scheduler.SetQueryCache(query_cache);
|
||||||
if (Settings::values.use_unified_memory.GetValue() && device_memory.IsBackingShared()) {
|
|
||||||
buffer_cache_runtime.TryEnableUnifiedMemory(
|
|
||||||
device_memory.GetPhysicalBase(), device_memory.GetPhysicalSize(),
|
|
||||||
device_memory.GetBackingHardwareBuffers(),
|
|
||||||
device_memory.GetBackingHardwareBufferWindowSize(),
|
|
||||||
device_memory.GetBackingHardwareBufferBase());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RasterizerVulkan::~RasterizerVulkan() {
|
RasterizerVulkan::~RasterizerVulkan() {
|
||||||
@@ -267,6 +260,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||||
|
buffer_cache.SetDrawInstanceCount(instance_count);
|
||||||
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
|
||||||
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
const auto& draw_state = maxwell3d->draw_manager.draw_state;
|
||||||
const u32 num_instances{instance_count};
|
const u32 num_instances{instance_count};
|
||||||
@@ -302,6 +296,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
|||||||
void RasterizerVulkan::DrawIndirect() {
|
void RasterizerVulkan::DrawIndirect() {
|
||||||
const auto& params = maxwell3d->draw_manager.indirect_state;
|
const auto& params = maxwell3d->draw_manager.indirect_state;
|
||||||
buffer_cache.SetDrawIndirect(¶ms);
|
buffer_cache.SetDrawIndirect(¶ms);
|
||||||
|
buffer_cache.SetDrawInstanceCount(0);
|
||||||
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
PrepareDraw(params.is_indexed, [this, ¶ms] {
|
||||||
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
const auto indirect_buffer = buffer_cache.GetDrawIndirectBuffer();
|
||||||
const auto& buffer = indirect_buffer.first;
|
const auto& buffer = indirect_buffer.first;
|
||||||
@@ -1924,13 +1919,19 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
|
|||||||
for (u32 binding = 0; binding < max_bindings; ++binding) {
|
for (u32 binding = 0; binding < max_bindings; ++binding) {
|
||||||
const auto& input_binding{regs.vertex_streams[binding]};
|
const auto& input_binding{regs.vertex_streams[binding]};
|
||||||
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)};
|
||||||
|
auto input_rate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
|
u32 divisor = 1;
|
||||||
|
if (is_instanced) {
|
||||||
|
input_rate = VK_VERTEX_INPUT_RATE_INSTANCE;
|
||||||
|
divisor = device.GetVertexAttribDivisor(input_binding.frequency);
|
||||||
|
}
|
||||||
bindings.push_back({
|
bindings.push_back({
|
||||||
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.binding = binding,
|
.binding = binding,
|
||||||
.stride = input_binding.stride,
|
.stride = input_binding.stride,
|
||||||
.inputRate = is_instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX,
|
.inputRate = input_rate,
|
||||||
.divisor = is_instanced ? input_binding.frequency : 1,
|
.divisor = divisor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -449,7 +449,9 @@ void Scheduler::EndRenderPass()
|
|||||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
|
||||||
|
| VK_ACCESS_TRANSFER_READ_BIT
|
||||||
|
| VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
@@ -460,7 +462,7 @@ void Scheduler::EndRenderPass()
|
|||||||
}
|
}
|
||||||
cmdbuf.EndRenderPass();
|
cmdbuf.EndRenderPass();
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
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,
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||||
if (has_transform_feedback) {
|
if (has_transform_feedback) {
|
||||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||||
|
|||||||
@@ -129,10 +129,6 @@ public:
|
|||||||
return master_semaphore->IsFree(tick);
|
return master_semaphore->IsFree(tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefreshTick() {
|
|
||||||
master_semaphore->Refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Waits for the given GPU tick, optionally pacing frames.
|
/// Waits for the given GPU tick, optionally pacing frames.
|
||||||
void Wait(u64 tick, double target_fps = 0.0) {
|
void Wait(u64 tick, double target_fps = 0.0) {
|
||||||
if (tick > 0) {
|
if (tick > 0) {
|
||||||
|
|||||||
@@ -160,6 +160,55 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
info.size.depth == 1;
|
info.size.depth == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] PixelFormat UnswizzleViewFormat(u32 bytes_per_block) {
|
||||||
|
switch (bytes_per_block) {
|
||||||
|
case 1:
|
||||||
|
return PixelFormat::R8_UINT;
|
||||||
|
case 2:
|
||||||
|
return PixelFormat::R16_UINT;
|
||||||
|
case 4:
|
||||||
|
return PixelFormat::R32_UINT;
|
||||||
|
case 8:
|
||||||
|
return PixelFormat::R32G32_UINT;
|
||||||
|
case 16:
|
||||||
|
return PixelFormat::R32G32B32A32_UINT;
|
||||||
|
default:
|
||||||
|
return PixelFormat::Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
||||||
|
|
||||||
|
[[nodiscard]] bool SupportsAcceleratedUnswizzleDevice(const Device& device) {
|
||||||
|
return device.IsKhrImageFormatListSupported() &&
|
||||||
|
device.GetMaxComputeWorkGroupInvocations() >= UNSWIZZLE_WORKGROUP_INVOCATIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool SupportsAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
|
||||||
|
if (!SupportsAcceleratedUnswizzleDevice(device)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.num_samples > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info.type != ImageType::e2D && info.type != ImageType::e3D &&
|
||||||
|
info.type != ImageType::Linear) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const PixelFormat view_format =
|
||||||
|
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||||
|
if (view_format == PixelFormat::Invalid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!VideoCore::Surface::IsViewCompatible(info.format, view_format, false, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto host_format =
|
||||||
|
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, view_format);
|
||||||
|
return device.IsFormatSupported(host_format.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
|
||||||
|
FormatType::Optimal);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
||||||
std::optional<VkFormat> format_override = {}) {
|
std::optional<VkFormat> format_override = {}) {
|
||||||
auto format_info =
|
auto format_info =
|
||||||
@@ -248,8 +297,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
return allocator.CreateImage(image_ci);
|
return allocator.CreateImage(image_ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] VkImageViewType StorageViewType(ImageType type) {
|
||||||
|
if (type == ImageType::e3D) {
|
||||||
|
return VK_IMAGE_VIEW_TYPE_3D;
|
||||||
|
}
|
||||||
|
if (type == ImageType::Linear) {
|
||||||
|
return VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
}
|
||||||
|
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||||
VkFormat format) {
|
VkFormat format, VkImageViewType view_type) {
|
||||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
@@ -260,7 +319,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
|||||||
.pNext = &storage_image_view_usage_create_info,
|
.pNext = &storage_image_view_usage_create_info,
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.image = image,
|
.image = image,
|
||||||
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
.viewType = view_type,
|
||||||
.format = format,
|
.format = format,
|
||||||
.components{
|
.components{
|
||||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||||
@@ -658,18 +717,11 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
|||||||
.subresourceRange = subresource_range,
|
.subresourceRange = subresource_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
|
||||||
read_barrier);
|
read_barrier);
|
||||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||||
// TODO: Move this to another API
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||||
cmdbuf.PipelineBarrier(
|
nullptr, nullptr, write_barrier);
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
0, nullptr, nullptr, write_barrier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||||
@@ -968,10 +1020,14 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
|||||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||||
}
|
}
|
||||||
bl2d_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
if (SupportsAcceleratedUnswizzleDevice(device)) {
|
||||||
compute_pass_descriptor_queue);
|
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
bl3db_unswizzle_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
compute_pass_descriptor_queue);
|
||||||
compute_pass_descriptor_queue);
|
bl_unswizzle_image_3d_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
|
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||||
|
compute_pass_descriptor_queue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCacheRuntime::Finish() {
|
void TextureCacheRuntime::Finish() {
|
||||||
@@ -1898,12 +1954,6 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
}
|
}
|
||||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||||
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
|
||||||
} else if (runtime->bl2d_unswizzle_pass &&
|
|
||||||
BlockLinearUnswizzle2DPass::IsSupported(runtime->device, info)) {
|
|
||||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
|
||||||
} else if (runtime->bl3db_unswizzle_pass &&
|
|
||||||
BlockLinearUnswizzle3DBufferPass::IsSupported(runtime->device, info)) {
|
|
||||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
|
||||||
}
|
}
|
||||||
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
|
if (IsPixelFormatBCn(info.format) && !runtime->device.IsOptimalBcnSupported()) {
|
||||||
flags |= VideoCommon::ImageFlagBits::Converted;
|
flags |= VideoCommon::ImageFlagBits::Converted;
|
||||||
@@ -1912,16 +1962,12 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
|||||||
if (runtime->device.HasDebuggingToolAttached()) {
|
if (runtime->device.HasDebuggingToolAttached()) {
|
||||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||||
}
|
}
|
||||||
|
if (False(flags & VideoCommon::ImageFlagBits::Converted) &&
|
||||||
|
SupportsAcceleratedUnswizzle(runtime->device, info)) {
|
||||||
|
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||||
|
}
|
||||||
current_image = &Image::original_image;
|
current_image = &Image::original_image;
|
||||||
storage_image_views.resize(info.resources.levels);
|
storage_image_views.resize(info.resources.levels);
|
||||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
|
||||||
const auto& device = runtime->device.GetLogical();
|
|
||||||
const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
|
||||||
for (s32 level = 0; level < info.resources.levels; ++level) {
|
|
||||||
storage_image_views[level] =
|
|
||||||
MakeStorageView(device, level, *original_image, storage_format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
||||||
@@ -2045,7 +2091,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
|||||||
temp_vk_image, info.format, info.num_samples,
|
temp_vk_image, info.format, info.num_samples,
|
||||||
{image_copies.data(), image_copies.size()}, false);
|
{image_copies.data(), image_copies.size()}, false);
|
||||||
}
|
}
|
||||||
initialized = true;
|
InitializationFor(current_image) = true;
|
||||||
runtime->ReleaseMsaaScratchImage(temp_vk_image);
|
runtime->ReleaseMsaaScratchImage(temp_vk_image);
|
||||||
|
|
||||||
if (is_rescaled) {
|
if (is_rescaled) {
|
||||||
@@ -2066,7 +2112,7 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset,
|
|||||||
const VkBuffer src_buffer = buffer;
|
const VkBuffer src_buffer = buffer;
|
||||||
const VkImage vk_image = *original_image;
|
const VkImage vk_image = *original_image;
|
||||||
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
const VkImageAspectFlags vk_aspect_mask = aspect_mask;
|
||||||
const bool was_initialized = std::exchange(initialized, true);
|
const bool was_initialized = std::exchange(InitializationFor(&Image::original_image), true);
|
||||||
|
|
||||||
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
|
scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized,
|
||||||
vk_copies](vk::CommandBuffer cmdbuf) {
|
vk_copies](vk::CommandBuffer cmdbuf) {
|
||||||
@@ -2331,16 +2377,46 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
|||||||
DownloadMemory(buffers, offsets, copies);
|
DownloadMemory(buffers, offsets, copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<vk::ImageView>& Image::StorageViewsFor(vk::Image Image::*image) {
|
||||||
|
if (image == &Image::scaled_image) {
|
||||||
|
if (scaled_storage_image_views.empty()) {
|
||||||
|
scaled_storage_image_views.resize(info.resources.levels);
|
||||||
|
}
|
||||||
|
return scaled_storage_image_views;
|
||||||
|
}
|
||||||
|
return storage_image_views;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool& Image::InitializationFor(vk::Image Image::*image) noexcept {
|
||||||
|
if (image == &Image::scaled_image) {
|
||||||
|
return scaled_initialized;
|
||||||
|
}
|
||||||
|
return original_initialized;
|
||||||
|
}
|
||||||
|
|
||||||
VkImageView Image::StorageImageView(s32 level) noexcept {
|
VkImageView Image::StorageImageView(s32 level) noexcept {
|
||||||
auto& view = storage_image_views[level];
|
const bool astc_decode = WillUseAcceleratedAstcDecode(runtime->device, info);
|
||||||
|
const bool unswizzle_upload =
|
||||||
|
!astc_decode && True(flags & ImageFlagBits::AcceleratedUpload);
|
||||||
|
vk::Image Image::*target = current_image;
|
||||||
|
if (astc_decode || unswizzle_upload) {
|
||||||
|
target = &Image::original_image;
|
||||||
|
}
|
||||||
|
auto& view = StorageViewsFor(target)[level];
|
||||||
if (!view) {
|
if (!view) {
|
||||||
auto format_info =
|
auto format_info =
|
||||||
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
||||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
if (astc_decode) {
|
||||||
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||||
}
|
}
|
||||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
if (unswizzle_upload) {
|
||||||
format_info.format);
|
const PixelFormat view_format =
|
||||||
|
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||||
|
format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false,
|
||||||
|
view_format);
|
||||||
|
}
|
||||||
|
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*target),
|
||||||
|
format_info.format, StorageViewType(info.type));
|
||||||
}
|
}
|
||||||
return *view;
|
return *view;
|
||||||
}
|
}
|
||||||
@@ -2380,6 +2456,7 @@ bool Image::ScaleUp(bool ignore) {
|
|||||||
}
|
}
|
||||||
if (NeedsScaleHelper()) {
|
if (NeedsScaleHelper()) {
|
||||||
if (!BlitScaleHelper(true)) {
|
if (!BlitScaleHelper(true)) {
|
||||||
|
flags &= ~ImageFlagBits::Rescaled;
|
||||||
current_image = &Image::original_image;
|
current_image = &Image::original_image;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2569,6 +2646,10 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
|
if (device->IsExtAstcDecodeModeSupported() && IsLdrAstcFormat(format_info.format)) {
|
||||||
view_next = &astc_decode_mode;
|
view_next = &astc_decode_mode;
|
||||||
}
|
}
|
||||||
|
auto subresource_range = MakeSubresourceRange(aspect_mask, info.range);
|
||||||
|
if (True(flags & VideoCommon::ImageViewFlagBits::Slice)) {
|
||||||
|
subresource_range.levelCount = 1;
|
||||||
|
}
|
||||||
const VkImageViewCreateInfo create_info{
|
const VkImageViewCreateInfo create_info{
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
.pNext = view_next,
|
.pNext = view_next,
|
||||||
@@ -2577,7 +2658,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
|||||||
.viewType = VkImageViewType{},
|
.viewType = VkImageViewType{},
|
||||||
.format = format_info.format,
|
.format = format_info.format,
|
||||||
.components = swizzle_mapping,
|
.components = swizzle_mapping,
|
||||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
.subresourceRange = subresource_range,
|
||||||
};
|
};
|
||||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||||
VkImageViewCreateInfo ci{create_info};
|
VkImageViewCreateInfo ci{create_info};
|
||||||
@@ -3147,17 +3228,21 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
|||||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||||
u32 z_start, u32 z_count) {
|
u32 z_start, u32 z_count) {
|
||||||
|
|
||||||
if (astc_decoder_pass && WillUseAcceleratedAstcDecode(device, image.info)) {
|
if (IsPixelFormatASTC(image.info.format)) {
|
||||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl2d_unswizzle_pass && BlockLinearUnswizzle2DPass::IsSupported(device, image.info)) {
|
if (bl_unswizzle_2d_pass && image.info.type == ImageType::e2D) {
|
||||||
return bl2d_unswizzle_pass->Unswizzle(image, map, swizzles);
|
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bl3db_unswizzle_pass && z_count == 0 &&
|
if (bl_unswizzle_image_3d_pass && image.info.type == ImageType::e3D &&
|
||||||
BlockLinearUnswizzle3DBufferPass::IsSupported(device, image.info)) {
|
!IsPixelFormatBCn(image.info.format)) {
|
||||||
return bl3db_unswizzle_pass->Unswizzle(image, map, swizzles);
|
return bl_unswizzle_image_3d_pass->Unswizzle(image, map, swizzles);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pitch_unswizzle_pass && image.info.type == ImageType::Linear) {
|
||||||
|
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||||
@@ -3175,74 +3260,6 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
|||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureCacheRuntime::IsUnifiedMemoryBindable() const noexcept {
|
|
||||||
const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport();
|
|
||||||
return import != nullptr && import->IsValid() && import->IsBindable();
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TextureCacheRuntime::UnifiedMemoryBase() const noexcept {
|
|
||||||
const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport();
|
|
||||||
if (import == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return import->GetBaseOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TextureCacheRuntime::UnifiedMemorySize() const noexcept {
|
|
||||||
const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport();
|
|
||||||
if (import == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return import->GetSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TextureCacheRuntime::UnifiedMemoryWindowSize() const noexcept {
|
|
||||||
const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport();
|
|
||||||
if (import == nullptr) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return import->GetWindowSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureCacheRuntime::CanUploadImageDirectly(const VideoCommon::ImageInfo& info) const {
|
|
||||||
return bl2d_unswizzle_pass.has_value() &&
|
|
||||||
BlockLinearUnswizzle2DPass::IsSupported(device, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureCacheRuntime::UploadImageDirectly(
|
|
||||||
Image& image, size_t window_index, u64 window_offset,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
|
||||||
if ((window_offset % device.GetStorageBufferAlignment()) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (image.guest_size_bytes > device.GetMaxStorageBufferRange()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const HostMemoryImport* const import = memory_allocator.GetHostMemoryImport();
|
|
||||||
if (import == nullptr || window_index >= import->GetWindowCount()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const VkBuffer window_buffer = import->GetWindowBuffer(window_index);
|
|
||||||
if (window_buffer == VK_NULL_HANDLE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bl2d_unswizzle_pass->UnswizzleFrom(image, window_buffer,
|
|
||||||
static_cast<VkDeviceSize>(window_offset), swizzles);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TextureCacheRuntime::CurrentTick() const noexcept {
|
|
||||||
return scheduler.CurrentTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureCacheRuntime::IsDirectUploadRetired(u64 tick) {
|
|
||||||
if (scheduler.IsFree(tick)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
scheduler.RefreshTick();
|
|
||||||
return scheduler.IsFree(tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCacheRuntime::TransitionImageLayout(Image& image) {
|
void TextureCacheRuntime::TransitionImageLayout(Image& image) {
|
||||||
if (!image.ExchangeInitialization()) {
|
if (!image.ExchangeInitialization()) {
|
||||||
VkImageMemoryBarrier barrier{
|
VkImageMemoryBarrier barrier{
|
||||||
|
|||||||
@@ -101,23 +101,6 @@ public:
|
|||||||
std::span<const VideoCommon::SwizzleParameters>,
|
std::span<const VideoCommon::SwizzleParameters>,
|
||||||
u32 z_start, u32 z_count);
|
u32 z_start, u32 z_count);
|
||||||
|
|
||||||
[[nodiscard]] bool IsUnifiedMemoryBindable() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemoryBase() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemorySize() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] u64 UnifiedMemoryWindowSize() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] bool CanUploadImageDirectly(const VideoCommon::ImageInfo& info) const;
|
|
||||||
|
|
||||||
bool UploadImageDirectly(Image& image, size_t window_index, u64 window_offset,
|
|
||||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
|
||||||
|
|
||||||
[[nodiscard]] u64 CurrentTick() const noexcept;
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsDirectUploadRetired(u64 tick);
|
|
||||||
|
|
||||||
void InsertUploadMemoryBarrier() {}
|
void InsertUploadMemoryBarrier() {}
|
||||||
|
|
||||||
void TransitionImageLayout(Image& image);
|
void TransitionImageLayout(Image& image);
|
||||||
@@ -176,8 +159,9 @@ public:
|
|||||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||||
|
|
||||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||||
std::optional<BlockLinearUnswizzle2DPass> bl2d_unswizzle_pass;
|
std::optional<BlockLinearUnswizzle2DPass> bl_unswizzle_2d_pass;
|
||||||
std::optional<BlockLinearUnswizzle3DBufferPass> bl3db_unswizzle_pass;
|
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_image_3d_pass;
|
||||||
|
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
|
||||||
const Settings::ResolutionScalingInfo& resolution;
|
const Settings::ResolutionScalingInfo& resolution;
|
||||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||||
|
|
||||||
@@ -369,7 +353,7 @@ public:
|
|||||||
|
|
||||||
/// Returns true when the image is already initialized and mark it as initialized
|
/// Returns true when the image is already initialized and mark it as initialized
|
||||||
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
[[nodiscard]] bool ExchangeInitialization() noexcept {
|
||||||
return std::exchange(initialized, true);
|
return std::exchange(InitializationFor(current_image), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageView StorageImageView(s32 level) noexcept;
|
VkImageView StorageImageView(s32 level) noexcept;
|
||||||
@@ -389,6 +373,10 @@ private:
|
|||||||
|
|
||||||
bool NeedsScaleHelper() const;
|
bool NeedsScaleHelper() const;
|
||||||
|
|
||||||
|
std::vector<vk::ImageView>& StorageViewsFor(vk::Image Image::*image);
|
||||||
|
|
||||||
|
bool& InitializationFor(vk::Image Image::*image) noexcept;
|
||||||
|
|
||||||
Scheduler* scheduler{};
|
Scheduler* scheduler{};
|
||||||
TextureCacheRuntime* runtime{};
|
TextureCacheRuntime* runtime{};
|
||||||
|
|
||||||
@@ -406,8 +394,10 @@ private:
|
|||||||
vk::Image Image::*current_image{};
|
vk::Image Image::*current_image{};
|
||||||
|
|
||||||
std::vector<vk::ImageView> storage_image_views;
|
std::vector<vk::ImageView> storage_image_views;
|
||||||
|
std::vector<vk::ImageView> scaled_storage_image_views;
|
||||||
VkImageAspectFlags aspect_mask = 0;
|
VkImageAspectFlags aspect_mask = 0;
|
||||||
bool initialized = false;
|
bool original_initialized = false;
|
||||||
|
bool scaled_initialized = false;
|
||||||
|
|
||||||
std::optional<Framebuffer> scale_framebuffer;
|
std::optional<Framebuffer> scale_framebuffer;
|
||||||
std::optional<Framebuffer> normal_framebuffer;
|
std::optional<Framebuffer> normal_framebuffer;
|
||||||
@@ -605,7 +595,6 @@ struct TextureCacheParams {
|
|||||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||||
static constexpr bool HAS_MSAA_DOWNLOADS = true;
|
static constexpr bool HAS_MSAA_DOWNLOADS = true;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = true;
|
|
||||||
|
|
||||||
using Runtime = Vulkan::TextureCacheRuntime;
|
using Runtime = Vulkan::TextureCacheRuntime;
|
||||||
using Image = Vulkan::Image;
|
using Image = Vulkan::Image;
|
||||||
|
|||||||
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -23,8 +26,8 @@ struct BlockLinearSwizzle2DParams {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct BlockLinearSwizzle3DParams {
|
struct BlockLinearSwizzle3DParams {
|
||||||
std::array<u32, 3> origin;
|
alignas(16) std::array<u32, 3> origin;
|
||||||
std::array<s32, 3> destination;
|
alignas(16) std::array<s32, 3> destination;
|
||||||
u32 bytes_per_block_log2;
|
u32 bytes_per_block_log2;
|
||||||
u32 slice_size;
|
u32 slice_size;
|
||||||
u32 block_size;
|
u32 block_size;
|
||||||
|
|||||||
@@ -95,8 +95,6 @@ struct ImageBase {
|
|||||||
u32 scale_rating = 0;
|
u32 scale_rating = 0;
|
||||||
u64 scale_tick = 0;
|
u64 scale_tick = 0;
|
||||||
bool has_scaled = false;
|
bool has_scaled = false;
|
||||||
u64 direct_upload_tick = 0;
|
|
||||||
bool direct_upload_blocked = false;
|
|
||||||
|
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "video_core/engines/kepler_compute.h"
|
#include "video_core/engines/kepler_compute.h"
|
||||||
#include "video_core/guest_memory.h"
|
#include "video_core/guest_memory.h"
|
||||||
#include "video_core/host1x/gpu_device_memory_manager.h"
|
#include "video_core/host1x/gpu_device_memory_manager.h"
|
||||||
|
#include "video_core/texture_cache/accelerated_swizzle.h"
|
||||||
#include "video_core/texture_cache/image_view_base.h"
|
#include "video_core/texture_cache/image_view_base.h"
|
||||||
#include "video_core/texture_cache/samples_helper.h"
|
#include "video_core/texture_cache/samples_helper.h"
|
||||||
#include "video_core/texture_cache/texture_cache_base.h"
|
#include "video_core/texture_cache/texture_cache_base.h"
|
||||||
@@ -119,16 +120,13 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
bool aggressive_mode = false;
|
bool aggressive_mode = false;
|
||||||
u64 ticks_to_destroy = 0;
|
u64 ticks_to_destroy = 0;
|
||||||
size_t num_iterations = 0;
|
size_t num_iterations = 0;
|
||||||
size_t num_downloads = 0;
|
|
||||||
const auto Configure = [&](bool allow_aggressive) {
|
const auto Configure = [&](bool allow_aggressive) {
|
||||||
high_priority_mode = total_used_memory >= expected_memory;
|
high_priority_mode = total_used_memory >= expected_memory;
|
||||||
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
|
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
|
||||||
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
|
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
|
||||||
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
|
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
|
||||||
num_downloads = MAX_GC_DOWNLOADS_PER_PASS;
|
|
||||||
};
|
};
|
||||||
const auto Cleanup = [this, &num_iterations, &num_downloads, &high_priority_mode,
|
const auto Cleanup = [this, &num_iterations, &high_priority_mode, &aggressive_mode](ImageId image_id) {
|
||||||
&aggressive_mode](ImageId image_id) {
|
|
||||||
if (num_iterations == 0) {
|
if (num_iterations == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -142,10 +140,6 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (must_download) {
|
if (must_download) {
|
||||||
if (num_downloads == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
--num_downloads;
|
|
||||||
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
||||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||||
image.DownloadMemory(map, copies);
|
image.DownloadMemory(map, copies);
|
||||||
@@ -286,11 +280,11 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
|
|||||||
|
|
||||||
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
||||||
{
|
{
|
||||||
bool is_continue = false;
|
bool is_feedback = false;
|
||||||
for (size_t i = 0; i < 8; ++i)
|
for (size_t i = 0; i < 8; ++i)
|
||||||
is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
is_feedback |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||||
if (is_continue)
|
if (is_feedback)
|
||||||
continue;
|
return true;
|
||||||
}
|
}
|
||||||
if (depth_active && view_image_id == rt_depth_image_id) {
|
if (depth_active && view_image_id == rt_depth_image_id) {
|
||||||
return true;
|
return true;
|
||||||
@@ -592,15 +586,6 @@ FramebufferId TextureCache<P>::GetFramebufferId(const RenderTargets& key) {
|
|||||||
template <class P>
|
template <class P>
|
||||||
void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
|
void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
|
||||||
ForEachImageInRegion(cpu_addr, size, [this](ImageId image_id, Image& image) {
|
ForEachImageInRegion(cpu_addr, size, [this](ImageId image_id, Image& image) {
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (image.direct_upload_tick != 0) {
|
|
||||||
const u64 upload_tick = image.direct_upload_tick;
|
|
||||||
image.direct_upload_tick = 0;
|
|
||||||
if (!runtime.IsDirectUploadRetired(upload_tick)) {
|
|
||||||
image.direct_upload_blocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (True(image.flags & ImageFlagBits::CpuModified)) {
|
if (True(image.flags & ImageFlagBits::CpuModified)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -642,14 +627,25 @@ void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
|
|||||||
std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
|
std::ranges::sort(images, [this](ImageId lhs, ImageId rhs) {
|
||||||
return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
|
return slot_images[lhs].modification_tick < slot_images[rhs].modification_tick;
|
||||||
});
|
});
|
||||||
|
size_t total_size_bytes = 0;
|
||||||
|
for (const ImageId image_id : images) {
|
||||||
|
total_size_bytes += slot_images[image_id].unswizzled_size_bytes;
|
||||||
|
}
|
||||||
|
auto download_map = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||||
for (const ImageId image_id : images) {
|
for (const ImageId image_id : images) {
|
||||||
Image& image = slot_images[image_id];
|
Image& image = slot_images[image_id];
|
||||||
auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
|
|
||||||
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||||
image.DownloadMemory(map, copies);
|
image.DownloadMemory(download_map, copies);
|
||||||
runtime.Finish();
|
download_map.offset += image.unswizzled_size_bytes;
|
||||||
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span,
|
}
|
||||||
|
runtime.Finish();
|
||||||
|
std::span<u8> download_span = download_map.mapped_span;
|
||||||
|
for (const ImageId image_id : images) {
|
||||||
|
const ImageBase& image = slot_images[image_id];
|
||||||
|
const auto copies = FixSmallVectorADL(FullDownloadCopies(image.info));
|
||||||
|
SwizzleImage(*gpu_memory, image.gpu_addr, image.info, copies, download_span,
|
||||||
swizzle_data_buffer);
|
swizzle_data_buffer);
|
||||||
|
download_span = download_span.subspan(image.unswizzled_size_bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1138,6 +1134,12 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
|||||||
|
|
||||||
TrackImage(image, image_id);
|
TrackImage(image, image_id);
|
||||||
|
|
||||||
|
if (image.info.rescaleable &&
|
||||||
|
IsRegionGpuModified(image.cpu_addr, image.guest_size_bytes)) {
|
||||||
|
runtime.TransitionImageLayout(image);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
if (image.info.num_samples > 1 && !runtime.CanUploadMSAA()) {
|
||||||
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
|
||||||
runtime.TransitionImageLayout(image);
|
runtime.TransitionImageLayout(image);
|
||||||
@@ -1161,59 +1163,11 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
|
|||||||
QueueAsyncUnswizzle(image, image_id);
|
QueueAsyncUnswizzle(image, image_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (True(image.flags & ImageFlagBits::AcceleratedUpload) &&
|
|
||||||
TryUploadFromUnifiedMemory(image)) {
|
|
||||||
runtime.InsertUploadMemoryBarrier();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
|
auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
|
||||||
UploadImageContents(image, staging);
|
UploadImageContents(image, staging);
|
||||||
runtime.InsertUploadMemoryBarrier();
|
runtime.InsertUploadMemoryBarrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
|
||||||
bool TextureCache<P>::TryUploadFromUnifiedMemory([[maybe_unused]] Image& image) {
|
|
||||||
if constexpr (USE_UNIFIED_MEMORY) {
|
|
||||||
if (image.direct_upload_blocked || image.guest_size_bytes == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!runtime.IsUnifiedMemoryBindable() || !runtime.CanUploadImageDirectly(image.info)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 window_size = runtime.UnifiedMemoryWindowSize();
|
|
||||||
if (window_size == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u8* const first = gpu_memory->GetSpan(image.gpu_addr, image.guest_size_bytes);
|
|
||||||
if (first == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 phys_offset = static_cast<u64>(first - device_memory.GetPhysicalBase());
|
|
||||||
const u64 unified_base = runtime.UnifiedMemoryBase();
|
|
||||||
if (phys_offset < unified_base) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 relative = phys_offset - unified_base;
|
|
||||||
const u64 unified_size = runtime.UnifiedMemorySize();
|
|
||||||
if (relative >= unified_size || unified_size - relative < image.guest_size_bytes) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 local_offset = relative % window_size;
|
|
||||||
if (window_size - local_offset < image.guest_size_bytes) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto swizzles = FullUploadSwizzles(image.info);
|
|
||||||
if (!runtime.UploadImageDirectly(image, static_cast<size_t>(relative / window_size),
|
|
||||||
local_offset, FixSmallVectorADL(swizzles))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
image.direct_upload_tick = runtime.CurrentTick();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
template <typename StagingBuffer>
|
template <typename StagingBuffer>
|
||||||
void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) {
|
void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) {
|
||||||
@@ -1221,8 +1175,7 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
|
|||||||
const GPUVAddr gpu_addr = image.gpu_addr;
|
const GPUVAddr gpu_addr = image.gpu_addr;
|
||||||
|
|
||||||
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
|
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
|
||||||
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
|
gpu_memory->ReadBlockUnsafe(gpu_addr, mapped_span.data(), image.guest_size_bytes);
|
||||||
VideoCommon::CacheType::NoTextureCache);
|
|
||||||
const auto uploads = FullUploadSwizzles(image.info);
|
const auto uploads = FullUploadSwizzles(image.info);
|
||||||
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
|
runtime.AccelerateImageUpload(image, staging, FixSmallVectorADL(uploads), 0, 0);
|
||||||
return;
|
return;
|
||||||
@@ -1329,6 +1282,9 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
|
bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
|
||||||
|
if (!Settings::values.resolution_info.active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!image.info.rescaleable) {
|
if (!image.info.rescaleable) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1416,12 +1372,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
|
|||||||
decode->image_id = image_id;
|
decode->image_id = image_id;
|
||||||
async_decodes.push_back(std::move(decode));
|
async_decodes.push_back(std::move(decode));
|
||||||
|
|
||||||
std::vector<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes, 0);
|
Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes);
|
||||||
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
Tegra::Memory::GpuGuestMemory<u8, Tegra::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data(*gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer);
|
||||||
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer);
|
auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer);
|
||||||
const size_t out_size = MapSizeBytes(image);
|
const size_t out_size = MapSizeBytes(image);
|
||||||
|
|
||||||
auto func = [out_size, copies, info = image.info,
|
auto func = [out_size, copies = std::move(copies), info = image.info,
|
||||||
input = std::move(local_unswizzle_data_buffer),
|
input = std::move(local_unswizzle_data_buffer),
|
||||||
async_decode = decode_ptr]() mutable {
|
async_decode = decode_ptr]() mutable {
|
||||||
async_decode->decoded_data.resize_destructive(out_size);
|
async_decode->decoded_data.resize_destructive(out_size);
|
||||||
@@ -1490,18 +1446,16 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
|||||||
Image& image = slot_images[task.image_id];
|
Image& image = slot_images[task.image_id];
|
||||||
|
|
||||||
if (!task.initialized) {
|
if (!task.initialized) {
|
||||||
task.total_size = MapSizeBytes(image);
|
task.total_size = image.guest_size_bytes;
|
||||||
task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true);
|
task.staging_buffer = runtime.UploadStagingBuffer(task.total_size, true);
|
||||||
|
|
||||||
const auto& info = image.info;
|
const auto layout = FullUploadSwizzles(task.info);
|
||||||
const u32 bytes_per_block = BytesPerBlock(info.format);
|
const auto params =
|
||||||
const u32 width_blocks = Common::DivCeil(info.size.width, 4u);
|
VideoCommon::Accelerated::MakeBlockLinearSwizzle3DParams(layout.front(), task.info);
|
||||||
const u32 height_blocks = Common::DivCeil(info.size.height, 4u);
|
task.bytes_per_slice = params.slice_size;
|
||||||
|
task.chunked = task.info.block.depth == 0;
|
||||||
const u32 stride = width_blocks * bytes_per_block;
|
|
||||||
const u32 aligned_height = height_blocks;
|
|
||||||
task.bytes_per_slice = static_cast<size_t>(stride) * aligned_height;
|
|
||||||
task.last_submitted_offset = 0;
|
task.last_submitted_offset = 0;
|
||||||
|
task.slices_submitted = 0;
|
||||||
task.initialized = true;
|
task.initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1516,31 +1470,39 @@ void TextureCache<P>::TickAsyncUnswizzle() {
|
|||||||
if (copy_amount == 0) copy_amount = task.bytes_per_slice;
|
if (copy_amount == 0) copy_amount = task.bytes_per_slice;
|
||||||
}
|
}
|
||||||
|
|
||||||
gpu_memory->ReadBlock(image.gpu_addr + task.current_offset,
|
gpu_memory->ReadBlockUnsafe(image.gpu_addr + task.current_offset,
|
||||||
task.staging_buffer.mapped_span.data() + task.current_offset,
|
task.staging_buffer.mapped_span.data() + task.current_offset,
|
||||||
copy_amount);
|
copy_amount);
|
||||||
task.current_offset += copy_amount;
|
task.current_offset += copy_amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool is_final_batch = task.current_offset >= task.total_size;
|
const bool is_final_batch = task.current_offset >= task.total_size;
|
||||||
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
|
||||||
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
|
||||||
|
|
||||||
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
if (task.chunked) {
|
||||||
const u32 z_start = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
const size_t bytes_ready = task.current_offset - task.last_submitted_offset;
|
||||||
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
const u32 complete_slices = static_cast<u32>(bytes_ready / task.bytes_per_slice);
|
||||||
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
|
||||||
|
|
||||||
if (z_count > 0) {
|
if (complete_slices >= swizzle_slices_per_batch || (is_final_batch && complete_slices > 0)) {
|
||||||
const auto uploads = FullUploadSwizzles(task.info);
|
const u32 z_start = task.slices_submitted;
|
||||||
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), z_start, z_count);
|
const u32 slices_to_process = (std::min)(complete_slices, swizzle_slices_per_batch);
|
||||||
task.last_submitted_offset += (static_cast<size_t>(z_count) * task.bytes_per_slice);
|
const u32 z_count = (std::min)(slices_to_process, image.info.size.depth - z_start);
|
||||||
|
|
||||||
|
if (z_count > 0) {
|
||||||
|
const auto uploads = FullUploadSwizzles(task.info);
|
||||||
|
runtime.AccelerateImageUpload(image, task.staging_buffer,
|
||||||
|
FixSmallVectorADL(uploads), z_start, z_count);
|
||||||
|
task.last_submitted_offset += static_cast<size_t>(z_count) * task.bytes_per_slice;
|
||||||
|
task.slices_submitted += z_count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (is_final_batch && task.slices_submitted == 0) {
|
||||||
|
const auto uploads = FullUploadSwizzles(task.info);
|
||||||
|
runtime.AccelerateImageUpload(image, task.staging_buffer, FixSmallVectorADL(uploads), 0,
|
||||||
|
image.info.size.depth);
|
||||||
|
task.slices_submitted = image.info.size.depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if complete
|
const bool all_slices_submitted = task.slices_submitted >= image.info.size.depth;
|
||||||
const u32 slices_submitted = static_cast<u32>(task.last_submitted_offset / task.bytes_per_slice);
|
|
||||||
const bool all_slices_submitted = slices_submitted >= image.info.size.depth;
|
|
||||||
|
|
||||||
if (is_final_batch && all_slices_submitted) {
|
if (is_final_batch && all_slices_submitted) {
|
||||||
runtime.FreeDeferredStagingBuffer(task.staging_buffer);
|
runtime.FreeDeferredStagingBuffer(task.staging_buffer);
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
|||||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
|
static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
|
||||||
/// True when the API can do asynchronous texture downloads.
|
/// True when the API can do asynchronous texture downloads.
|
||||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
|
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = P::IMPLEMENTS_ASYNC_DOWNLOADS;
|
||||||
static constexpr bool USE_UNIFIED_MEMORY = P::USE_UNIFIED_MEMORY;
|
|
||||||
|
|
||||||
static constexpr size_t UNSET_CHANNEL{(std::numeric_limits<size_t>::max)()};
|
static constexpr size_t UNSET_CHANNEL{(std::numeric_limits<size_t>::max)()};
|
||||||
|
|
||||||
@@ -121,7 +120,6 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
|||||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
|
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
|
||||||
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
|
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
|
||||||
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
|
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
|
||||||
static constexpr size_t MAX_GC_DOWNLOADS_PER_PASS = 4;
|
|
||||||
|
|
||||||
using Runtime = typename P::Runtime;
|
using Runtime = typename P::Runtime;
|
||||||
using Image = typename P::Image;
|
using Image = typename P::Image;
|
||||||
@@ -140,6 +138,8 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
|||||||
AsyncBuffer staging_buffer;
|
AsyncBuffer staging_buffer;
|
||||||
size_t last_submitted_offset = 0;
|
size_t last_submitted_offset = 0;
|
||||||
size_t bytes_per_slice;
|
size_t bytes_per_slice;
|
||||||
|
u32 slices_submitted = 0;
|
||||||
|
bool chunked = false;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -309,8 +309,6 @@ private:
|
|||||||
|
|
||||||
void RefreshContents(Image& image, ImageId image_id);
|
void RefreshContents(Image& image, ImageId image_id);
|
||||||
|
|
||||||
bool TryUploadFromUnifiedMemory(Image& image);
|
|
||||||
|
|
||||||
/// Upload data from guest to an image
|
/// Upload data from guest to an image
|
||||||
template <typename StagingBuffer>
|
template <typename StagingBuffer>
|
||||||
void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
|
void UploadImageContents(Image& image, StagingBuffer& staging_buffer);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/host_memory.h"
|
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -945,7 +944,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
|
|
||||||
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
|
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
|
||||||
FOR_EACH_VK_EXTENSION(EXTENSION);
|
FOR_EACH_VK_EXTENSION(EXTENSION);
|
||||||
FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION);
|
|
||||||
|
|
||||||
extensions.depth_stencil_resolve =
|
extensions.depth_stencil_resolve =
|
||||||
extensions.depth_stencil_resolve &&
|
extensions.depth_stencil_resolve &&
|
||||||
@@ -964,13 +962,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
extensions.robustness_2 = false;
|
extensions.robustness_2 = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (extensions.external_memory_ahb && !extensions.queue_family_foreign) {
|
|
||||||
loaded_extensions.erase(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
|
|
||||||
extensions.external_memory_ahb = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef FEATURE_EXTENSION
|
#undef FEATURE_EXTENSION
|
||||||
#undef EXTENSION
|
#undef EXTENSION
|
||||||
|
|
||||||
@@ -1127,6 +1118,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
|
||||||
SetNext(next, properties.transform_feedback);
|
SetNext(next, properties.transform_feedback);
|
||||||
}
|
}
|
||||||
|
if (extensions.vertex_attribute_divisor) {
|
||||||
|
properties.vertex_attribute_divisor.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
|
||||||
|
SetNext(next, properties.vertex_attribute_divisor);
|
||||||
|
}
|
||||||
if (extensions.maintenance5) {
|
if (extensions.maintenance5) {
|
||||||
properties.maintenance5.sType =
|
properties.maintenance5.sType =
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
||||||
@@ -1137,21 +1133,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
|||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
|
||||||
SetNext(next, properties.custom_border_color);
|
SetNext(next, properties.custom_border_color);
|
||||||
}
|
}
|
||||||
if (extensions.external_memory_host) {
|
|
||||||
properties.external_memory_host.sType =
|
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT;
|
|
||||||
SetNext(next, properties.external_memory_host);
|
|
||||||
}
|
|
||||||
if (extensions.maintenance3 || instance_version >= VK_API_VERSION_1_1) {
|
|
||||||
properties.maintenance3.sType =
|
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES;
|
|
||||||
SetNext(next, properties.maintenance3);
|
|
||||||
}
|
|
||||||
if (extensions.maintenance4 || features.maintenance4.maintenance4) {
|
|
||||||
properties.maintenance4.sType =
|
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_PROPERTIES;
|
|
||||||
SetNext(next, properties.maintenance4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the property fetch.
|
// Perform the property fetch.
|
||||||
physical.GetProperties2(properties2);
|
physical.GetProperties2(properties2);
|
||||||
@@ -1539,27 +1520,12 @@ void Device::CollectPhysicalMemoryInfo() {
|
|||||||
device_access_memory = 0;
|
device_access_memory = 0;
|
||||||
u64 device_initial_usage = 0;
|
u64 device_initial_usage = 0;
|
||||||
u64 local_memory = 0;
|
u64 local_memory = 0;
|
||||||
const auto heap_has_usable_type = [&mem_properties](size_t heap) {
|
|
||||||
for (u32 index = 0; index < mem_properties.memoryTypeCount; ++index) {
|
|
||||||
if (mem_properties.memoryTypes[index].heapIndex != heap) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ((mem_properties.memoryTypes[index].propertyFlags &
|
|
||||||
VK_MEMORY_PROPERTY_PROTECTED_BIT) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
for (size_t element = 0; element < num_properties; ++element) {
|
for (size_t element = 0; element < num_properties; ++element) {
|
||||||
const bool is_heap_local =
|
const bool is_heap_local =
|
||||||
(mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
|
(mem_properties.memoryHeaps[element].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
|
||||||
if (!is_integrated && !is_heap_local) {
|
if (!is_integrated && !is_heap_local) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!heap_has_usable_type(element)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
valid_heap_memory.push_back(element);
|
valid_heap_memory.push_back(element);
|
||||||
if (is_heap_local) {
|
if (is_heap_local) {
|
||||||
local_memory += mem_properties.memoryHeaps[element].size;
|
local_memory += mem_properties.memoryHeaps[element].size;
|
||||||
@@ -1571,12 +1537,6 @@ void Device::CollectPhysicalMemoryInfo() {
|
|||||||
}
|
}
|
||||||
device_access_memory += mem_properties.memoryHeaps[element].size;
|
device_access_memory += mem_properties.memoryHeaps[element].size;
|
||||||
}
|
}
|
||||||
const u64 committed_backing = Common::GetCommittedBackingSize();
|
|
||||||
if (committed_backing != 0) {
|
|
||||||
LOG_INFO(Render_Vulkan, "Discounting {} MiB of guest memory committed by the host",
|
|
||||||
committed_backing >> 20);
|
|
||||||
local_memory -= (std::min)(local_memory, committed_backing);
|
|
||||||
}
|
|
||||||
if (is_integrated) {
|
if (is_integrated) {
|
||||||
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
|
const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
|
||||||
const u64 memory_size = Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive ? 6_GiB : 4_GiB;
|
const u64 memory_size = Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive ? 6_GiB : 4_GiB;
|
||||||
@@ -1591,7 +1551,6 @@ void Device::CollectPhysicalMemoryInfo() {
|
|||||||
device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory);
|
device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device_access_memory -= (std::min)(device_access_memory, committed_backing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Device::CollectToolingInfo() {
|
void Device::CollectToolingInfo() {
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
|
FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \
|
||||||
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
|
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
|
||||||
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
|
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
|
||||||
|
FEATURE(EXT, VertexAttributeDivisor, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||||
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
|
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
|
||||||
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
|
FEATURE(KHR, Maintenance5, MAINTENANCE_5, maintenance5) \
|
||||||
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
||||||
@@ -85,14 +86,12 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \
|
EXTENSION(EXT, CONDITIONAL_RENDERING, conditional_rendering) \
|
||||||
EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
|
EXTENSION(EXT, CONSERVATIVE_RASTERIZATION, conservative_rasterization) \
|
||||||
EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
|
EXTENSION(EXT, DEPTH_RANGE_UNRESTRICTED, depth_range_unrestricted) \
|
||||||
EXTENSION(EXT, EXTERNAL_MEMORY_HOST, external_memory_host) \
|
|
||||||
EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
|
EXTENSION(EXT, MEMORY_BUDGET, memory_budget) \
|
||||||
EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \
|
EXTENSION(EXT, ROBUSTNESS_2, robustness_2) \
|
||||||
EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \
|
EXTENSION(EXT, SAMPLER_FILTER_MINMAX, sampler_filter_minmax) \
|
||||||
EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
|
EXTENSION(EXT, SHADER_STENCIL_EXPORT, shader_stencil_export) \
|
||||||
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
||||||
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
||||||
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
|
||||||
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
||||||
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
||||||
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
||||||
@@ -117,14 +116,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
|||||||
EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \
|
EXTENSION(IMG, FILTER_CUBIC, filter_cubic_img) \
|
||||||
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
|
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION) \
|
|
||||||
EXTENSION(EXT, QUEUE_FAMILY_FOREIGN, queue_family_foreign) \
|
|
||||||
EXTENSION(ANDROID, EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER, external_memory_ahb)
|
|
||||||
#else
|
|
||||||
#define FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Define extensions which must be supported.
|
// Define extensions which must be supported.
|
||||||
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
|
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
|
||||||
EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
|
EXTENSION_NAME(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) \
|
||||||
@@ -363,6 +354,7 @@ public:
|
|||||||
|
|
||||||
#define FN_MAX_LIMIT_LIST \
|
#define FN_MAX_LIMIT_LIST \
|
||||||
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
||||||
|
FN_MAX_LIMIT_ELEM(ComputeWorkGroupInvocations) \
|
||||||
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
||||||
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
||||||
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
||||||
@@ -688,6 +680,32 @@ FN_MAX_LIMIT_LIST
|
|||||||
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
return features.host_query_reset.hostQueryReset != VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetMaxVertexAttribDivisor() const {
|
||||||
|
const u32 reported = properties.vertex_attribute_divisor.maxVertexAttribDivisor;
|
||||||
|
if (reported == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return reported;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsVertexAttributeInstanceRateZeroDivisorSupported() const {
|
||||||
|
return features.vertex_attribute_divisor.vertexAttributeInstanceRateZeroDivisor == VK_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetVertexAttribDivisor(u32 frequency) const {
|
||||||
|
const u32 max_divisor = GetMaxVertexAttribDivisor();
|
||||||
|
if (frequency == 0) {
|
||||||
|
if (IsVertexAttributeInstanceRateZeroDivisorSupported()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return max_divisor;
|
||||||
|
}
|
||||||
|
if (frequency > max_divisor) {
|
||||||
|
return max_divisor;
|
||||||
|
}
|
||||||
|
return frequency;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the device supports VK_EXT_transform_feedback.
|
/// Returns true if the device supports VK_EXT_transform_feedback.
|
||||||
bool IsExtTransformFeedbackSupported() const {
|
bool IsExtTransformFeedbackSupported() const {
|
||||||
return extensions.transform_feedback;
|
return extensions.transform_feedback;
|
||||||
@@ -886,30 +904,6 @@ FN_MAX_LIMIT_LIST
|
|||||||
return extensions.conditional_rendering;
|
return extensions.conditional_rendering;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsExtExternalMemoryHostSupported() const {
|
|
||||||
return extensions.external_memory_host;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsExtExternalMemoryAhbSupported() const {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
return extensions.external_memory_ahb && extensions.queue_family_foreign;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetMinImportedHostPointerAlignment() const {
|
|
||||||
return properties.external_memory_host.minImportedHostPointerAlignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetMaxBufferSize() const {
|
|
||||||
return properties.maintenance4.maxBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetMaxMemoryAllocationSize() const {
|
|
||||||
return properties.maintenance3.maxMemoryAllocationSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsExtAstcDecodeModeSupported() const {
|
bool IsExtAstcDecodeModeSupported() const {
|
||||||
return extensions.astc_decode_mode;
|
return extensions.astc_decode_mode;
|
||||||
}
|
}
|
||||||
@@ -1176,7 +1170,6 @@ private:
|
|||||||
FOR_EACH_VK_FEATURE_1_4(FEATURE);
|
FOR_EACH_VK_FEATURE_1_4(FEATURE);
|
||||||
FOR_EACH_VK_FEATURE_EXT(FEATURE);
|
FOR_EACH_VK_FEATURE_EXT(FEATURE);
|
||||||
FOR_EACH_VK_EXTENSION(EXTENSION);
|
FOR_EACH_VK_EXTENSION(EXTENSION);
|
||||||
FOR_EACH_VK_PLATFORM_EXTENSION(EXTENSION);
|
|
||||||
|
|
||||||
#undef EXTENSION
|
#undef EXTENSION
|
||||||
#undef FEATURE
|
#undef FEATURE
|
||||||
@@ -1208,12 +1201,10 @@ private:
|
|||||||
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
|
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer{};
|
||||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||||
VkPhysicalDeviceMaintenance3Properties maintenance3{};
|
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT vertex_attribute_divisor{};
|
||||||
VkPhysicalDeviceMaintenance4Properties maintenance4{};
|
|
||||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||||
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
||||||
VkPhysicalDeviceExternalMemoryHostPropertiesEXT external_memory_host{};
|
|
||||||
|
|
||||||
VkPhysicalDeviceProperties properties{};
|
VkPhysicalDeviceProperties properties{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,34 +25,9 @@
|
|||||||
#include "video_core/gpu_logging/gpu_logging.h"
|
#include "video_core/gpu_logging/gpu_logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
#include <android/hardware_buffer.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] std::optional<u32> FindImportMemoryType(
|
|
||||||
const VkPhysicalDeviceMemoryProperties &props, u32 type_mask) {
|
|
||||||
const auto find = [&](VkMemoryPropertyFlags wanted) -> std::optional<u32> {
|
|
||||||
for (u32 i = 0; i < props.memoryTypeCount; ++i) {
|
|
||||||
if (((type_mask >> i) & 1u) != 0 &&
|
|
||||||
(props.memoryTypes[i].propertyFlags & wanted) == wanted) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
};
|
|
||||||
auto type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
|
|
||||||
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
|
|
||||||
if (!type_index) {
|
|
||||||
type_index = find(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
||||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
||||||
}
|
|
||||||
return type_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers translating MemoryUsage to flags/usage
|
// Helpers translating MemoryUsage to flags/usage
|
||||||
|
|
||||||
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
|
[[maybe_unused]] VkMemoryPropertyFlags MemoryUsagePropertyFlags(MemoryUsage usage) {
|
||||||
@@ -225,316 +200,6 @@ namespace Vulkan {
|
|||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemoryImport::HostMemoryImport(const Device &device_, void *base, size_t size,
|
|
||||||
std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window, size_t hardware_buffer_base)
|
|
||||||
: device{device_} {
|
|
||||||
if (ImportHardwareBuffers(hardware_buffers, hardware_buffer_window, hardware_buffer_base,
|
|
||||||
size)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (device.IsTiler()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!hardware_buffers.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ImportHostPointer(base, size)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOG_INFO(Render_Vulkan, "Unified memory disabled, no host memory import path");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HostMemoryImport::ImportHostPointer(void *base, size_t size) {
|
|
||||||
if (!device.IsExtExternalMemoryHostSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 alignment = device.GetMinImportedHostPointerAlignment();
|
|
||||||
if (alignment == 0 || !Common::IsAligned(reinterpret_cast<uintptr_t>(base), alignment) ||
|
|
||||||
!Common::IsAligned(size, alignment)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
using namespace Common::Literals;
|
|
||||||
constexpr VkDeviceSize DesktopWindowSize = 4_GiB;
|
|
||||||
VkDeviceSize candidate_window = DesktopWindowSize;
|
|
||||||
const u64 max_buffer_size = device.GetMaxBufferSize();
|
|
||||||
if (max_buffer_size != 0 && max_buffer_size < candidate_window) {
|
|
||||||
candidate_window = max_buffer_size;
|
|
||||||
}
|
|
||||||
const u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
|
|
||||||
if (max_allocation_size != 0 && max_allocation_size < candidate_window) {
|
|
||||||
candidate_window = max_allocation_size;
|
|
||||||
}
|
|
||||||
candidate_window = Common::AlignDown(candidate_window, alignment);
|
|
||||||
if (candidate_window == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
window_size = candidate_window;
|
|
||||||
|
|
||||||
const auto &logical = device.GetLogical();
|
|
||||||
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
|
|
||||||
|
|
||||||
for (size_t offset = 0; offset < size; offset += window_size) {
|
|
||||||
u8 *const window_base = static_cast<u8 *>(base) + offset;
|
|
||||||
const VkDeviceSize window_len =
|
|
||||||
(std::min)(static_cast<VkDeviceSize>(size - offset), window_size);
|
|
||||||
VkMemoryHostPointerPropertiesEXT host_props{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.memoryTypeBits = 0,
|
|
||||||
};
|
|
||||||
if (logical.GetMemoryHostPointerPropertiesEXT(
|
|
||||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, window_base,
|
|
||||||
&host_props) != VK_SUCCESS ||
|
|
||||||
host_props.memoryTypeBits == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkExternalMemoryBufferCreateInfo external_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
|
||||||
};
|
|
||||||
const VkBufferCreateInfo buffer_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = &external_info,
|
|
||||||
.flags = 0,
|
|
||||||
.size = window_len,
|
|
||||||
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
VkBuffer new_buffer{};
|
|
||||||
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkMemoryRequirements requirements =
|
|
||||||
logical.GetBufferMemoryRequirements(new_buffer);
|
|
||||||
const u32 type_mask = requirements.memoryTypeBits & host_props.memoryTypeBits;
|
|
||||||
if (type_mask == 0 || requirements.size > window_len) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto type_index = FindImportMemoryType(memory_props, type_mask);
|
|
||||||
if (!type_index) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const u32 heap_index = memory_props.memoryTypes[*type_index].heapIndex;
|
|
||||||
const VkDeviceSize heap_size = memory_props.memoryHeaps[heap_index].size;
|
|
||||||
if (imported_size + window_len > heap_size / 2) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkImportMemoryHostPointerInfoEXT import_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT,
|
|
||||||
.pHostPointer = window_base,
|
|
||||||
};
|
|
||||||
const VkMemoryAllocateInfo alloc_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
||||||
.pNext = &import_info,
|
|
||||||
.allocationSize = window_len,
|
|
||||||
.memoryTypeIndex = *type_index,
|
|
||||||
};
|
|
||||||
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
|
|
||||||
if (!memory) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
windows.push_back(Window{
|
|
||||||
.memory = std::move(memory),
|
|
||||||
.buffer = new_buffer,
|
|
||||||
});
|
|
||||||
imported_size += static_cast<size_t>(window_len);
|
|
||||||
}
|
|
||||||
if (windows.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HostMemoryImport::ImportHardwareBuffers(
|
|
||||||
[[maybe_unused]] std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
[[maybe_unused]] size_t hardware_buffer_window,
|
|
||||||
[[maybe_unused]] size_t hardware_buffer_base, [[maybe_unused]] size_t size) {
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
if (hardware_buffers.empty() || hardware_buffer_window == 0 ||
|
|
||||||
!device.IsExtExternalMemoryAhbSupported()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const u64 max_allocation_size = device.GetMaxMemoryAllocationSize();
|
|
||||||
if (max_allocation_size != 0 && hardware_buffer_window > max_allocation_size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (hardware_buffer_base >= size) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto &logical = device.GetLogical();
|
|
||||||
const auto memory_props = device.GetPhysical().GetMemoryProperties().memoryProperties;
|
|
||||||
window_size = hardware_buffer_window;
|
|
||||||
base_offset = hardware_buffer_base;
|
|
||||||
|
|
||||||
const auto import_all = [&](VkBufferUsageFlags usage, bool want_address) {
|
|
||||||
for (size_t i = 0; i < hardware_buffers.size(); ++i) {
|
|
||||||
const size_t offset = hardware_buffer_base + i * hardware_buffer_window;
|
|
||||||
if (offset >= size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkDeviceSize window_len = (std::min)(
|
|
||||||
static_cast<VkDeviceSize>(size - offset),
|
|
||||||
static_cast<VkDeviceSize>(hardware_buffer_window));
|
|
||||||
VkAndroidHardwareBufferPropertiesANDROID ahb_props{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.allocationSize = 0,
|
|
||||||
.memoryTypeBits = 0,
|
|
||||||
};
|
|
||||||
if (logical.GetAndroidHardwareBufferPropertiesANDROID(hardware_buffers[i],
|
|
||||||
&ahb_props) != VK_SUCCESS ||
|
|
||||||
ahb_props.memoryTypeBits == 0 || ahb_props.allocationSize < window_len) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkExternalMemoryBufferCreateInfo external_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.handleTypes =
|
|
||||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
|
|
||||||
};
|
|
||||||
const VkBufferCreateInfo buffer_ci{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.pNext = &external_info,
|
|
||||||
.flags = 0,
|
|
||||||
.size = window_len,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
.queueFamilyIndexCount = 0,
|
|
||||||
.pQueueFamilyIndices = nullptr,
|
|
||||||
};
|
|
||||||
VkBuffer new_buffer{};
|
|
||||||
if (logical.CreateBufferRaw(buffer_ci, &new_buffer) != VK_SUCCESS) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkMemoryRequirements requirements =
|
|
||||||
logical.GetBufferMemoryRequirements(new_buffer);
|
|
||||||
const u32 type_mask = requirements.memoryTypeBits & ahb_props.memoryTypeBits;
|
|
||||||
if (type_mask == 0 || requirements.size > ahb_props.allocationSize) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const auto type_index = FindImportMemoryType(memory_props, type_mask);
|
|
||||||
if (!type_index) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const VkImportAndroidHardwareBufferInfoANDROID import_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.buffer = hardware_buffers[i],
|
|
||||||
};
|
|
||||||
const VkMemoryDedicatedAllocateInfo dedicated_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
|
|
||||||
.pNext = &import_info,
|
|
||||||
.image = VK_NULL_HANDLE,
|
|
||||||
.buffer = new_buffer,
|
|
||||||
};
|
|
||||||
const VkMemoryAllocateFlagsInfo flags_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
|
|
||||||
.pNext = &dedicated_info,
|
|
||||||
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
|
|
||||||
.deviceMask = 0,
|
|
||||||
};
|
|
||||||
const void *alloc_next = &dedicated_info;
|
|
||||||
if (want_address) {
|
|
||||||
alloc_next = &flags_info;
|
|
||||||
}
|
|
||||||
const VkMemoryAllocateInfo alloc_info{
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
||||||
.pNext = alloc_next,
|
|
||||||
.allocationSize = ahb_props.allocationSize,
|
|
||||||
.memoryTypeIndex = *type_index,
|
|
||||||
};
|
|
||||||
vk::DeviceMemory memory = logical.TryAllocateMemory(alloc_info);
|
|
||||||
if (!memory) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (logical.BindBufferMemory(new_buffer, *memory, 0) != VK_SUCCESS) {
|
|
||||||
logical.DestroyBufferRaw(new_buffer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
VkDeviceAddress address = 0;
|
|
||||||
if (want_address) {
|
|
||||||
address = logical.GetBufferDeviceAddress(new_buffer);
|
|
||||||
}
|
|
||||||
windows.push_back(Window{
|
|
||||||
.memory = std::move(memory),
|
|
||||||
.buffer = new_buffer,
|
|
||||||
.address = address,
|
|
||||||
});
|
|
||||||
imported_size += static_cast<size_t>(window_len);
|
|
||||||
}
|
|
||||||
return !windows.empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr VkBufferUsageFlags TransferUsage =
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
||||||
VkBufferUsageFlags shader_usage = TransferUsage |
|
|
||||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
|
|
||||||
VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
|
|
||||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
|
||||||
const bool want_address = device.IsBufferDeviceAddressSupported();
|
|
||||||
VkBufferUsageFlags minimal_usage = TransferUsage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
||||||
if (want_address) {
|
|
||||||
shader_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
||||||
minimal_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto reset_windows = [&] {
|
|
||||||
for (Window &window : windows) {
|
|
||||||
if (window.buffer != VK_NULL_HANDLE) {
|
|
||||||
logical.DestroyBufferRaw(window.buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
windows.clear();
|
|
||||||
imported_size = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
bindable = import_all(shader_usage, want_address);
|
|
||||||
if (!bindable) {
|
|
||||||
reset_windows();
|
|
||||||
bindable = import_all(minimal_usage, want_address);
|
|
||||||
}
|
|
||||||
if (!bindable) {
|
|
||||||
reset_windows();
|
|
||||||
import_all(TransferUsage, false);
|
|
||||||
}
|
|
||||||
if (windows.empty()) {
|
|
||||||
window_size = 0;
|
|
||||||
base_offset = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foreign_ownership = true;
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
HostMemoryImport::~HostMemoryImport() {
|
|
||||||
for (Window &window : windows) {
|
|
||||||
if (window.buffer != VK_NULL_HANDLE) {
|
|
||||||
device.GetLogical().DestroyBufferRaw(window.buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryAllocator::MemoryAllocator(const Device &device_)
|
MemoryAllocator::MemoryAllocator(const Device &device_)
|
||||||
: device{device_}, allocator{device.GetAllocator()},
|
: device{device_}, allocator{device.GetAllocator()},
|
||||||
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
|
properties{device_.GetPhysical().GetMemoryProperties().memoryProperties},
|
||||||
@@ -667,17 +332,6 @@ namespace Vulkan {
|
|||||||
return MemoryCommit(allocator, a, info);
|
return MemoryCommit(allocator, a, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemoryImport *MemoryAllocator::CreateHostMemoryImport(
|
|
||||||
void *base, size_t size, std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window, size_t hardware_buffer_base) {
|
|
||||||
unified_memory = std::make_unique<HostMemoryImport>(
|
|
||||||
device, base, size, hardware_buffers, hardware_buffer_window, hardware_buffer_base);
|
|
||||||
if (!unified_memory->IsValid()) {
|
|
||||||
unified_memory.reset();
|
|
||||||
}
|
|
||||||
return unified_memory.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) {
|
MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) {
|
||||||
// Allocate memory appropriate for this buffer automatically
|
// Allocate memory appropriate for this buffer automatically
|
||||||
const auto vma_usage = MemoryUsageVma(usage);
|
const auto vma_usage = MemoryUsageVma(usage);
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
#include "video_core/vulkan_common/vma.h"
|
#include "video_core/vulkan_common/vma.h"
|
||||||
|
|
||||||
struct AHardwareBuffer;
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
@@ -86,76 +84,6 @@ namespace Vulkan {
|
|||||||
void *mapped_ptr{}; ///< Optional persistent mapped pointer
|
void *mapped_ptr{}; ///< Optional persistent mapped pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
class HostMemoryImport {
|
|
||||||
public:
|
|
||||||
explicit HostMemoryImport(const Device &device_, void *base, size_t size,
|
|
||||||
std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window, size_t hardware_buffer_base);
|
|
||||||
|
|
||||||
~HostMemoryImport();
|
|
||||||
|
|
||||||
HostMemoryImport(const HostMemoryImport &) = delete;
|
|
||||||
|
|
||||||
HostMemoryImport &operator=(const HostMemoryImport &) = delete;
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsValid() const noexcept {
|
|
||||||
return !windows.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t GetSize() const noexcept {
|
|
||||||
return imported_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t GetBaseOffset() const noexcept {
|
|
||||||
return base_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool NeedsForeignOwnershipTransfer() const noexcept {
|
|
||||||
return foreign_ownership;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceSize GetWindowSize() const noexcept {
|
|
||||||
return window_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkBuffer GetWindowBuffer(size_t index) const noexcept {
|
|
||||||
return windows[index].buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] VkDeviceAddress GetWindowAddress(size_t index) const noexcept {
|
|
||||||
return windows[index].address;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t GetWindowCount() const noexcept {
|
|
||||||
return windows.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsBindable() const noexcept {
|
|
||||||
return bindable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct Window {
|
|
||||||
vk::DeviceMemory memory;
|
|
||||||
VkBuffer buffer{};
|
|
||||||
VkDeviceAddress address{};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ImportHostPointer(void *base, size_t size);
|
|
||||||
|
|
||||||
bool ImportHardwareBuffers(std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window, size_t hardware_buffer_base,
|
|
||||||
size_t size);
|
|
||||||
|
|
||||||
const Device &device;
|
|
||||||
std::vector<Window> windows;
|
|
||||||
VkDeviceSize window_size{};
|
|
||||||
size_t imported_size{};
|
|
||||||
size_t base_offset{};
|
|
||||||
bool foreign_ownership{};
|
|
||||||
bool bindable{};
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Memory allocator container.
|
/// Memory allocator container.
|
||||||
/// Allocates and releases memory allocations on demand.
|
/// Allocates and releases memory allocations on demand.
|
||||||
class MemoryAllocator {
|
class MemoryAllocator {
|
||||||
@@ -192,15 +120,6 @@ namespace Vulkan {
|
|||||||
/// Commits memory required by the buffer and binds it (for buffers created outside VMA).
|
/// Commits memory required by the buffer and binds it (for buffers created outside VMA).
|
||||||
MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage);
|
MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage);
|
||||||
|
|
||||||
HostMemoryImport *CreateHostMemoryImport(void *base, size_t size,
|
|
||||||
std::span<AHardwareBuffer *const> hardware_buffers,
|
|
||||||
size_t hardware_buffer_window,
|
|
||||||
size_t hardware_buffer_base);
|
|
||||||
|
|
||||||
[[nodiscard]] HostMemoryImport *GetHostMemoryImport() const noexcept {
|
|
||||||
return unified_memory.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool IsAutoUsage(VmaMemoryUsage u) noexcept {
|
static bool IsAutoUsage(VmaMemoryUsage u) noexcept {
|
||||||
switch (u) {
|
switch (u) {
|
||||||
@@ -218,7 +137,6 @@ namespace Vulkan {
|
|||||||
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties.
|
const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties.
|
||||||
VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity
|
VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity
|
||||||
u32 valid_memory_types{~0u};
|
u32 valid_memory_types{~0u};
|
||||||
std::unique_ptr<HostMemoryImport> unified_memory;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -217,16 +217,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
|||||||
X(vkGetBufferMemoryRequirements2);
|
X(vkGetBufferMemoryRequirements2);
|
||||||
X(vkGetDeviceQueue);
|
X(vkGetDeviceQueue);
|
||||||
X(vkGetEventStatus);
|
X(vkGetEventStatus);
|
||||||
X(vkGetMemoryHostPointerPropertiesEXT);
|
|
||||||
X(vkGetFenceStatus);
|
X(vkGetFenceStatus);
|
||||||
X(vkGetImageMemoryRequirements);
|
X(vkGetImageMemoryRequirements);
|
||||||
X(vkGetPipelineCacheData);
|
X(vkGetPipelineCacheData);
|
||||||
X(vkGetMemoryFdKHR);
|
X(vkGetMemoryFdKHR);
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
X(vkGetMemoryWin32HandleKHR);
|
X(vkGetMemoryWin32HandleKHR);
|
||||||
#endif
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
X(vkGetAndroidHardwareBufferPropertiesANDROID);
|
|
||||||
#endif
|
#endif
|
||||||
X(vkGetQueryPoolResults);
|
X(vkGetQueryPoolResults);
|
||||||
X(vkGetPipelineExecutablePropertiesKHR);
|
X(vkGetPipelineExecutablePropertiesKHR);
|
||||||
|
|||||||
@@ -333,16 +333,12 @@ struct DeviceDispatch : InstanceDispatch {
|
|||||||
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{};
|
PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2{};
|
||||||
PFN_vkGetDeviceQueue vkGetDeviceQueue{};
|
PFN_vkGetDeviceQueue vkGetDeviceQueue{};
|
||||||
PFN_vkGetEventStatus vkGetEventStatus{};
|
PFN_vkGetEventStatus vkGetEventStatus{};
|
||||||
PFN_vkGetMemoryHostPointerPropertiesEXT vkGetMemoryHostPointerPropertiesEXT{};
|
|
||||||
PFN_vkGetFenceStatus vkGetFenceStatus{};
|
PFN_vkGetFenceStatus vkGetFenceStatus{};
|
||||||
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
|
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements{};
|
||||||
PFN_vkGetPipelineCacheData vkGetPipelineCacheData{};
|
PFN_vkGetPipelineCacheData vkGetPipelineCacheData{};
|
||||||
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
|
PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR{};
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
PFN_vkGetMemoryWin32HandleKHR vkGetMemoryWin32HandleKHR{};
|
||||||
#endif
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
PFN_vkGetAndroidHardwareBufferPropertiesANDROID vkGetAndroidHardwareBufferPropertiesANDROID{};
|
|
||||||
#endif
|
#endif
|
||||||
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
PFN_vkGetPipelineExecutablePropertiesKHR vkGetPipelineExecutablePropertiesKHR{};
|
||||||
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
PFN_vkGetPipelineExecutableStatisticsKHR vkGetPipelineExecutableStatisticsKHR{};
|
||||||
@@ -1089,34 +1085,6 @@ public:
|
|||||||
VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer,
|
VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer,
|
||||||
void* pnext = nullptr) const noexcept;
|
void* pnext = nullptr) const noexcept;
|
||||||
|
|
||||||
VkResult GetMemoryHostPointerPropertiesEXT(
|
|
||||||
VkExternalMemoryHandleTypeFlagBits handle_type, const void* host_pointer,
|
|
||||||
VkMemoryHostPointerPropertiesEXT* out_properties) const noexcept {
|
|
||||||
return dld->vkGetMemoryHostPointerPropertiesEXT(handle, handle_type, host_pointer,
|
|
||||||
out_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
VkResult GetAndroidHardwareBufferPropertiesANDROID(
|
|
||||||
const struct AHardwareBuffer* buffer,
|
|
||||||
VkAndroidHardwareBufferPropertiesANDROID* out_properties) const noexcept {
|
|
||||||
return dld->vkGetAndroidHardwareBufferPropertiesANDROID(handle, buffer, out_properties);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VkResult CreateBufferRaw(const VkBufferCreateInfo& ci, VkBuffer* out_buffer) const noexcept {
|
|
||||||
return dld->vkCreateBuffer(handle, &ci, nullptr, out_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestroyBufferRaw(VkBuffer buffer) const noexcept {
|
|
||||||
dld->vkDestroyBuffer(handle, buffer, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkResult BindBufferMemory(VkBuffer buffer, VkDeviceMemory memory,
|
|
||||||
VkDeviceSize offset) const noexcept {
|
|
||||||
return dld->vkBindBufferMemory(handle, buffer, memory, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
|
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
|
||||||
|
|
||||||
std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
|
std::vector<VkPipelineExecutablePropertiesKHR> GetPipelineExecutablePropertiesKHR(
|
||||||
|
|||||||
Reference in New Issue
Block a user