|
@@ -6,6 +6,7 @@
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
#include <algorithm>
|
|
|
#include <array>
|
|
#include <array>
|
|
|
|
|
+#include <list>
|
|
|
#include <memory>
|
|
#include <memory>
|
|
|
#include <mutex>
|
|
#include <mutex>
|
|
|
#include <set>
|
|
#include <set>
|
|
@@ -62,6 +63,30 @@ public:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ void OnCPUWrite(VAddr addr, std::size_t size) {
|
|
|
|
|
+ std::lock_guard lock{mutex};
|
|
|
|
|
+
|
|
|
|
|
+ for (const auto& surface : GetSurfacesInRegion(addr, size)) {
|
|
|
|
|
+ if (surface->IsMemoryMarked()) {
|
|
|
|
|
+ UnmarkMemory(surface);
|
|
|
|
|
+ surface->SetSyncPending(true);
|
|
|
|
|
+ marked_for_unregister.emplace_back(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void SyncGuestHost() {
|
|
|
|
|
+ std::lock_guard lock{mutex};
|
|
|
|
|
+
|
|
|
|
|
+ for (const auto& surface : marked_for_unregister) {
|
|
|
|
|
+ if (surface->IsRegistered()) {
|
|
|
|
|
+ surface->SetSyncPending(false);
|
|
|
|
|
+ Unregister(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ marked_for_unregister.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Guarantees that rendertargets don't unregister themselves if the
|
|
* Guarantees that rendertargets don't unregister themselves if the
|
|
|
* collide. Protection is currently only done on 3D slices.
|
|
* collide. Protection is currently only done on 3D slices.
|
|
@@ -85,10 +110,20 @@ public:
|
|
|
return a->GetModificationTick() < b->GetModificationTick();
|
|
return a->GetModificationTick() < b->GetModificationTick();
|
|
|
});
|
|
});
|
|
|
for (const auto& surface : surfaces) {
|
|
for (const auto& surface : surfaces) {
|
|
|
|
|
+ mutex.unlock();
|
|
|
FlushSurface(surface);
|
|
FlushSurface(surface);
|
|
|
|
|
+ mutex.lock();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ bool MustFlushRegion(VAddr addr, std::size_t size) {
|
|
|
|
|
+ std::lock_guard lock{mutex};
|
|
|
|
|
+
|
|
|
|
|
+ const auto surfaces = GetSurfacesInRegion(addr, size);
|
|
|
|
|
+ return std::any_of(surfaces.cbegin(), surfaces.cend(),
|
|
|
|
|
+ [](const TSurface& surface) { return surface->IsModified(); });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
TView GetTextureSurface(const Tegra::Texture::TICEntry& tic,
|
|
TView GetTextureSurface(const Tegra::Texture::TICEntry& tic,
|
|
|
const VideoCommon::Shader::Sampler& entry) {
|
|
const VideoCommon::Shader::Sampler& entry) {
|
|
|
std::lock_guard lock{mutex};
|
|
std::lock_guard lock{mutex};
|
|
@@ -206,8 +241,14 @@ public:
|
|
|
|
|
|
|
|
auto surface_view = GetSurface(gpu_addr, *cpu_addr,
|
|
auto surface_view = GetSurface(gpu_addr, *cpu_addr,
|
|
|
SurfaceParams::CreateForFramebuffer(system, index), true);
|
|
SurfaceParams::CreateForFramebuffer(system, index), true);
|
|
|
- if (render_targets[index].target)
|
|
|
|
|
- render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
|
|
|
|
|
|
|
+ if (render_targets[index].target) {
|
|
|
|
|
+ auto& surface = render_targets[index].target;
|
|
|
|
|
+ surface->MarkAsRenderTarget(false, NO_RT);
|
|
|
|
|
+ const auto& cr_params = surface->GetSurfaceParams();
|
|
|
|
|
+ if (!cr_params.is_tiled && Settings::values.use_asynchronous_gpu_emulation) {
|
|
|
|
|
+ AsyncFlushSurface(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
render_targets[index].target = surface_view.first;
|
|
render_targets[index].target = surface_view.first;
|
|
|
render_targets[index].view = surface_view.second;
|
|
render_targets[index].view = surface_view.second;
|
|
|
if (render_targets[index].target)
|
|
if (render_targets[index].target)
|
|
@@ -284,6 +325,34 @@ public:
|
|
|
return ++ticks;
|
|
return ++ticks;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ void CommitAsyncFlushes() {
|
|
|
|
|
+ committed_flushes.push_back(uncommitted_flushes);
|
|
|
|
|
+ uncommitted_flushes.reset();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool HasUncommittedFlushes() const {
|
|
|
|
|
+ return uncommitted_flushes != nullptr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool ShouldWaitAsyncFlushes() const {
|
|
|
|
|
+ return !committed_flushes.empty() && committed_flushes.front() != nullptr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void PopAsyncFlushes() {
|
|
|
|
|
+ if (committed_flushes.empty()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ auto& flush_list = committed_flushes.front();
|
|
|
|
|
+ if (!flush_list) {
|
|
|
|
|
+ committed_flushes.pop_front();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (TSurface& surface : *flush_list) {
|
|
|
|
|
+ FlushSurface(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+ committed_flushes.pop_front();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
protected:
|
|
protected:
|
|
|
explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
|
explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
|
|
bool is_astc_supported)
|
|
bool is_astc_supported)
|
|
@@ -345,9 +414,20 @@ protected:
|
|
|
surface->SetCpuAddr(*cpu_addr);
|
|
surface->SetCpuAddr(*cpu_addr);
|
|
|
RegisterInnerCache(surface);
|
|
RegisterInnerCache(surface);
|
|
|
surface->MarkAsRegistered(true);
|
|
surface->MarkAsRegistered(true);
|
|
|
|
|
+ surface->SetMemoryMarked(true);
|
|
|
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
|
|
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ void UnmarkMemory(TSurface surface) {
|
|
|
|
|
+ if (!surface->IsMemoryMarked()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const std::size_t size = surface->GetSizeInBytes();
|
|
|
|
|
+ const VAddr cpu_addr = surface->GetCpuAddr();
|
|
|
|
|
+ rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
|
|
|
|
|
+ surface->SetMemoryMarked(false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
void Unregister(TSurface surface) {
|
|
void Unregister(TSurface surface) {
|
|
|
if (guard_render_targets && surface->IsProtected()) {
|
|
if (guard_render_targets && surface->IsProtected()) {
|
|
|
return;
|
|
return;
|
|
@@ -355,9 +435,11 @@ protected:
|
|
|
if (!guard_render_targets && surface->IsRenderTarget()) {
|
|
if (!guard_render_targets && surface->IsRenderTarget()) {
|
|
|
ManageRenderTargetUnregister(surface);
|
|
ManageRenderTargetUnregister(surface);
|
|
|
}
|
|
}
|
|
|
- const std::size_t size = surface->GetSizeInBytes();
|
|
|
|
|
- const VAddr cpu_addr = surface->GetCpuAddr();
|
|
|
|
|
- rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
|
|
|
|
|
|
|
+ UnmarkMemory(surface);
|
|
|
|
|
+ if (surface->IsSyncPending()) {
|
|
|
|
|
+ marked_for_unregister.remove(surface);
|
|
|
|
|
+ surface->SetSyncPending(false);
|
|
|
|
|
+ }
|
|
|
UnregisterInnerCache(surface);
|
|
UnregisterInnerCache(surface);
|
|
|
surface->MarkAsRegistered(false);
|
|
surface->MarkAsRegistered(false);
|
|
|
ReserveSurface(surface->GetSurfaceParams(), surface);
|
|
ReserveSurface(surface->GetSurfaceParams(), surface);
|
|
@@ -417,7 +499,7 @@ private:
|
|
|
**/
|
|
**/
|
|
|
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
|
|
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
|
|
|
const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
|
|
const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
|
|
|
- if (Settings::values.use_accurate_gpu_emulation) {
|
|
|
|
|
|
|
+ if (Settings::IsGPULevelExtreme()) {
|
|
|
return RecycleStrategy::Flush;
|
|
return RecycleStrategy::Flush;
|
|
|
}
|
|
}
|
|
|
// 3D Textures decision
|
|
// 3D Textures decision
|
|
@@ -461,7 +543,7 @@ private:
|
|
|
}
|
|
}
|
|
|
switch (PickStrategy(overlaps, params, gpu_addr, untopological)) {
|
|
switch (PickStrategy(overlaps, params, gpu_addr, untopological)) {
|
|
|
case RecycleStrategy::Ignore: {
|
|
case RecycleStrategy::Ignore: {
|
|
|
- return InitializeSurface(gpu_addr, params, Settings::values.use_accurate_gpu_emulation);
|
|
|
|
|
|
|
+ return InitializeSurface(gpu_addr, params, Settings::IsGPULevelExtreme());
|
|
|
}
|
|
}
|
|
|
case RecycleStrategy::Flush: {
|
|
case RecycleStrategy::Flush: {
|
|
|
std::sort(overlaps.begin(), overlaps.end(),
|
|
std::sort(overlaps.begin(), overlaps.end(),
|
|
@@ -509,7 +591,7 @@ private:
|
|
|
}
|
|
}
|
|
|
const auto& final_params = new_surface->GetSurfaceParams();
|
|
const auto& final_params = new_surface->GetSurfaceParams();
|
|
|
if (cr_params.type != final_params.type) {
|
|
if (cr_params.type != final_params.type) {
|
|
|
- if (Settings::values.use_accurate_gpu_emulation) {
|
|
|
|
|
|
|
+ if (Settings::IsGPULevelExtreme()) {
|
|
|
BufferCopy(current_surface, new_surface);
|
|
BufferCopy(current_surface, new_surface);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
@@ -598,7 +680,7 @@ private:
|
|
|
if (passed_tests == 0) {
|
|
if (passed_tests == 0) {
|
|
|
return {};
|
|
return {};
|
|
|
// In Accurate GPU all tests should pass, else we recycle
|
|
// In Accurate GPU all tests should pass, else we recycle
|
|
|
- } else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) {
|
|
|
|
|
|
|
+ } else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
|
|
|
return {};
|
|
return {};
|
|
|
}
|
|
}
|
|
|
for (const auto& surface : overlaps) {
|
|
for (const auto& surface : overlaps) {
|
|
@@ -668,7 +750,7 @@ private:
|
|
|
for (const auto& surface : overlaps) {
|
|
for (const auto& surface : overlaps) {
|
|
|
if (!surface->MatchTarget(params.target)) {
|
|
if (!surface->MatchTarget(params.target)) {
|
|
|
if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
|
|
if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) {
|
|
|
- if (Settings::values.use_accurate_gpu_emulation) {
|
|
|
|
|
|
|
+ if (Settings::IsGPULevelExtreme()) {
|
|
|
return std::nullopt;
|
|
return std::nullopt;
|
|
|
}
|
|
}
|
|
|
Unregister(surface);
|
|
Unregister(surface);
|
|
@@ -1106,6 +1188,13 @@ private:
|
|
|
TView view;
|
|
TView view;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ void AsyncFlushSurface(TSurface& surface) {
|
|
|
|
|
+ if (!uncommitted_flushes) {
|
|
|
|
|
+ uncommitted_flushes = std::make_shared<std::list<TSurface>>();
|
|
|
|
|
+ }
|
|
|
|
|
+ uncommitted_flushes->push_back(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
VideoCore::RasterizerInterface& rasterizer;
|
|
VideoCore::RasterizerInterface& rasterizer;
|
|
|
|
|
|
|
|
FormatLookupTable format_lookup_table;
|
|
FormatLookupTable format_lookup_table;
|
|
@@ -1150,6 +1239,11 @@ private:
|
|
|
std::unordered_map<u32, TSurface> invalid_cache;
|
|
std::unordered_map<u32, TSurface> invalid_cache;
|
|
|
std::vector<u8> invalid_memory;
|
|
std::vector<u8> invalid_memory;
|
|
|
|
|
|
|
|
|
|
+ std::list<TSurface> marked_for_unregister;
|
|
|
|
|
+
|
|
|
|
|
+ std::shared_ptr<std::list<TSurface>> uncommitted_flushes{};
|
|
|
|
|
+ std::list<std::shared_ptr<std::list<TSurface>>> committed_flushes;
|
|
|
|
|
+
|
|
|
StagingCache staging_cache;
|
|
StagingCache staging_cache;
|
|
|
std::recursive_mutex mutex;
|
|
std::recursive_mutex mutex;
|
|
|
};
|
|
};
|