texture_cache: Implement asynchronous flushing

This commit is contained in:
ReinUsesLisp
2019-07-09 01:33:29 -03:00
parent e6e75f1c12
commit f733682aa7
5 changed files with 122 additions and 30 deletions

View File

@@ -245,28 +245,61 @@ StagingBuffer::StagingBuffer(std::size_t size) {
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
}
StagingBuffer::~StagingBuffer() = default;
void StagingBuffer::QueueFence() {
sync.Release();
sync.Create();
StagingBuffer::~StagingBuffer() {
if (sync) {
glDeleteSync(sync);
}
}
bool StagingBuffer::IsAvailable() const {
if (!sync.handle) {
return true;
}
switch (glClientWaitSync(sync.handle, 0, 0)) {
void StagingBuffer::QueueFence(bool own) {
DEBUG_ASSERT(!sync);
owned = own;
sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
void StagingBuffer::WaitFence() {
ASSERT(sync);
switch (glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED)) {
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
return true;
break;
case GL_TIMEOUT_EXPIRED:
return false;
case GL_WAIT_FAILED:
UNREACHABLE_MSG("Fence wait failed");
break;
}
Discard();
}
void StagingBuffer::Discard() {
DEBUG_ASSERT(sync);
glDeleteSync(sync);
sync = nullptr;
owned = false;
}
bool StagingBuffer::IsAvailable() {
if (owned) {
return false;
}
if (!sync) {
return true;
}
UNREACHABLE();
switch (glClientWaitSync(sync, 0, 0)) {
case GL_TIMEOUT_EXPIRED:
return false;
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
break;
case GL_WAIT_FAILED:
UNREACHABLE_MSG("Fence wait failed");
break;
default:
UNREACHABLE_MSG("Unknown glClientWaitSync result");
break;
}
glDeleteSync(sync);
sync = nullptr;
return true;
}
@@ -308,7 +341,10 @@ void CachedSurface::DownloadTexture(StagingBuffer& buffer) {
static_cast<GLsizei>(params.GetHostMipmapSize(level)), mip_offset);
}
}
glFinish();
// According to Cemu glGetTextureImage and friends do not flush, resulting in a softlock if we
// wait for a fence. To fix this we have to explicitly flush and then queue a fence.
glFlush();
buffer.QueueFence(true);
}
void CachedSurface::UploadTexture(StagingBuffer& buffer) {
@@ -319,7 +355,7 @@ void CachedSurface::UploadTexture(StagingBuffer& buffer) {
for (u32 level = 0; level < params.emulated_levels; ++level) {
UploadTextureMipmap(level, buffer);
}
buffer.QueueFence();
buffer.QueueFence(false);
}
void CachedSurface::UploadTextureMipmap(u32 level, const StagingBuffer& staging_buffer) {

View File

@@ -122,9 +122,13 @@ public:
explicit StagingBuffer(std::size_t size);
~StagingBuffer();
void QueueFence();
void QueueFence(bool own);
[[nodiscard]] bool IsAvailable() const;
void WaitFence();
void Discard();
[[nodiscard]] bool IsAvailable();
[[nodiscard]] GLuint GetHandle() const {
return buffer.handle;
@@ -136,8 +140,9 @@ public:
private:
OGLBuffer buffer;
OGLSync sync;
GLsync sync{};
u8* pointer{};
bool owned{};
};
class TextureCacheOpenGL final : public TextureCacheBase {

View File

@@ -25,7 +25,7 @@ public:
const u32 ceil = Common::Log2Ceil64(size);
auto& buffers = cache[ceil];
const auto it = std::find_if(buffers.begin(), buffers.end(),
[](const auto& buffer) { return buffer->IsAvailable(); });
[](auto& buffer) { return buffer->IsAvailable(); });
if (it != buffers.end()) {
return **it;
}

View File

@@ -177,9 +177,24 @@ public:
virtual void DownloadTexture(StagingBufferType& buffer) = 0;
void SetFlushBuffer(StagingBufferType* buffer) {
flush_buffer = buffer;
}
StagingBufferType* GetFlushBuffer() const {
return flush_buffer;
}
void MarkAsModified(const bool is_modified_, const u64 tick) {
is_modified = is_modified_ || is_target;
modification_tick = tick;
if (is_modified && flush_buffer) {
// The buffer has been modified while we thought it was no longer being to be used and
// we queued a flush.
flush_buffer->Discard();
flush_buffer = nullptr;
}
}
void MarkAsRenderTarget(const bool is_target, const u32 index) {
@@ -303,6 +318,8 @@ private:
bool is_picked{};
u32 index{NO_RT};
u64 modification_tick{};
StagingBufferType* flush_buffer{};
};
} // namespace VideoCommon

View File

@@ -133,12 +133,18 @@ public:
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents, true);
if (depth_buffer.target)
if (auto& old_target = depth_buffer.target; old_target != surface_view.first) {
FlushAoT(old_target);
}
if (depth_buffer.target) {
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
}
depth_buffer.target = surface_view.first;
depth_buffer.view = surface_view.second;
if (depth_buffer.target)
if (depth_buffer.target) {
depth_buffer.target->MarkAsRenderTarget(true, DEPTH_RT);
}
return surface_view.second;
}
@@ -167,12 +173,18 @@ public:
auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
preserve_contents, true);
if (render_targets[index].target)
if (auto& old_target = render_targets[index].target; old_target != surface_view.first) {
FlushAoT(old_target);
}
if (render_targets[index].target) {
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
}
render_targets[index].target = surface_view.first;
render_targets[index].view = surface_view.second;
if (render_targets[index].target)
if (render_targets[index].target) {
render_targets[index].target->MarkAsRenderTarget(true, static_cast<u32>(index));
}
return surface_view.second;
}
@@ -189,19 +201,25 @@ public:
}
void SetEmptyDepthBuffer() {
if (depth_buffer.target == nullptr) {
auto& target = depth_buffer.target;
if (target == nullptr) {
return;
}
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
FlushAoT(target);
target->MarkAsRenderTarget(false, NO_RT);
depth_buffer.target = nullptr;
depth_buffer.view = nullptr;
}
void SetEmptyColorBuffer(std::size_t index) {
if (render_targets[index].target == nullptr) {
auto& target = render_targets[index].target;
if (target == nullptr) {
return;
}
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
FlushAoT(target);
target->MarkAsRenderTarget(false, NO_RT);
render_targets[index].target = nullptr;
render_targets[index].view = nullptr;
}
@@ -697,9 +715,16 @@ private:
if (!surface->IsModified()) {
return;
}
auto& buffer = staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
surface->DownloadTexture(buffer);
surface->FlushBuffer(system.GPU().MemoryManager(), buffer.GetPointer());
auto buffer = surface->GetFlushBuffer();
if (!buffer) {
buffer = &staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
surface->DownloadTexture(*buffer);
}
buffer->WaitFence();
surface->SetFlushBuffer(nullptr);
surface->FlushBuffer(system.GPU().MemoryManager(), buffer->GetPointer());
surface->MarkAsModified(false, Tick());
}
@@ -767,6 +792,15 @@ private:
return {};
}
void FlushAoT(TSurface& surface) {
if (!surface || !surface->IsLinear() || surface->GetFlushBuffer()) {
return;
}
auto& buffer = staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
surface->DownloadTexture(buffer);
surface->SetFlushBuffer(&buffer);
}
constexpr PixelFormat GetSiblingFormat(PixelFormat format) const {
return siblings_table[static_cast<std::size_t>(format)];
}