staging_buffer_cache: Disable OpenGL staging buffers for Intel proprietary drivers
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
148
src/video_core/renderer_opengl/gl_staging_buffer.cpp
Normal file
148
src/video_core/renderer_opengl/gl_staging_buffer.cpp
Normal 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
|
||||
105
src/video_core/renderer_opengl/gl_staging_buffer.h
Normal file
105
src/video_core/renderer_opengl/gl_staging_buffer.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user