mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 04:24:31 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29825a3290 | |||
| 10e924c8cb | |||
| f67a300a69 | |||
| 1330abd2b6 | |||
| db708d2ea0 | |||
| cf76977977 | |||
| 20e6c163a6 | |||
| dfb2fa717e | |||
| 787f80e05c | |||
| 142eeb0b8a | |||
| c394eef32e | |||
| a2d24b3eb7 | |||
| 59ae72c97d | |||
| 723afa7d46 | |||
| 8d96b2e894 | |||
| 1b01f5c93a | |||
| 43ed5cf9b5 | |||
| 3c2f298101 | |||
| cece80d688 | |||
| 0b1c69bc39 | |||
| f3bd7aa402 | |||
| e0a742277a | |||
| aec1658697 | |||
| 65e45f0a9a | |||
| a8f4fdd20f | |||
| b24d8b3912 |
+1
-1
@@ -594,7 +594,7 @@ abstract class SettingsItem(
|
||||
IntSetting.ANDROID_PIPELINE_WORKERS,
|
||||
titleId = R.string.pipeline_worker_cores,
|
||||
descriptionId = R.string.pipeline_worker_cores_description,
|
||||
min = 4,
|
||||
min = 1,
|
||||
max = 8,
|
||||
units = "cores"
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace AndroidSettings {
|
||||
&show_performance_overlay};
|
||||
|
||||
|
||||
Settings::Setting<s32> pipeline_worker_count{linkage, 4, "pipeline_worker_count",
|
||||
Settings::Setting<s32> pipeline_worker_count{linkage, 2, "pipeline_worker_count",
|
||||
Settings::Category::Android,
|
||||
Settings::Specialization::Default,
|
||||
true,
|
||||
|
||||
@@ -157,6 +157,8 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info,
|
||||
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||
}
|
||||
|
||||
constexpr size_t NCE_WRITE_FAULT_CLUSTER_PAGES = 4;
|
||||
|
||||
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||
|
||||
@@ -165,7 +167,7 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi
|
||||
const Common::ProcessAddress addr =
|
||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
|
||||
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE * NCE_WRITE_FAULT_CLUSTER_PAGES)) {
|
||||
// We handled the access successfully and are returning to guest code.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,10 @@ public:
|
||||
// New batch API to update multiple ranges with a single lock acquisition.
|
||||
void UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta);
|
||||
|
||||
void UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta);
|
||||
|
||||
[[nodiscard]] bool IsRegionTextureCached(DAddr addr, size_t size) const noexcept;
|
||||
|
||||
private:
|
||||
struct TranslationEntry {
|
||||
DAddr guest_page{};
|
||||
@@ -234,6 +238,7 @@ private:
|
||||
(1ULL << (device_virtual_bits - page_bits)) / subentries;
|
||||
using CachedPages = std::array<CounterEntry, num_counter_entries>;
|
||||
std::unique_ptr<CachedPages> cached_pages;
|
||||
std::unique_ptr<CachedPages> texture_cached_pages;
|
||||
Common::RangeMutex counter_guard;
|
||||
std::mutex mapping_guard;
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
||||
{
|
||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||
cached_pages = std::make_unique<CachedPages>();
|
||||
texture_cached_pages = std::make_unique<CachedPages>();
|
||||
|
||||
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||
for (size_t i = 0; i < total_virtual; i++) {
|
||||
@@ -625,6 +626,28 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
|
||||
UpdatePagesCachedCountNoLock(addr, size, delta);
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta) {
|
||||
Common::ScopedRangeLock lk(counter_guard, addr, size);
|
||||
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
|
||||
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
|
||||
CounterAtomicType& count = texture_cached_pages->at(page >> subentries_shift).Count(page);
|
||||
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
bool DeviceMemoryManager<Traits>::IsRegionTextureCached(DAddr addr, size_t size) const noexcept {
|
||||
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
|
||||
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
|
||||
if (texture_cached_pages->at(page >> subentries_shift).Count(page).load(
|
||||
std::memory_order_acquire) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
|
||||
if (ranges.empty()) {
|
||||
|
||||
@@ -121,6 +121,15 @@ void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CpuWriteInvalidate(DAddr device_addr, u64 size) {
|
||||
if (!memory_tracker.CpuMarkIfNotGpuModified(device_addr, size)) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{mutex};
|
||||
WriteMemory(device_addr, size);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::CachedWriteMemory(DAddr device_addr, u64 size) {
|
||||
const bool is_dirty = IsRegionRegistered(device_addr, size);
|
||||
@@ -175,9 +184,71 @@ std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(DA
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::DownloadMemory(DAddr device_addr, u64 size) {
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
|
||||
DownloadBufferMemory(buffer, device_addr, size);
|
||||
if constexpr (!USE_MEMORY_MAPS) {
|
||||
std::scoped_lock lock{mutex};
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId, Buffer& buffer) {
|
||||
DownloadBufferMemory(buffer, device_addr, size);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
boost::container::small_vector<std::pair<BufferCopy, BufferId>, 8> downloads;
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
memory_tracker.ForEachDownloadRangeAndClear(
|
||||
device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||
const DAddr buffer_addr = buffer.CpuAddr();
|
||||
const auto add_download = [&](DAddr start, DAddr end) {
|
||||
const u64 new_offset = start - buffer_addr;
|
||||
const u64 new_size = end - start;
|
||||
downloads.push_back({
|
||||
BufferCopy{
|
||||
.src_offset = new_offset,
|
||||
.dst_offset = total_size_bytes,
|
||||
.size = new_size,
|
||||
},
|
||||
buffer_id,
|
||||
});
|
||||
constexpr u64 align = 64ULL;
|
||||
constexpr u64 mask = ~(align - 1ULL);
|
||||
total_size_bytes += (new_size + align - 1) & mask;
|
||||
largest_copy = (std::max)(largest_copy, new_size);
|
||||
};
|
||||
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, add_download);
|
||||
ClearDownload(device_addr_out, range_size);
|
||||
gpu_modified_ranges.Subtract(device_addr_out, range_size);
|
||||
});
|
||||
});
|
||||
if (total_size_bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
|
||||
boost::container::small_vector<BufferCopy, 8> writebacks;
|
||||
runtime.PreCopyBarrier();
|
||||
for (auto& [copy, buffer_id] : downloads) {
|
||||
copy.dst_offset += download_staging.offset;
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
buffer.MarkUsage(copy.src_offset, copy.size);
|
||||
const std::array copies{copy};
|
||||
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
|
||||
BufferCopy writeback{copy};
|
||||
writeback.src_offset = static_cast<u64>(buffer.CpuAddr()) + copy.src_offset;
|
||||
writebacks.push_back(writeback);
|
||||
}
|
||||
runtime.PostCopyBarrier();
|
||||
lock.unlock();
|
||||
|
||||
runtime.Finish();
|
||||
const u8* const base = download_staging.mapped_span.data();
|
||||
for (const BufferCopy& writeback : writebacks) {
|
||||
const u64 staging_offset = writeback.dst_offset - download_staging.offset;
|
||||
device_memory.WriteBlockUnsafe(static_cast<DAddr>(writeback.src_offset),
|
||||
base + staging_offset, writeback.size);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -214,7 +285,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
||||
auto& src_buffer = slot_buffers[buffer_a];
|
||||
auto& dest_buffer = slot_buffers[buffer_b];
|
||||
SynchronizeBuffer(src_buffer, *cpu_src_address, static_cast<u32>(amount));
|
||||
SynchronizeBuffer(dest_buffer, *cpu_dest_address, static_cast<u32>(amount));
|
||||
memory_tracker.UnmarkRegionAsCpuModified(*cpu_dest_address, static_cast<u32>(amount));
|
||||
std::array copies{BufferCopy{
|
||||
.src_offset = src_buffer.Offset(*cpu_src_address),
|
||||
.dst_offset = dest_buffer.Offset(*cpu_dest_address),
|
||||
@@ -673,32 +744,44 @@ void BufferCache<P>::PopAsyncFlushes() {
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::PopAsyncBuffers() {
|
||||
if (async_buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!async_buffers.front().has_value()) {
|
||||
struct Writeback {
|
||||
DAddr addr;
|
||||
const u8* src;
|
||||
u64 size;
|
||||
};
|
||||
boost::container::small_vector<Writeback, 8> writebacks;
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
if (async_buffers.empty()) {
|
||||
return;
|
||||
}
|
||||
if (!async_buffers.front().has_value()) {
|
||||
async_buffers.pop_front();
|
||||
return;
|
||||
}
|
||||
auto& downloads = pending_downloads.front();
|
||||
auto& async_buffer = async_buffers.front();
|
||||
const u8* base = async_buffer->mapped_span.data();
|
||||
const size_t base_offset = async_buffer->offset;
|
||||
for (const auto& copy : downloads) {
|
||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
||||
const u64 dst_offset = copy.dst_offset - base_offset;
|
||||
const u8* read_mapped_memory = base + dst_offset;
|
||||
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
|
||||
writebacks.push_back(
|
||||
{start, &read_mapped_memory[start - device_addr], end - start});
|
||||
});
|
||||
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
||||
gpu_modified_ranges.Subtract(start, end - start);
|
||||
});
|
||||
}
|
||||
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||
async_buffers.pop_front();
|
||||
return;
|
||||
pending_downloads.pop_front();
|
||||
}
|
||||
auto& downloads = pending_downloads.front();
|
||||
auto& async_buffer = async_buffers.front();
|
||||
u8* base = async_buffer->mapped_span.data();
|
||||
const size_t base_offset = async_buffer->offset;
|
||||
for (const auto& copy : downloads) {
|
||||
const DAddr device_addr = static_cast<DAddr>(copy.src_offset);
|
||||
const u64 dst_offset = copy.dst_offset - base_offset;
|
||||
const u8* read_mapped_memory = base + dst_offset;
|
||||
async_downloads.ForEachInRange(device_addr, copy.size, [&](DAddr start, DAddr end, s32) {
|
||||
device_memory.WriteBlockUnsafe(start, &read_mapped_memory[start - device_addr],
|
||||
end - start);
|
||||
});
|
||||
async_downloads.Subtract(device_addr, copy.size, [&](DAddr start, DAddr end) {
|
||||
gpu_modified_ranges.Subtract(start, end - start);
|
||||
});
|
||||
for (const auto& wb : writebacks) {
|
||||
device_memory.WriteBlockUnsafe(wb.addr, wb.src, wb.size);
|
||||
}
|
||||
async_buffers_death_ring.emplace_back(*async_buffer);
|
||||
async_buffers.pop_front();
|
||||
pending_downloads.pop_front();
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -1638,6 +1721,9 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
|
||||
|
||||
template <class P>
|
||||
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) {
|
||||
if (!memory_tracker.HasCpuModifiedCheap(device_addr, size)) {
|
||||
return true;
|
||||
}
|
||||
upload_copies.clear();
|
||||
u64 total_size_bytes = 0;
|
||||
u64 largest_copy = 0;
|
||||
|
||||
@@ -217,6 +217,8 @@ public:
|
||||
|
||||
void WriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
void CpuWriteInvalidate(DAddr device_addr, u64 size);
|
||||
|
||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
bool OnCPUWrite(DAddr device_addr, u64 size);
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <utility>
|
||||
@@ -49,6 +51,24 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasCpuModifiedCheap(VAddr query_cpu_addr, u64 query_size) noexcept {
|
||||
std::size_t remaining_size{query_size};
|
||||
std::size_t page_index{query_cpu_addr >> HIGHER_PAGE_BITS};
|
||||
u64 page_offset{query_cpu_addr & HIGHER_PAGE_MASK};
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
const Manager* manager = top_tier[page_index].load(std::memory_order_acquire);
|
||||
if (manager == nullptr || manager->CpuModifiedPageCount() != 0) {
|
||||
return true;
|
||||
}
|
||||
page_index++;
|
||||
page_offset = 0;
|
||||
remaining_size -= copy_amount;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if a region has been modified from the CPU
|
||||
[[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
|
||||
return IteratePages<true>(query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
|
||||
@@ -130,12 +150,28 @@ public:
|
||||
}
|
||||
|
||||
void FlushCachedWrites() noexcept {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
for (auto id : cached_pages) {
|
||||
top_tier[id]->FlushCachedWrites();
|
||||
top_tier[id].load(std::memory_order_relaxed)->FlushCachedWrites();
|
||||
}
|
||||
cached_pages.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CpuMarkIfNotGpuModified(VAddr addr, u64 size) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
const bool gpu = IteratePagesNoLock<false>(
|
||||
addr, size, [](Manager* manager, u64 offset, size_t sz) {
|
||||
return manager->IsRegionModified(Type::GPU, offset, sz);
|
||||
});
|
||||
if (gpu) {
|
||||
return true;
|
||||
}
|
||||
IteratePagesNoLock<true>(addr, size, [](Manager* manager, u64 offset, size_t sz) {
|
||||
manager->ChangeRegionState(Type::CPU, true, manager->cpu_addr + offset, sz);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
|
||||
template <typename Func>
|
||||
void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) {
|
||||
@@ -162,6 +198,12 @@ public:
|
||||
private:
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
bool IteratePages(VAddr cpu_address, size_t size, Func&& func) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
return IteratePagesNoLock<create_region_on_fail>(cpu_address, size, std::forward<Func>(func));
|
||||
}
|
||||
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
bool IteratePagesNoLock(VAddr cpu_address, size_t size, Func&& func) {
|
||||
using FuncReturn = typename std::invoke_result<Func, Manager*, u64, size_t>::type;
|
||||
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
|
||||
std::size_t remaining_size{size};
|
||||
@@ -170,7 +212,7 @@ private:
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
auto* manager{top_tier[page_index]};
|
||||
auto* manager{top_tier[page_index].load(std::memory_order_relaxed)};
|
||||
if (manager) {
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(manager, page_offset, copy_amount)) {
|
||||
@@ -181,7 +223,7 @@ private:
|
||||
}
|
||||
} else if constexpr (create_region_on_fail) {
|
||||
CreateRegion(page_index);
|
||||
manager = top_tier[page_index];
|
||||
manager = top_tier[page_index].load(std::memory_order_relaxed);
|
||||
if constexpr (BOOL_BREAK) {
|
||||
if (func(manager, page_offset, copy_amount)) {
|
||||
return true;
|
||||
@@ -199,6 +241,7 @@ private:
|
||||
|
||||
template <bool create_region_on_fail, typename Func>
|
||||
std::pair<u64, u64> IteratePairs(VAddr cpu_address, size_t size, Func&& func) {
|
||||
std::scoped_lock lk{tracker_mutex};
|
||||
std::size_t remaining_size{size};
|
||||
std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
|
||||
u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
|
||||
@@ -207,7 +250,7 @@ private:
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount{
|
||||
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
|
||||
auto* manager{top_tier[page_index]};
|
||||
auto* manager{top_tier[page_index].load(std::memory_order_relaxed)};
|
||||
const auto execute = [&] {
|
||||
auto [new_begin, new_end] = func(manager, page_offset, copy_amount);
|
||||
if (new_begin != 0 || new_end != 0) {
|
||||
@@ -220,7 +263,7 @@ private:
|
||||
execute();
|
||||
} else if constexpr (create_region_on_fail) {
|
||||
CreateRegion(page_index);
|
||||
manager = top_tier[page_index];
|
||||
manager = top_tier[page_index].load(std::memory_order_relaxed);
|
||||
execute();
|
||||
}
|
||||
page_index++;
|
||||
@@ -236,7 +279,7 @@ private:
|
||||
|
||||
void CreateRegion(std::size_t page_index) {
|
||||
const VAddr base_cpu_addr = page_index << HIGHER_PAGE_BITS;
|
||||
top_tier[page_index] = GetNewManager(base_cpu_addr);
|
||||
top_tier[page_index].store(GetNewManager(base_cpu_addr), std::memory_order_release);
|
||||
}
|
||||
|
||||
Manager* GetNewManager(VAddr base_cpu_address) {
|
||||
@@ -254,11 +297,12 @@ private:
|
||||
return new_manager;
|
||||
}
|
||||
|
||||
std::array<Manager*, NUM_HIGH_PAGES> top_tier{};
|
||||
std::array<std::atomic<Manager*>, NUM_HIGH_PAGES> top_tier{};
|
||||
std::deque<std::array<Manager, MANAGER_POOL_SIZE>> manager_pool;
|
||||
std::deque<Manager*> free_managers;
|
||||
ankerl::unordered_dense::set<u32> cached_pages;
|
||||
DeviceTracker* device_tracker = nullptr;
|
||||
std::mutex tracker_mutex;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <limits>
|
||||
#include <span>
|
||||
@@ -48,6 +49,11 @@ struct WordManager {
|
||||
u64 const last_word = (~u64{0} << shift) >> shift;
|
||||
heap[num_words * size_t(Type::CPU) + num_words - 1] = last_word;
|
||||
heap[num_words * size_t(Type::Untracked) + num_words - 1] = last_word;
|
||||
u32 cpu_pages = 0;
|
||||
for (size_t i = 0; i < num_words; ++i) {
|
||||
cpu_pages += static_cast<u32>(std::popcount(heap[num_words * size_t(Type::CPU) + i]));
|
||||
}
|
||||
cpu_modified_pages.store(cpu_pages, std::memory_order_relaxed);
|
||||
}
|
||||
explicit WordManager() = default;
|
||||
|
||||
@@ -120,10 +126,15 @@ struct WordManager {
|
||||
[[maybe_unused]] std::span<u64> untracked_words = Span(Type::Untracked);
|
||||
[[maybe_unused]] std::span<u64> cached_words = Span(Type::CachedCPU);
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
|
||||
if (type == Type::CPU || type == Type::CachedCPU) {
|
||||
CollectChangedRanges(!enable, index, untracked_words[index], mask, ranges);
|
||||
}
|
||||
if (type == Type::CPU) {
|
||||
const u64 old = state_words[index];
|
||||
cpu_delta += enable ? std::popcount(~old & mask) : -std::popcount(old & mask);
|
||||
}
|
||||
if (enable) {
|
||||
state_words[index] |= mask;
|
||||
if (type == Type::CPU || type == Type::CachedCPU)
|
||||
@@ -138,6 +149,9 @@ struct WordManager {
|
||||
untracked_words[index] &= ~mask;
|
||||
}
|
||||
});
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (!ranges.empty()) {
|
||||
ApplyCollectedRanges(ranges, (!enable) ? 1 : -1);
|
||||
}
|
||||
@@ -165,6 +179,7 @@ struct WordManager {
|
||||
(pending_pointer - pending_offset) * BYTES_PER_PAGE);
|
||||
};
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
IterateWords(offset, size, [&](size_t index, u64 mask) {
|
||||
if (type == Type::GPU)
|
||||
mask &= ~untracked_words[index];
|
||||
@@ -173,6 +188,8 @@ struct WordManager {
|
||||
if (type == Type::CPU || type == Type::CachedCPU) {
|
||||
CollectChangedRanges(true, index, untracked_words[index], mask, ranges);
|
||||
}
|
||||
if (type == Type::CPU)
|
||||
cpu_delta -= std::popcount(word);
|
||||
state_words[index] &= ~mask;
|
||||
if (type == Type::CPU || type == Type::CachedCPU)
|
||||
untracked_words[index] &= ~mask;
|
||||
@@ -194,6 +211,9 @@ struct WordManager {
|
||||
}
|
||||
});
|
||||
});
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (pending) {
|
||||
release();
|
||||
}
|
||||
@@ -248,13 +268,18 @@ struct WordManager {
|
||||
auto const untracked_words = Span(Type::Untracked);
|
||||
auto const cpu_words = Span(Type::CPU);
|
||||
std::vector<std::pair<VAddr, u64>> ranges;
|
||||
s64 cpu_delta = 0;
|
||||
for (u64 word_index = 0; word_index < num_words; ++word_index) {
|
||||
const u64 cached_bits = cached_words[word_index];
|
||||
CollectChangedRanges(false, word_index, untracked_words[word_index], cached_bits, ranges);
|
||||
cpu_delta += std::popcount(~cpu_words[word_index] & cached_bits);
|
||||
untracked_words[word_index] |= cached_bits;
|
||||
cpu_words[word_index] |= cached_bits;
|
||||
cached_words[word_index] = 0;
|
||||
}
|
||||
if (cpu_delta != 0) {
|
||||
cpu_modified_pages.fetch_add(static_cast<u32>(cpu_delta), std::memory_order_release);
|
||||
}
|
||||
if (!ranges.empty()) {
|
||||
ApplyCollectedRanges(ranges, -1);
|
||||
}
|
||||
@@ -319,9 +344,14 @@ struct WordManager {
|
||||
return std::span<const u64>(heap.data() + num_words * size_t(type), num_words);
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 CpuModifiedPageCount() const noexcept {
|
||||
return cpu_modified_pages.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::array<u64, size_t(Type::Max) * num_words> heap = {};
|
||||
DeviceTracker* tracker = nullptr;
|
||||
VAddr cpu_addr = 0;
|
||||
std::atomic<u32> cpu_modified_pages{0};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -91,9 +91,6 @@ public:
|
||||
func();
|
||||
}
|
||||
fences.push(std::move(new_fence));
|
||||
if (should_flush) {
|
||||
rasterizer.FlushCommands();
|
||||
}
|
||||
if constexpr (can_async_check) {
|
||||
guard.unlock();
|
||||
cv.notify_all();
|
||||
@@ -238,10 +235,10 @@ private:
|
||||
|
||||
void PopAsyncFlushes() {
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.PopAsyncFlushes();
|
||||
buffer_cache.PopAsyncFlushes();
|
||||
}
|
||||
buffer_cache.PopAsyncFlushes();
|
||||
query_cache.PopAsyncFlushes();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ set(GLSL_INCLUDES
|
||||
|
||||
set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/astc_decoder.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||
|
||||
@@ -964,35 +964,70 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
|
||||
}
|
||||
|
||||
uint UnquantizeTexelWeight(EncodingData val) {
|
||||
uint encoding = Encoding(val), bitlen = NumBits(val), bitval = BitValue(val);
|
||||
if (encoding == JUST_BITS) {
|
||||
return (bitlen >= 1 && bitlen <= 5)
|
||||
? uint(floor(0.5f + float(bitval) * 64.0f / float((1 << bitlen) - 1)))
|
||||
: FastReplicateTo6(bitval, bitlen);
|
||||
} else if (encoding == TRIT || encoding == QUINT) {
|
||||
uint B = 0, C = 0, D = 0;
|
||||
uint b_mask = (0x3100 >> (bitlen * 4)) & 0xf;
|
||||
uint b = (bitval >> 1) & b_mask;
|
||||
const uint encoding = Encoding(val);
|
||||
const uint bitlen = NumBits(val);
|
||||
const uint bitval = BitValue(val);
|
||||
const uint A = ReplicateBitTo7((bitval & 1));
|
||||
uint B = 0, C = 0, D = 0;
|
||||
uint result = 0;
|
||||
const uint bitlen_0_results[5] = {0, 16, 32, 48, 64};
|
||||
switch (encoding) {
|
||||
case JUST_BITS:
|
||||
return FastReplicateTo6(bitval, bitlen);
|
||||
case TRIT: {
|
||||
D = QuintTritValue(val);
|
||||
if (encoding == TRIT) {
|
||||
switch (bitlen) {
|
||||
case 0: return D * 32; //0,32,64
|
||||
case 1: C = 50; break;
|
||||
case 2: C = 23; B = (b << 6) | (b << 2) | b; break;
|
||||
case 3: C = 11; B = (b << 5) | b; break;
|
||||
}
|
||||
} else if (encoding == QUINT) {
|
||||
switch (bitlen) {
|
||||
case 0: return D * 16; //0, 16, 32, 48, 64
|
||||
case 1: C = 28; break;
|
||||
case 2: C = 13; B = (b << 6) | (b << 1); break;
|
||||
}
|
||||
switch (bitlen) {
|
||||
case 0:
|
||||
return bitlen_0_results[D * 2];
|
||||
case 1: {
|
||||
C = 50;
|
||||
break;
|
||||
}
|
||||
uint A = ReplicateBitTo7(bitval & 1);
|
||||
uint res = (A & 0x20) | (((D * C + B) ^ A) >> 2);
|
||||
return res + (res > 32 ? 1 : 0);
|
||||
case 2: {
|
||||
C = 23;
|
||||
const uint b = (bitval >> 1) & 1;
|
||||
B = (b << 6) | (b << 2) | b;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
C = 11;
|
||||
const uint cb = (bitval >> 1) & 3;
|
||||
B = (cb << 5) | cb;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case QUINT: {
|
||||
D = QuintTritValue(val);
|
||||
switch (bitlen) {
|
||||
case 0:
|
||||
return bitlen_0_results[D];
|
||||
case 1: {
|
||||
C = 28;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
C = 13;
|
||||
const uint b = (bitval >> 1) & 1;
|
||||
B = (b << 6) | (b << 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (encoding != JUST_BITS && bitlen > 0) {
|
||||
result = D * C + B;
|
||||
result ^= A;
|
||||
result = (A & 0x20) | (result >> 2);
|
||||
}
|
||||
if (result > 32) {
|
||||
result += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void UnquantizeTexelWeights(uvec2 size, bool is_dual_plane) {
|
||||
@@ -1394,11 +1429,10 @@ void DecompressBlock(ivec3 coord) {
|
||||
}
|
||||
|
||||
uint SwizzleOffset(uvec2 pos) {
|
||||
return ((pos.x & 32u) << 3u) |
|
||||
((pos.y & 6u) << 5u) |
|
||||
((pos.x & 16u) << 1u) |
|
||||
((pos.y & 1u) << 4u) |
|
||||
(pos.x & 15u);
|
||||
const uint x = pos.x;
|
||||
const uint y = pos.y;
|
||||
return ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 +
|
||||
((x % 32) / 16) * 32 + (y % 2) * 16 + (x % 16);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450
|
||||
|
||||
#ifdef VULKAN
|
||||
#define VERTEX_ID gl_VertexIndex
|
||||
#else
|
||||
#define VERTEX_ID gl_VertexID
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
float x = float((VERTEX_ID & 1) << 2);
|
||||
float y = float((VERTEX_ID & 2) << 1);
|
||||
gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0);
|
||||
}
|
||||
@@ -485,7 +485,6 @@ void RasterizerOpenGL::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
|
||||
texture_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::BufferCache))) {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::QueryCache))) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_command_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
||||
#include "video_core/vulkan_common/vulkan_device.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -14,32 +18,52 @@ constexpr size_t COMMAND_BUFFER_POOL_SIZE = 4;
|
||||
struct CommandPool::Pool {
|
||||
vk::CommandPool handle;
|
||||
vk::CommandBuffers cmdbufs;
|
||||
u64 tick;
|
||||
};
|
||||
|
||||
CommandPool::CommandPool(MasterSemaphore& master_semaphore_, const Device& device_)
|
||||
: ResourcePool(master_semaphore_, COMMAND_BUFFER_POOL_SIZE), device{device_} {}
|
||||
: master_semaphore{master_semaphore_}, device{device_} {}
|
||||
|
||||
CommandPool::~CommandPool() = default;
|
||||
|
||||
void CommandPool::Allocate(size_t begin, size_t end) {
|
||||
// Command buffers are going to be committed, recorded, executed every single usage cycle.
|
||||
// They are also going to be reset when committed.
|
||||
void CommandPool::AllocatePool() {
|
||||
Pool& pool = pools.emplace_back();
|
||||
pool.handle = device.GetLogical().CreateCommandPool({
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags =
|
||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
|
||||
.queueFamilyIndex = device.GetGraphicsFamily(),
|
||||
});
|
||||
pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
|
||||
pool.tick = 0;
|
||||
}
|
||||
|
||||
void CommandPool::AcquirePool() {
|
||||
if (!pools.empty()) {
|
||||
master_semaphore.Refresh();
|
||||
const u64 gpu_tick = master_semaphore.KnownGpuTick();
|
||||
for (size_t i = 0; i < pools.size(); ++i) {
|
||||
const size_t candidate = (current_pool + 1 + i) % pools.size();
|
||||
if (gpu_tick >= pools[candidate].tick) {
|
||||
current_pool = candidate;
|
||||
current_index = 0;
|
||||
pools[current_pool].handle.Reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
AllocatePool();
|
||||
current_pool = pools.size() - 1;
|
||||
current_index = 0;
|
||||
}
|
||||
|
||||
VkCommandBuffer CommandPool::Commit() {
|
||||
const size_t index = CommitResource();
|
||||
const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
|
||||
const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
|
||||
return pools[pool_index].cmdbufs[sub_index];
|
||||
if (pools.empty() || current_index >= COMMAND_BUFFER_POOL_SIZE) {
|
||||
AcquirePool();
|
||||
}
|
||||
Pool& pool = pools[current_pool];
|
||||
pool.tick = master_semaphore.CurrentTick();
|
||||
return pool.cmdbufs[current_index++];
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -6,7 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -14,20 +17,24 @@ namespace Vulkan {
|
||||
class Device;
|
||||
class MasterSemaphore;
|
||||
|
||||
class CommandPool final : public ResourcePool {
|
||||
class CommandPool final {
|
||||
public:
|
||||
explicit CommandPool(MasterSemaphore& master_semaphore_, const Device& device_);
|
||||
~CommandPool() override;
|
||||
|
||||
void Allocate(size_t begin, size_t end) override;
|
||||
~CommandPool();
|
||||
|
||||
VkCommandBuffer Commit();
|
||||
|
||||
private:
|
||||
struct Pool;
|
||||
|
||||
void AllocatePool();
|
||||
void AcquirePool();
|
||||
|
||||
MasterSemaphore& master_semaphore;
|
||||
const Device& device;
|
||||
std::vector<Pool> pools;
|
||||
size_t current_pool = 0;
|
||||
size_t current_index = 0;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
@@ -17,6 +18,8 @@
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/host_shaders/astc_decoder_comp_spv.h"
|
||||
#include "video_core/host_shaders/astc_decoder_frag_spv.h"
|
||||
#include "video_core/host_shaders/astc_decoder_vert_spv.h"
|
||||
#include "video_core/host_shaders/queries_prefix_scan_sum_comp_spv.h"
|
||||
#include "video_core/host_shaders/queries_prefix_scan_sum_nosubgroups_comp_spv.h"
|
||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||
@@ -25,8 +28,11 @@
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/maxwell_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_render_pass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/texture_cache/accelerated_swizzle.h"
|
||||
@@ -613,7 +619,394 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, image_barrier);
|
||||
});
|
||||
scheduler.Finish();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct AstcFragPushConstants {
|
||||
std::array<u32, 2> blocks_dims;
|
||||
u32 layer_stride;
|
||||
u32 block_size;
|
||||
u32 x_shift;
|
||||
u32 block_height;
|
||||
u32 block_height_mask;
|
||||
u32 dest_layer;
|
||||
};
|
||||
|
||||
constexpr VkDescriptorSetLayoutBinding ASTC_FRAG_BINDING{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkDescriptorUpdateTemplateEntry ASTC_FRAG_TEMPLATE{
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = 0,
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
};
|
||||
|
||||
constexpr DescriptorBankInfo ASTC_FRAG_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 1,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 0,
|
||||
.score = 1,
|
||||
};
|
||||
|
||||
constexpr VkPushConstantRange ASTC_FRAG_PUSH_CONSTANT_RANGE{
|
||||
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.offset = 0,
|
||||
.size = static_cast<u32>(sizeof(AstcFragPushConstants)),
|
||||
};
|
||||
|
||||
constexpr VkPipelineVertexInputStateCreateInfo ASTC_FRAG_VERTEX_INPUT{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.vertexBindingDescriptionCount = 0,
|
||||
.pVertexBindingDescriptions = nullptr,
|
||||
.vertexAttributeDescriptionCount = 0,
|
||||
.pVertexAttributeDescriptions = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkPipelineInputAssemblyStateCreateInfo ASTC_FRAG_INPUT_ASSEMBLY{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
constexpr VkPipelineViewportStateCreateInfo ASTC_FRAG_VIEWPORT{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.viewportCount = 1,
|
||||
.pViewports = nullptr,
|
||||
.scissorCount = 1,
|
||||
.pScissors = nullptr,
|
||||
};
|
||||
|
||||
constexpr VkPipelineRasterizationStateCreateInfo ASTC_FRAG_RASTERIZATION{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
constexpr VkPipelineMultisampleStateCreateInfo ASTC_FRAG_MULTISAMPLE{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.minSampleShading = 0.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
constexpr VkPipelineColorBlendAttachmentState ASTC_FRAG_BLEND_ATTACHMENT{
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
constexpr std::array ASTC_FRAG_DYNAMIC_STATES{VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR};
|
||||
} // Anonymous namespace
|
||||
|
||||
ASTCDecoderFragmentPass::ASTCDecoderFragmentPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_,
|
||||
RenderPassCache& render_pass_cache_)
|
||||
: device{device_}, scheduler{scheduler_},
|
||||
compute_pass_descriptor_queue{compute_pass_descriptor_queue_},
|
||||
render_pass_cache{render_pass_cache_},
|
||||
descriptor_set_layout{device.GetLogical().CreateDescriptorSetLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &ASTC_FRAG_BINDING,
|
||||
})},
|
||||
descriptor_template{device.GetLogical().CreateDescriptorUpdateTemplate({
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.descriptorUpdateEntryCount = 1,
|
||||
.pDescriptorUpdateEntries = &ASTC_FRAG_TEMPLATE,
|
||||
.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET,
|
||||
.descriptorSetLayout = *descriptor_set_layout,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.pipelineLayout = VK_NULL_HANDLE,
|
||||
.set = 0,
|
||||
})},
|
||||
pipeline_layout{device.GetLogical().CreatePipelineLayout({
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = descriptor_set_layout.address(),
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &ASTC_FRAG_PUSH_CONSTANT_RANGE,
|
||||
})},
|
||||
descriptor_allocator{descriptor_pool_.Allocator(device_, scheduler_, *descriptor_set_layout,
|
||||
ASTC_FRAG_BANK_INFO)},
|
||||
vertex_shader{BuildShader(device, ASTC_DECODER_VERT_SPV)},
|
||||
fragment_shader{BuildShader(device, ASTC_DECODER_FRAG_SPV)} {}
|
||||
|
||||
ASTCDecoderFragmentPass::~ASTCDecoderFragmentPass() = default;
|
||||
|
||||
VkPipeline ASTCDecoderFragmentPass::FindOrEmplacePipeline(VkRenderPass render_pass) {
|
||||
const auto it = std::ranges::find(pipeline_keys, render_pass);
|
||||
if (it != pipeline_keys.end()) {
|
||||
return *pipelines[std::distance(pipeline_keys.begin(), it)];
|
||||
}
|
||||
pipeline_keys.push_back(render_pass);
|
||||
const std::array stages{
|
||||
VkPipelineShaderStageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = *vertex_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
VkPipelineShaderStageCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = *fragment_shader,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = nullptr,
|
||||
},
|
||||
};
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_CLEAR,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &ASTC_FRAG_BLEND_ATTACHMENT,
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
const VkPipelineDynamicStateCreateInfo dynamic_state{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.dynamicStateCount = static_cast<u32>(ASTC_FRAG_DYNAMIC_STATES.size()),
|
||||
.pDynamicStates = ASTC_FRAG_DYNAMIC_STATES.data(),
|
||||
};
|
||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &ASTC_FRAG_VERTEX_INPUT,
|
||||
.pInputAssemblyState = &ASTC_FRAG_INPUT_ASSEMBLY,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &ASTC_FRAG_VIEWPORT,
|
||||
.pRasterizationState = &ASTC_FRAG_RASTERIZATION,
|
||||
.pMultisampleState = &ASTC_FRAG_MULTISAMPLE,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &color_blend,
|
||||
.pDynamicState = &dynamic_state,
|
||||
.layout = *pipeline_layout,
|
||||
.renderPass = render_pass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void ASTCDecoderFragmentPass::Assemble(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||
VideoCore::Surface::PixelFormat decoded_format) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
while (!frame_resources.empty() && scheduler.IsFree(frame_resources.front().tick)) {
|
||||
frame_resources.pop_front();
|
||||
}
|
||||
const std::array<u32, 2> block_dims{
|
||||
VideoCore::Surface::DefaultBlockWidth(image.info.format),
|
||||
VideoCore::Surface::DefaultBlockHeight(image.info.format),
|
||||
};
|
||||
RenderPassKey key{};
|
||||
key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||
key.color_formats[0] = decoded_format;
|
||||
key.depth_format = VideoCore::Surface::PixelFormat::Invalid;
|
||||
key.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
const VkRenderPass render_pass = render_pass_cache.Get(key);
|
||||
const VkPipeline pipeline = FindOrEmplacePipeline(render_pass);
|
||||
const VkFormat vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, decoded_format).format;
|
||||
const VkImage vk_image = image.Handle();
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
scheduler.Record([vk_image, aspect_mask, is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = static_cast<VkAccessFlags>(
|
||||
is_initialized ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_NONE),
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.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_GRAPHICS_COMPUTE_TRANSFER,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, barrier);
|
||||
});
|
||||
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
const u32 level = swizzle.level;
|
||||
const u32 width = std::max(1u, image.info.size.width >> level);
|
||||
const u32 height = std::max(1u, image.info.size.height >> level);
|
||||
const u32 layers = image.info.resources.layers;
|
||||
for (u32 layer = 0; layer < layers; ++layer) {
|
||||
vk::ImageView view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = vk_image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = vk_format,
|
||||
.components{},
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
});
|
||||
vk::Framebuffer framebuffer =
|
||||
device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = render_pass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = view.address(),
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = 1,
|
||||
});
|
||||
const AstcFragPushConstants pc{
|
||||
.blocks_dims = block_dims,
|
||||
.layer_stride = params.layer_stride,
|
||||
.block_size = params.block_size,
|
||||
.x_shift = params.x_shift,
|
||||
.block_height = params.block_height,
|
||||
.block_height_mask = params.block_height_mask,
|
||||
.dest_layer = layer,
|
||||
};
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 1);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
scheduler.Record([this, pipeline, render_pass, descriptor_data, pc, width, height,
|
||||
fb = *framebuffer](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
const VkRenderPassBeginInfo begin{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = render_pass,
|
||||
.framebuffer = fb,
|
||||
.renderArea{.offset = {0, 0}, .extent = {width, height}},
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
const VkViewport viewport{
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(width),
|
||||
.height = static_cast<float>(height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
const VkRect2D scissor{.offset = {0, 0}, .extent = {width, height}};
|
||||
cmdbuf.BeginRenderPass(begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, scissor);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, set,
|
||||
{});
|
||||
cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, pc);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
});
|
||||
frame_resources.push_back(FrameResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.view = std::move(view),
|
||||
.framebuffer = std::move(framebuffer),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
.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_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0, barrier);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0;
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
@@ -137,6 +140,44 @@ private:
|
||||
MemoryAllocator& memory_allocator;
|
||||
};
|
||||
|
||||
class RenderPassCache;
|
||||
|
||||
class ASTCDecoderFragmentPass final {
|
||||
public:
|
||||
explicit ASTCDecoderFragmentPass(const Device& device_, Scheduler& scheduler_,
|
||||
DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_,
|
||||
RenderPassCache& render_pass_cache_);
|
||||
~ASTCDecoderFragmentPass();
|
||||
|
||||
void Assemble(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles,
|
||||
VideoCore::Surface::PixelFormat decoded_format);
|
||||
|
||||
private:
|
||||
VkPipeline FindOrEmplacePipeline(VkRenderPass render_pass);
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
RenderPassCache& render_pass_cache;
|
||||
vk::DescriptorSetLayout descriptor_set_layout;
|
||||
vk::DescriptorUpdateTemplate descriptor_template;
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
DescriptorAllocator descriptor_allocator;
|
||||
vk::ShaderModule vertex_shader;
|
||||
vk::ShaderModule fragment_shader;
|
||||
std::vector<VkRenderPass> pipeline_keys;
|
||||
std::vector<vk::Pipeline> pipelines;
|
||||
|
||||
struct FrameResources {
|
||||
u64 tick;
|
||||
vk::ImageView view;
|
||||
vk::Framebuffer framebuffer;
|
||||
};
|
||||
std::deque<FrameResources> frame_resources;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle3DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle3DPass(const Device& device_, Scheduler& scheduler_,
|
||||
|
||||
@@ -167,6 +167,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
}
|
||||
const Shader::Stage stage{program.stage};
|
||||
const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough};
|
||||
const bool has_tessellation{key.unique_hashes[3] != 0};
|
||||
const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
|
||||
const float point_size{std::bit_cast<float>(key.state.point_size)};
|
||||
switch (stage) {
|
||||
@@ -185,7 +186,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
LOG_WARNING(Render_Vulkan, "XFB requested in pipeline key but device lacks VK_EXT_transform_feedback; ignoring XFB decorations");
|
||||
}
|
||||
}
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
if (!has_tessellation) {
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
}
|
||||
}
|
||||
if (key.state.dynamic_vertex_input) {
|
||||
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
|
||||
@@ -224,6 +227,9 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
|
||||
ASSERT(false);
|
||||
return Shader::TessSpacing::Equal;
|
||||
}();
|
||||
if (!has_geometry) {
|
||||
info.convert_depth_mode = gl_ndc;
|
||||
}
|
||||
break;
|
||||
case Shader::Stage::Geometry:
|
||||
if (program.output_topology == Shader::OutputTopology::PointList) {
|
||||
@@ -305,12 +311,8 @@ size_t GetTotalPipelineWorkers() {
|
||||
std::max<size_t>(static_cast<size_t>(std::thread::hardware_concurrency()), 2ULL) - 1ULL;
|
||||
#ifdef __ANDROID__
|
||||
const int configured = AndroidSettings::values.pipeline_worker_count.GetValue();
|
||||
const int clamped = std::clamp(configured, 4, 8);
|
||||
const size_t desired = static_cast<size_t>(clamped);
|
||||
if (desired == 0) {
|
||||
return 1ULL;
|
||||
}
|
||||
return std::min(max_core_threads, desired);
|
||||
const size_t desired = static_cast<size_t>(std::max(configured, 1));
|
||||
return std::min<size_t>(max_core_threads, desired);
|
||||
#else
|
||||
return max_core_threads;
|
||||
#endif
|
||||
|
||||
@@ -241,11 +241,13 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
if (!pipeline) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
// update engine as channel may be different.
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
if (!pipeline->Configure(is_indexed))
|
||||
return;
|
||||
{
|
||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||
if (!pipeline->Configure(is_indexed)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDynamicStates();
|
||||
|
||||
@@ -673,7 +675,6 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
|
||||
texture_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::BufferCache))) {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.DownloadMemory(addr, size);
|
||||
}
|
||||
if ((True(which & VideoCommon::CacheType::QueryCache))) {
|
||||
@@ -771,16 +772,22 @@ bool RasterizerVulkan::OnCPUWrite(DAddr addr, u64 size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP = true;
|
||||
static constexpr bool ENABLE_FINE_GRAINED_TRACKER_LOCK = true;
|
||||
|
||||
void RasterizerVulkan::OnCacheInvalidation(DAddr addr, u64 size) {
|
||||
if (addr == 0 || size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
if (!ENABLE_TEXTURE_CACHE_INVALIDATION_SKIP ||
|
||||
device_memory.IsRegionTextureCached(addr, size)) {
|
||||
std::scoped_lock lock{texture_cache.mutex};
|
||||
texture_cache.WriteMemory(addr, size);
|
||||
}
|
||||
{
|
||||
if (ENABLE_FINE_GRAINED_TRACKER_LOCK) {
|
||||
buffer_cache.CpuWriteInvalidate(addr, size);
|
||||
} else {
|
||||
std::scoped_lock lock{buffer_cache.mutex};
|
||||
buffer_cache.WriteMemory(addr, size);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,9 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
if (info.storage) {
|
||||
usage |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
if (IsPixelFormatASTC(format)) {
|
||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
@@ -911,6 +914,10 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
if (Settings::values.accelerate_astc.GetValue() == Settings::AstcDecodeMode::Gpu) {
|
||||
astc_decoder_pass.emplace(device, scheduler, descriptor_pool, staging_buffer_pool,
|
||||
compute_pass_descriptor_queue, memory_allocator);
|
||||
if (device.IsTiler()) {
|
||||
astc_decoder_fragment_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue, render_pass_cache);
|
||||
}
|
||||
}
|
||||
if (!device.IsKhrImageFormatListSupported()) {
|
||||
return;
|
||||
@@ -2850,6 +2857,13 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
u32 z_start, u32 z_count) {
|
||||
|
||||
if (IsPixelFormatASTC(image.info.format)) {
|
||||
if (astc_decoder_fragment_pass) {
|
||||
const VideoCore::Surface::PixelFormat decoded_format =
|
||||
WillUseWidenedAstcFormat(device, image.info)
|
||||
? VideoCore::Surface::PixelFormat::R32G32B32A32_FLOAT
|
||||
: VideoCore::Surface::PixelFormat::A8B8G8R8_UNORM;
|
||||
return astc_decoder_fragment_pass->Assemble(image, map, swizzles, decoded_format);
|
||||
}
|
||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
|
||||
void AccelerateImageUpload(Image&, const StagingBufferRef&,
|
||||
std::span<const VideoCommon::SwizzleParameters>,
|
||||
u32 z_start, u32 z_count);
|
||||
u32 z_start = 0, u32 z_count = 0);
|
||||
|
||||
void InsertUploadMemoryBarrier() {}
|
||||
|
||||
@@ -147,6 +147,7 @@ public:
|
||||
BlitImageHelper& blit_image_helper;
|
||||
RenderPassCache& render_pass_cache;
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
std::optional<ASTCDecoderFragmentPass> astc_decoder_fragment_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
|
||||
@@ -2308,6 +2308,7 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
if (image.cpu_addr < ~(1ULL << 40)) {
|
||||
device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2320,12 +2321,14 @@ void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
|
||||
const DAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ForEachSparseSegment(image,
|
||||
[this]([[maybe_unused]] GPUVAddr gpu_addr, DAddr cpu_addr, size_t size) {
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, 1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2336,6 +2339,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
|
||||
if (False(image.flags & ImageFlagBits::Sparse)) {
|
||||
if (image.cpu_addr < ~(1ULL << 40)) {
|
||||
device_memory.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
device_memory.UpdateTexturePagesCount(image.cpu_addr, image.guest_size_bytes, -1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2348,6 +2352,7 @@ void TextureCache<P>::UntrackImage(ImageBase& image, ImageId image_id) {
|
||||
const DAddr cpu_addr = map.cpu_addr;
|
||||
const std::size_t size = map.size;
|
||||
device_memory.UpdatePagesCachedCount(cpu_addr, size, -1);
|
||||
device_memory.UpdateTexturePagesCount(cpu_addr, size, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ public:
|
||||
/// Prepare an image to be used
|
||||
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
std::mutex mutex;
|
||||
|
||||
private:
|
||||
/// Iterate over all page indices in a range
|
||||
|
||||
@@ -517,16 +517,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls.");
|
||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken workgroup memory explicit layout.");
|
||||
RemoveExtensionFeature(extensions.workgroup_memory_explicit_layout,
|
||||
features.workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken conservative rasterization.");
|
||||
RemoveExtension(extensions.conservative_rasterization,
|
||||
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken depth clip control.");
|
||||
RemoveExtensionFeature(extensions.depth_clip_control, features.depth_clip_control,
|
||||
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64.");
|
||||
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
|
||||
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
|
||||
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
|
||||
features.shader_atomic_int64.shaderSharedInt64Atomics = false;
|
||||
features.features.shaderInt64 = false;
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken workgroup memory explicit layout.");
|
||||
RemoveExtensionFeature(extensions.workgroup_memory_explicit_layout,
|
||||
features.workgroup_memory_explicit_layout,
|
||||
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
|
||||
|
||||
#if defined(__ANDROID__) && defined(ARCHITECTURE_arm64)
|
||||
// BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load.
|
||||
|
||||
@@ -230,6 +230,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkMapMemory);
|
||||
X(vkQueueSubmit);
|
||||
X(vkQueueSubmit2);
|
||||
X(vkResetCommandPool);
|
||||
X(vkResetFences);
|
||||
X(vkResetQueryPool);
|
||||
X(vkSetDebugUtilsObjectNameEXT);
|
||||
|
||||
@@ -346,6 +346,7 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkMapMemory vkMapMemory{};
|
||||
PFN_vkQueueSubmit vkQueueSubmit{};
|
||||
PFN_vkQueueSubmit2 vkQueueSubmit2{};
|
||||
PFN_vkResetCommandPool vkResetCommandPool{};
|
||||
PFN_vkResetFences vkResetFences{};
|
||||
PFN_vkResetQueryPool vkResetQueryPool{};
|
||||
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT{};
|
||||
@@ -925,6 +926,10 @@ public:
|
||||
CommandBuffers Allocate(std::size_t num_buffers,
|
||||
VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const;
|
||||
|
||||
void Reset(VkCommandPoolResetFlags flags = 0) const {
|
||||
Check(dld->vkResetCommandPool(owner, handle, flags));
|
||||
}
|
||||
|
||||
/// Set object name.
|
||||
void SetObjectNameEXT(const char* name) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user