staging_buffer_cache: Disable OpenGL staging buffers for Intel proprietary drivers

This commit is contained in:
ReinUsesLisp
2019-07-10 01:49:01 -03:00
parent 256caec9ef
commit 7ac5d2bac8
9 changed files with 350 additions and 153 deletions

View File

@@ -64,6 +64,8 @@ add_library(video_core STATIC
renderer_opengl/gl_shader_manager.h
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_shader_util.h
renderer_opengl/gl_staging_buffer.cpp
renderer_opengl/gl_staging_buffer.h
renderer_opengl/gl_state.cpp
renderer_opengl/gl_state.h
renderer_opengl/gl_stream_buffer.cpp

View File

@@ -4,6 +4,8 @@
#include <array>
#include <cstddef>
#include <string_view>
#include <glad/glad.h>
#include "common/logging/log.h"
@@ -23,6 +25,9 @@ T GetInteger(GLenum pname) {
} // Anonymous namespace
Device::Device() {
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
const bool intel_proprietary = vendor == "Intel";
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
@@ -32,6 +37,7 @@ Device::Device() {
has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
has_variable_aoffi = TestVariableAoffi();
has_component_indexing_bug = TestComponentIndexingBug();
has_broken_pbo_streaming = intel_proprietary;
}
Device::Device(std::nullptr_t) {
@@ -42,6 +48,7 @@ Device::Device(std::nullptr_t) {
has_vertex_viewport_layer = true;
has_variable_aoffi = true;
has_component_indexing_bug = false;
has_broken_pbo_streaming = false;
}
bool Device::TestVariableAoffi() {

View File

@@ -46,6 +46,10 @@ public:
return has_component_indexing_bug;
}
bool HasBrokenPBOStreaming() const {
return has_broken_pbo_streaming;
}
private:
static bool TestVariableAoffi();
static bool TestComponentIndexingBug();
@@ -58,6 +62,7 @@ private:
bool has_vertex_viewport_layer{};
bool has_variable_aoffi{};
bool has_component_indexing_bug{};
bool has_broken_pbo_streaming{};
};
} // namespace OpenGL

View File

@@ -0,0 +1,148 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include <glad/glad.h>
#include "common/assert.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_staging_buffer.h"
namespace OpenGL {
StagingBufferCache::StagingBufferCache(const Device& device)
: VideoCommon::StagingBufferCache<StagingBuffer>{!device.HasBrokenPBOStreaming()},
device{device} {}
StagingBufferCache::~StagingBufferCache() = default;
std::unique_ptr<StagingBuffer> StagingBufferCache::CreateBuffer(std::size_t size, bool is_flush) {
if (device.HasBrokenPBOStreaming()) {
return std::unique_ptr<StagingBuffer>(new CpuStagingBuffer(size, is_flush));
} else {
return std::unique_ptr<StagingBuffer>(new PersistentStagingBuffer(size, is_flush));
}
}
PersistentStagingBuffer::PersistentStagingBuffer(std::size_t size, bool is_readable)
: is_readable{is_readable} {
buffer.Create();
glNamedBufferStorage(buffer.handle, static_cast<GLsizeiptr>(size), nullptr,
GL_MAP_PERSISTENT_BIT |
(is_readable ? GL_MAP_READ_BIT : GL_MAP_WRITE_BIT));
pointer = reinterpret_cast<u8*>(glMapNamedBufferRange(
buffer.handle, 0, static_cast<GLsizeiptr>(size),
GL_MAP_PERSISTENT_BIT | (is_readable ? GL_MAP_READ_BIT
: (GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT |
GL_MAP_UNSYNCHRONIZED_BIT))));
}
PersistentStagingBuffer::~PersistentStagingBuffer() {
if (sync) {
glDeleteSync(sync);
}
}
u8* PersistentStagingBuffer::GetOpenGLPointer() const {
return nullptr;
}
u8* PersistentStagingBuffer::Map([[maybe_unused]] std::size_t size) const {
return pointer;
}
void PersistentStagingBuffer::Unmap(std::size_t size) const {
if (!is_readable) {
glFlushMappedNamedBufferRange(buffer.handle, 0, size);
}
}
void PersistentStagingBuffer::QueueFence(bool own) {
DEBUG_ASSERT(!sync);
owned = own;
sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
void PersistentStagingBuffer::WaitFence() {
DEBUG_ASSERT(sync);
switch (glClientWaitSync(sync, 0, GL_TIMEOUT_IGNORED)) {
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
break;
case GL_TIMEOUT_EXPIRED:
case GL_WAIT_FAILED:
UNREACHABLE_MSG("Fence wait failed");
break;
}
Discard();
}
void PersistentStagingBuffer::Discard() {
DEBUG_ASSERT(sync);
glDeleteSync(sync);
sync = nullptr;
owned = false;
}
bool PersistentStagingBuffer::IsAvailable() {
if (owned) {
return false;
}
if (!sync) {
return true;
}
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;
}
void PersistentStagingBuffer::Bind(GLenum target) const {
glBindBuffer(target, buffer.handle);
}
CpuStagingBuffer::CpuStagingBuffer(std::size_t size, bool is_readable)
: pointer{std::make_unique<u8[]>(size)} {}
CpuStagingBuffer::~CpuStagingBuffer() = default;
u8* CpuStagingBuffer::Map(std::size_t size) const {
return pointer.get();
}
u8* CpuStagingBuffer::GetOpenGLPointer() const {
return pointer.get();
}
void CpuStagingBuffer::Unmap(std::size_t size) const {}
void CpuStagingBuffer::QueueFence(bool own) {}
void CpuStagingBuffer::WaitFence() {}
void CpuStagingBuffer::Discard() {
UNREACHABLE();
}
bool CpuStagingBuffer::IsAvailable() {
return true;
}
void CpuStagingBuffer::Bind(GLenum target) const {
glBindBuffer(target, 0);
}
} // namespace OpenGL

View File

@@ -0,0 +1,105 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/staging_buffer_cache.h"
namespace OpenGL {
class Device;
class StagingBuffer;
class StagingBufferCache final : public VideoCommon::StagingBufferCache<StagingBuffer> {
public:
explicit StagingBufferCache(const Device& device);
~StagingBufferCache() override;
protected:
std::unique_ptr<StagingBuffer> CreateBuffer(std::size_t size, bool is_flush) override;
private:
const Device& device;
};
class StagingBuffer : public NonCopyable {
public:
virtual ~StagingBuffer() = default;
[[nodiscard]] virtual u8* GetOpenGLPointer() const = 0;
[[nodiscard]] virtual u8* Map(std::size_t size) const = 0;
virtual void Unmap(std::size_t size) const = 0;
virtual void QueueFence(bool own) = 0;
virtual void WaitFence() = 0;
virtual void Discard() = 0;
[[nodiscard]] virtual bool IsAvailable() = 0;
virtual void Bind(GLenum target) const = 0;
};
class PersistentStagingBuffer final : public StagingBuffer {
public:
explicit PersistentStagingBuffer(std::size_t size, bool is_readable);
~PersistentStagingBuffer() override;
u8* GetOpenGLPointer() const override;
u8* Map(std::size_t size) const override;
void Unmap(std::size_t size) const override;
void QueueFence(bool own) override;
void WaitFence() override;
void Discard() override;
bool IsAvailable() override;
void Bind(GLenum target) const override;
private:
OGLBuffer buffer;
GLsync sync{};
u8* pointer{};
bool is_readable{};
bool owned{};
};
class CpuStagingBuffer final : public StagingBuffer {
public:
explicit CpuStagingBuffer(std::size_t size, bool is_readable);
~CpuStagingBuffer() override;
u8* GetOpenGLPointer() const override;
u8* Map(std::size_t size) const override;
void Unmap(std::size_t size) const override;
void QueueFence(bool own) override;
void WaitFence() override;
void Discard() override;
bool IsAvailable() override;
void Bind(GLenum target) const override;
private:
std::unique_ptr<u8[]> pointer;
};
} // namespace OpenGL

View File

@@ -10,6 +10,7 @@
#include "core/core.h"
#include "video_core/morton.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_staging_buffer.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_texture_cache.h"
#include "video_core/renderer_opengl/utils.h"
@@ -235,74 +236,6 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
} // Anonymous namespace
StagingBuffer::StagingBuffer(std::size_t size) {
buffer.Create();
glNamedBufferStorage(buffer.handle, static_cast<GLsizeiptr>(size), nullptr,
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
GL_CLIENT_STORAGE_BIT);
pointer = static_cast<u8*>(glMapNamedBufferRange(
buffer.handle, 0, static_cast<GLsizeiptr>(size),
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
}
StagingBuffer::~StagingBuffer() {
if (sync) {
glDeleteSync(sync);
}
}
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:
break;
case GL_TIMEOUT_EXPIRED:
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;
}
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;
}
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
std::vector<u8>& temporary_buffer)
: VideoCommon::SurfaceBase<View, StagingBuffer>{gpu_addr, params, temporary_buffer} {
@@ -324,14 +257,12 @@ CachedSurface::~CachedSurface() = default;
void CachedSurface::DownloadTexture(StagingBuffer& buffer) {
MICROPROFILE_SCOPE(OpenGL_Texture_Download);
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer.GetHandle());
buffer.Bind(GL_PIXEL_PACK_BUFFER);
const auto pointer_base = buffer.GetOpenGLPointer();
for (u32 level = 0; level < params.emulated_levels; ++level) {
glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
const auto mip_offset = reinterpret_cast<void*>(params.GetHostMipmapLevelOffset(level));
const auto mip_offset = pointer_base + params.GetHostMipmapLevelOffset(level);
if (is_compressed) {
glGetCompressedTextureImage(texture.handle, level,
static_cast<GLsizei>(params.GetHostMipmapSize(level)),
@@ -341,6 +272,8 @@ void CachedSurface::DownloadTexture(StagingBuffer& buffer) {
static_cast<GLsizei>(params.GetHostMipmapSize(level)), mip_offset);
}
}
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
// 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();
@@ -349,41 +282,41 @@ void CachedSurface::DownloadTexture(StagingBuffer& buffer) {
void CachedSurface::UploadTexture(StagingBuffer& buffer) {
MICROPROFILE_SCOPE(OpenGL_Texture_Upload);
SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); });
glFlushMappedNamedBufferRange(buffer.GetHandle(), 0,
static_cast<GLsizeiptr>(GetHostSizeInBytes()));
buffer.Bind(GL_PIXEL_UNPACK_BUFFER);
const auto pointer = buffer.GetOpenGLPointer();
for (u32 level = 0; level < params.emulated_levels; ++level) {
UploadTextureMipmap(level, buffer);
UploadTextureMipmap(level, pointer);
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
buffer.QueueFence(false);
}
void CachedSurface::UploadTextureMipmap(u32 level, const StagingBuffer& staging_buffer) {
void CachedSurface::UploadTextureMipmap(u32 level, const u8* opengl_pointer) {
glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
const auto compression_type = params.GetCompressionType();
std::size_t mip_offset = compression_type == SurfaceCompression::Converted
? params.GetConvertedMipmapOffset(level)
: params.GetHostMipmapLevelOffset(level);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, staging_buffer.GetHandle());
const u8* mip_offset = opengl_pointer + (compression_type == SurfaceCompression::Converted
? params.GetConvertedMipmapOffset(level)
: params.GetHostMipmapLevelOffset(level));
if (is_compressed) {
const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))};
switch (params.target) {
case SurfaceTarget::Texture2D:
glCompressedTextureSubImage2D(
texture.handle, level, 0, 0, static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)), internal_format, image_size,
reinterpret_cast<const void*>(mip_offset));
glCompressedTextureSubImage2D(texture.handle, level, 0, 0,
static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)),
internal_format, image_size, mip_offset);
break;
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glCompressedTextureSubImage3D(
texture.handle, level, 0, 0, 0, static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)),
static_cast<GLsizei>(params.GetMipDepth(level)), internal_format, image_size,
reinterpret_cast<const void*>(mip_offset));
glCompressedTextureSubImage3D(texture.handle, level, 0, 0, 0,
static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)),
static_cast<GLsizei>(params.GetMipDepth(level)),
internal_format, image_size, mip_offset);
break;
case SurfaceTarget::TextureCubemap: {
const std::size_t layer_size{params.GetHostLayerSize(level)};
@@ -392,7 +325,7 @@ void CachedSurface::UploadTextureMipmap(u32 level, const StagingBuffer& staging_
static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)), 1,
internal_format, static_cast<GLsizei>(layer_size),
reinterpret_cast<const void*>(mip_offset));
mip_offset);
mip_offset += layer_size;
}
break;
@@ -404,36 +337,33 @@ void CachedSurface::UploadTextureMipmap(u32 level, const StagingBuffer& staging_
switch (params.target) {
case SurfaceTarget::Texture1D:
glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type,
reinterpret_cast<const void*>(mip_offset));
mip_offset);
break;
case SurfaceTarget::TextureBuffer:
ASSERT(level == 0);
glNamedBufferSubData(texture_buffer.handle, 0,
params.GetMipWidth(level) * params.GetBytesPerPixel(),
reinterpret_cast<const void*>(mip_offset));
params.GetMipWidth(level) * params.GetBytesPerPixel(), mip_offset);
break;
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2D:
glTextureSubImage2D(texture.handle, level, 0, 0, params.GetMipWidth(level),
params.GetMipHeight(level), format, type,
reinterpret_cast<const void*>(mip_offset));
params.GetMipHeight(level), format, type, mip_offset);
break;
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray:
glTextureSubImage3D(texture.handle, level, 0, 0, 0,
static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)),
static_cast<GLsizei>(params.GetMipDepth(level)), format, type,
reinterpret_cast<const void*>(mip_offset));
glTextureSubImage3D(
texture.handle, level, 0, 0, 0, static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)),
static_cast<GLsizei>(params.GetMipDepth(level)), format, type, mip_offset);
break;
case SurfaceTarget::TextureCubemap: {
const std::size_t face_size = params.GetHostLayerSize(level);
const std::size_t layer_size = params.GetHostLayerSize(level);
for (std::size_t face = 0; face < params.depth; ++face) {
glTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
params.GetMipWidth(level), params.GetMipHeight(level), 1,
format, type, reinterpret_cast<const void*>(mip_offset));
mip_offset += face_size;
format, type, mip_offset);
mip_offset += layer_size;
}
break;
}
@@ -534,7 +464,7 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
VideoCore::RasterizerInterface& rasterizer,
const Device& device)
: TextureCacheBase{system, rasterizer} {
: TextureCacheBase{system, rasterizer, std::make_unique<StagingBufferCache>(device)} {
src_framebuffer.Create();
dst_framebuffer.Create();
}

View File

@@ -18,6 +18,7 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/renderer_opengl/gl_staging_buffer.h"
namespace OpenGL {
@@ -59,7 +60,7 @@ protected:
View CreateViewInner(const ViewParams& view_key, bool is_proxy);
private:
void UploadTextureMipmap(u32 level, const StagingBuffer& staging_buffer);
void UploadTextureMipmap(u32 level, const u8* opengl_pointer);
GLenum internal_format{};
GLenum format{};
@@ -117,34 +118,6 @@ private:
bool is_proxy;
};
class StagingBuffer final {
public:
explicit StagingBuffer(std::size_t size);
~StagingBuffer();
void QueueFence(bool own);
void WaitFence();
void Discard();
[[nodiscard]] bool IsAvailable();
[[nodiscard]] GLuint GetHandle() const {
return buffer.handle;
}
[[nodiscard]] u8* GetPointer() const {
return pointer;
}
private:
OGLBuffer buffer;
GLsync sync{};
u8* pointer{};
bool owned{};
};
class TextureCacheOpenGL final : public TextureCacheBase {
public:
explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,

View File

@@ -17,24 +17,42 @@ namespace VideoCommon {
template <typename StagingBufferType>
class StagingBufferCache {
public:
explicit StagingBufferCache() = default;
~StagingBufferCache() = default;
using Cache = std::unordered_map<u32, std::vector<std::unique_ptr<StagingBufferType>>>;
StagingBufferType& GetBuffer(std::size_t size) {
public:
explicit StagingBufferCache(bool can_flush_aot) : can_flush_aot{can_flush_aot} {}
virtual ~StagingBufferCache() = default;
[[nodiscard]] StagingBufferType& GetWriteBuffer(std::size_t size) {
return GetBuffer(size, false);
}
[[nodiscard]] StagingBufferType& GetReadBuffer(std::size_t size) {
return GetBuffer(size, true);
}
[[nodiscard]] bool CanFlushAheadOfTime() const {
return can_flush_aot;
}
protected:
virtual std::unique_ptr<StagingBufferType> CreateBuffer(std::size_t size, bool is_flush) = 0;
private:
StagingBufferType& GetBuffer(std::size_t size, bool is_flush) {
const u32 ceil = Common::Log2Ceil64(size);
auto& buffers = cache[ceil];
auto& buffers = (is_flush ? flush_cache : upload_cache)[ceil];
const auto it = std::find_if(buffers.begin(), buffers.end(),
[](auto& buffer) { return buffer->IsAvailable(); });
if (it != buffers.end()) {
return **it;
}
const std::size_t buf_size = 1ULL << ceil;
return *buffers.emplace_back(std::make_unique<StagingBufferType>(buf_size));
return *buffers.emplace_back(CreateBuffer(1ULL << ceil, is_flush));
}
private:
std::unordered_map<u32, std::vector<std::unique_ptr<StagingBufferType>>> cache;
bool can_flush_aot{};
Cache upload_cache;
Cache flush_cache;
};
} // namespace VideoCommon

View File

@@ -254,8 +254,10 @@ public:
}
protected:
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: system{system}, rasterizer{rasterizer} {
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
std::unique_ptr<StagingBufferCache<StagingBufferType>> staging_buffer_cache)
: system{system}, rasterizer{rasterizer}, staging_buffer_cache{
std::move(staging_buffer_cache)} {
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
SetEmptyColorBuffer(i);
}
@@ -705,8 +707,12 @@ private:
}
void LoadSurface(const TSurface& surface) {
auto& buffer = staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
surface->LoadBuffer(system.GPU().MemoryManager(), buffer.GetPointer());
const auto host_size = surface->GetHostSizeInBytes();
auto& buffer = staging_buffer_cache->GetWriteBuffer(host_size);
surface->LoadBuffer(system.GPU().MemoryManager(), buffer.Map(host_size));
buffer.Unmap(host_size);
surface->UploadTexture(buffer);
surface->MarkAsModified(false, Tick());
}
@@ -716,15 +722,17 @@ private:
return;
}
const auto host_size = surface->GetHostSizeInBytes();
auto buffer = surface->GetFlushBuffer();
if (!buffer) {
buffer = &staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
buffer = &staging_buffer_cache->GetReadBuffer(host_size);
surface->DownloadTexture(*buffer);
}
buffer->WaitFence();
surface->SetFlushBuffer(nullptr);
surface->FlushBuffer(system.GPU().MemoryManager(), buffer->GetPointer());
surface->FlushBuffer(system.GPU().MemoryManager(), buffer->Map(host_size));
buffer->Unmap(host_size);
surface->MarkAsModified(false, Tick());
}
@@ -793,10 +801,11 @@ private:
}
void FlushAoT(TSurface& surface) {
if (!surface || !surface->IsLinear() || surface->GetFlushBuffer()) {
if (staging_buffer_cache->CanFlushAheadOfTime() || !surface || !surface->IsLinear() ||
surface->GetFlushBuffer()) {
return;
}
auto& buffer = staging_buffer_cache.GetBuffer(surface->GetHostSizeInBytes());
auto& buffer = staging_buffer_cache->GetReadBuffer(surface->GetHostSizeInBytes());
surface->DownloadTexture(buffer);
surface->SetFlushBuffer(&buffer);
}
@@ -847,7 +856,7 @@ private:
std::vector<TSurface> sampled_textures;
StagingBufferCache<StagingBufferType> staging_buffer_cache;
std::unique_ptr<StagingBufferCache<StagingBufferType>> staging_buffer_cache;
std::recursive_mutex mutex;
};