diff options
| author | 2019-06-20 03:22:25 -0300 | |
|---|---|---|
| committer | 2019-07-06 00:37:55 -0300 | |
| commit | 1fa21fa1927feecc63f0d81824ce4ea203f79fcc (patch) | |
| tree | bff3e1dceea5a44d1698d7c88d6c61e3c9e06fee | |
| parent | buffer_cache: Implement a generic buffer cache (diff) | |
| download | yuzu-1fa21fa1927feecc63f0d81824ce4ea203f79fcc.tar.gz yuzu-1fa21fa1927feecc63f0d81824ce4ea203f79fcc.tar.xz yuzu-1fa21fa1927feecc63f0d81824ce4ea203f79fcc.zip | |
gl_buffer_cache: Implement with generic buffer cache
| -rw-r--r-- | src/video_core/rasterizer_interface.h | 3 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_buffer_cache.cpp | 189 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_buffer_cache.h | 115 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 40 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 1 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/utils.cpp | 17 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/utils.h | 14 |
8 files changed, 92 insertions, 291 deletions
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5ee4f8e8e..2b7367568 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -47,6 +47,9 @@ public: | |||
| 47 | /// and invalidated | 47 | /// and invalidated |
| 48 | virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0; | 48 | virtual void FlushAndInvalidateRegion(CacheAddr addr, u64 size) = 0; |
| 49 | 49 | ||
| 50 | /// Notify rasterizer that a frame is about to finish | ||
| 51 | virtual void TickFrame() = 0; | ||
| 52 | |||
| 50 | /// Attempt to use a faster method to perform a surface copy | 53 | /// Attempt to use a faster method to perform a surface copy |
| 51 | virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 54 | virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
| 52 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, | 55 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index fb3aedd07..2a9b523f5 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -2,192 +2,57 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | 5 | #include <memory> |
| 7 | #include <utility> | ||
| 8 | 6 | ||
| 9 | #include "common/alignment.h" | 7 | #include <glad/glad.h> |
| 8 | |||
| 10 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 10 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 14 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 11 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 15 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 16 | 13 | ||
| 17 | namespace OpenGL { | 14 | namespace OpenGL { |
| 18 | 15 | ||
| 19 | namespace { | 16 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, |
| 17 | std::size_t stream_size) | ||
| 18 | : VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer>{ | ||
| 19 | rasterizer, system, std::make_unique<OGLStreamBuffer>(stream_size, true)} {} | ||
| 20 | 20 | ||
| 21 | constexpr GLuint EmptyBuffer = 0; | 21 | OGLBufferCache::~OGLBufferCache() = default; |
| 22 | constexpr GLintptr CachedBufferOffset = 0; | ||
| 23 | 22 | ||
| 24 | OGLBuffer CreateBuffer(std::size_t size, GLenum usage) { | 23 | OGLBuffer OGLBufferCache::CreateBuffer(std::size_t size) { |
| 25 | OGLBuffer buffer; | 24 | OGLBuffer buffer; |
| 26 | buffer.Create(); | 25 | buffer.Create(); |
| 27 | glNamedBufferData(buffer.handle, size, nullptr, usage); | 26 | glNamedBufferData(buffer.handle, static_cast<GLsizeiptr>(size), nullptr, GL_DYNAMIC_DRAW); |
| 28 | return buffer; | 27 | return buffer; |
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | } // Anonymous namespace | 30 | const GLuint* OGLBufferCache::ToHandle(const OGLBuffer& buffer) { |
| 32 | 31 | return &buffer.handle; | |
| 33 | CachedBufferEntry::CachedBufferEntry(VAddr cpu_addr, u8* host_ptr) | ||
| 34 | : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr} {} | ||
| 35 | |||
| 36 | OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, std::size_t size) | ||
| 37 | : RasterizerCache{rasterizer}, system{system}, stream_buffer(size, true) {} | ||
| 38 | |||
| 39 | OGLBufferCache::~OGLBufferCache() = default; | ||
| 40 | |||
| 41 | void OGLBufferCache::Unregister(const std::shared_ptr<CachedBufferEntry>& entry) { | ||
| 42 | std::lock_guard lock{mutex}; | ||
| 43 | |||
| 44 | if (entry->IsInternalized()) { | ||
| 45 | internalized_entries.erase(entry->GetCacheAddr()); | ||
| 46 | } | ||
| 47 | ReserveBuffer(entry); | ||
| 48 | RasterizerCache<std::shared_ptr<CachedBufferEntry>>::Unregister(entry); | ||
| 49 | } | ||
| 50 | |||
| 51 | OGLBufferCache::BufferInfo OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, | ||
| 52 | std::size_t alignment, bool internalize, | ||
| 53 | bool is_written) { | ||
| 54 | std::lock_guard lock{mutex}; | ||
| 55 | |||
| 56 | auto& memory_manager = system.GPU().MemoryManager(); | ||
| 57 | const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; | ||
| 58 | const auto cache_addr{ToCacheAddr(host_ptr)}; | ||
| 59 | if (!host_ptr) { | ||
| 60 | return {EmptyBuffer, 0}; | ||
| 61 | } | ||
| 62 | |||
| 63 | // Cache management is a big overhead, so only cache entries with a given size. | ||
| 64 | // TODO: Figure out which size is the best for given games. | ||
| 65 | if (!internalize && size < 0x800 && | ||
| 66 | internalized_entries.find(cache_addr) == internalized_entries.end()) { | ||
| 67 | return StreamBufferUpload(host_ptr, size, alignment); | ||
| 68 | } | ||
| 69 | |||
| 70 | auto entry = TryGet(host_ptr); | ||
| 71 | if (!entry) { | ||
| 72 | return FixedBufferUpload(gpu_addr, host_ptr, size, internalize, is_written); | ||
| 73 | } | ||
| 74 | |||
| 75 | if (entry->GetSize() < size) { | ||
| 76 | GrowBuffer(entry, size); | ||
| 77 | } | ||
| 78 | if (is_written) { | ||
| 79 | entry->MarkAsModified(true, *this); | ||
| 80 | } | ||
| 81 | return {entry->GetBuffer(), CachedBufferOffset}; | ||
| 82 | } | ||
| 83 | |||
| 84 | OGLBufferCache::BufferInfo OGLBufferCache::UploadHostMemory(const void* raw_pointer, | ||
| 85 | std::size_t size, | ||
| 86 | std::size_t alignment) { | ||
| 87 | std::lock_guard lock{mutex}; | ||
| 88 | return StreamBufferUpload(raw_pointer, size, alignment); | ||
| 89 | } | ||
| 90 | |||
| 91 | bool OGLBufferCache::Map(std::size_t max_size) { | ||
| 92 | const auto max_size_ = static_cast<GLsizeiptr>(max_size); | ||
| 93 | bool invalidate; | ||
| 94 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = stream_buffer.Map(max_size_, 4); | ||
| 95 | buffer_offset = buffer_offset_base; | ||
| 96 | return invalidate; | ||
| 97 | } | ||
| 98 | |||
| 99 | void OGLBufferCache::Unmap() { | ||
| 100 | stream_buffer.Unmap(buffer_offset - buffer_offset_base); | ||
| 101 | } | ||
| 102 | |||
| 103 | void OGLBufferCache::FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& entry) { | ||
| 104 | glGetNamedBufferSubData(entry->GetBuffer(), 0, entry->GetSize(), entry->GetWritableHostPtr()); | ||
| 105 | } | ||
| 106 | |||
| 107 | OGLBufferCache::BufferInfo OGLBufferCache::StreamBufferUpload(const void* raw_pointer, | ||
| 108 | std::size_t size, | ||
| 109 | std::size_t alignment) { | ||
| 110 | AlignBuffer(alignment); | ||
| 111 | const GLintptr uploaded_offset = buffer_offset; | ||
| 112 | std::memcpy(buffer_ptr, raw_pointer, size); | ||
| 113 | |||
| 114 | buffer_ptr += size; | ||
| 115 | buffer_offset += size; | ||
| 116 | return {stream_buffer.GetHandle(), uploaded_offset}; | ||
| 117 | } | ||
| 118 | |||
| 119 | OGLBufferCache::BufferInfo OGLBufferCache::FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, | ||
| 120 | std::size_t size, bool internalize, | ||
| 121 | bool is_written) { | ||
| 122 | auto& memory_manager = system.GPU().MemoryManager(); | ||
| 123 | const auto cpu_addr = *memory_manager.GpuToCpuAddress(gpu_addr); | ||
| 124 | auto entry = GetUncachedBuffer(cpu_addr, host_ptr); | ||
| 125 | entry->SetSize(size); | ||
| 126 | entry->SetInternalState(internalize); | ||
| 127 | Register(entry); | ||
| 128 | |||
| 129 | if (internalize) { | ||
| 130 | internalized_entries.emplace(ToCacheAddr(host_ptr)); | ||
| 131 | } | ||
| 132 | if (is_written) { | ||
| 133 | entry->MarkAsModified(true, *this); | ||
| 134 | } | ||
| 135 | |||
| 136 | if (entry->GetCapacity() < size) { | ||
| 137 | entry->SetCapacity(CreateBuffer(size, GL_STATIC_DRAW), size); | ||
| 138 | } | ||
| 139 | glNamedBufferSubData(entry->GetBuffer(), 0, static_cast<GLintptr>(size), host_ptr); | ||
| 140 | return {entry->GetBuffer(), CachedBufferOffset}; | ||
| 141 | } | ||
| 142 | |||
| 143 | void OGLBufferCache::GrowBuffer(std::shared_ptr<CachedBufferEntry>& entry, std::size_t new_size) { | ||
| 144 | const auto old_size = static_cast<GLintptr>(entry->GetSize()); | ||
| 145 | if (entry->GetCapacity() < new_size) { | ||
| 146 | const auto old_buffer = entry->GetBuffer(); | ||
| 147 | OGLBuffer new_buffer = CreateBuffer(new_size, GL_STATIC_COPY); | ||
| 148 | |||
| 149 | // Copy bits from the old buffer to the new buffer. | ||
| 150 | glCopyNamedBufferSubData(old_buffer, new_buffer.handle, 0, 0, old_size); | ||
| 151 | entry->SetCapacity(std::move(new_buffer), new_size); | ||
| 152 | } | ||
| 153 | // Upload the new bits. | ||
| 154 | const auto size_diff = static_cast<GLintptr>(new_size - old_size); | ||
| 155 | glNamedBufferSubData(entry->GetBuffer(), old_size, size_diff, entry->GetHostPtr() + old_size); | ||
| 156 | |||
| 157 | // Update entry's size in the object and in the cache. | ||
| 158 | entry->SetSize(new_size); | ||
| 159 | Unregister(entry); | ||
| 160 | Register(entry); | ||
| 161 | } | 32 | } |
| 162 | 33 | ||
| 163 | std::shared_ptr<CachedBufferEntry> OGLBufferCache::GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr) { | 34 | const GLuint* OGLBufferCache::GetEmptyBuffer(std::size_t) { |
| 164 | if (auto entry = TryGetReservedBuffer(host_ptr)) { | 35 | static const GLuint null_buffer = 0; |
| 165 | return entry; | 36 | return &null_buffer; |
| 166 | } | ||
| 167 | return std::make_shared<CachedBufferEntry>(cpu_addr, host_ptr); | ||
| 168 | } | 37 | } |
| 169 | 38 | ||
| 170 | std::shared_ptr<CachedBufferEntry> OGLBufferCache::TryGetReservedBuffer(u8* host_ptr) { | 39 | void OGLBufferCache::UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, |
| 171 | const auto it = buffer_reserve.find(ToCacheAddr(host_ptr)); | 40 | const u8* data) { |
| 172 | if (it == buffer_reserve.end()) { | 41 | glNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), |
| 173 | return {}; | 42 | static_cast<GLsizeiptr>(size), data); |
| 174 | } | ||
| 175 | auto& reserve = it->second; | ||
| 176 | auto entry = reserve.back(); | ||
| 177 | reserve.pop_back(); | ||
| 178 | return entry; | ||
| 179 | } | 43 | } |
| 180 | 44 | ||
| 181 | void OGLBufferCache::ReserveBuffer(std::shared_ptr<CachedBufferEntry> entry) { | 45 | void OGLBufferCache::DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, |
| 182 | buffer_reserve[entry->GetCacheAddr()].push_back(std::move(entry)); | 46 | std::size_t size, u8* data) { |
| 47 | glGetNamedBufferSubData(buffer.handle, static_cast<GLintptr>(offset), | ||
| 48 | static_cast<GLsizeiptr>(size), data); | ||
| 183 | } | 49 | } |
| 184 | 50 | ||
| 185 | void OGLBufferCache::AlignBuffer(std::size_t alignment) { | 51 | void OGLBufferCache::CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, |
| 186 | // Align the offset, not the mapped pointer | 52 | std::size_t src_offset, std::size_t dst_offset, |
| 187 | const GLintptr offset_aligned = | 53 | std::size_t size) { |
| 188 | static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); | 54 | glCopyNamedBufferSubData(src.handle, dst.handle, static_cast<GLintptr>(src_offset), |
| 189 | buffer_ptr += offset_aligned - buffer_offset; | 55 | static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size)); |
| 190 | buffer_offset = offset_aligned; | ||
| 191 | } | 56 | } |
| 192 | 57 | ||
| 193 | } // namespace OpenGL | 58 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 19d643e41..3befdc6ab 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -4,15 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | 7 | #include <memory> |
| 10 | #include <tuple> | ||
| 11 | #include <unordered_set> | ||
| 12 | #include <utility> | ||
| 13 | #include <vector> | ||
| 14 | 8 | ||
| 15 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/buffer_cache.h" | ||
| 16 | #include "video_core/rasterizer_cache.h" | 11 | #include "video_core/rasterizer_cache.h" |
| 17 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 18 | #include "video_core/renderer_opengl/gl_stream_buffer.h" | 13 | #include "video_core/renderer_opengl/gl_stream_buffer.h" |
| @@ -23,112 +18,30 @@ class System; | |||
| 23 | 18 | ||
| 24 | namespace OpenGL { | 19 | namespace OpenGL { |
| 25 | 20 | ||
| 21 | class OGLStreamBuffer; | ||
| 26 | class RasterizerOpenGL; | 22 | class RasterizerOpenGL; |
| 27 | 23 | ||
| 28 | class CachedBufferEntry final : public RasterizerCacheObject { | 24 | class OGLBufferCache final : public VideoCommon::BufferCache<OGLBuffer, GLuint, OGLStreamBuffer> { |
| 29 | public: | 25 | public: |
| 30 | explicit CachedBufferEntry(VAddr cpu_addr, u8* host_ptr); | 26 | explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, |
| 31 | 27 | std::size_t stream_size); | |
| 32 | VAddr GetCpuAddr() const override { | ||
| 33 | return cpu_addr; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::size_t GetSizeInBytes() const override { | ||
| 37 | return size; | ||
| 38 | } | ||
| 39 | |||
| 40 | u8* GetWritableHostPtr() const { | ||
| 41 | return host_ptr; | ||
| 42 | } | ||
| 43 | |||
| 44 | std::size_t GetSize() const { | ||
| 45 | return size; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t GetCapacity() const { | ||
| 49 | return capacity; | ||
| 50 | } | ||
| 51 | |||
| 52 | bool IsInternalized() const { | ||
| 53 | return is_internal; | ||
| 54 | } | ||
| 55 | |||
| 56 | GLuint GetBuffer() const { | ||
| 57 | return buffer.handle; | ||
| 58 | } | ||
| 59 | |||
| 60 | void SetSize(std::size_t new_size) { | ||
| 61 | size = new_size; | ||
| 62 | } | ||
| 63 | |||
| 64 | void SetInternalState(bool is_internal_) { | ||
| 65 | is_internal = is_internal_; | ||
| 66 | } | ||
| 67 | |||
| 68 | void SetCapacity(OGLBuffer&& new_buffer, std::size_t new_capacity) { | ||
| 69 | capacity = new_capacity; | ||
| 70 | buffer = std::move(new_buffer); | ||
| 71 | } | ||
| 72 | |||
| 73 | private: | ||
| 74 | u8* host_ptr{}; | ||
| 75 | VAddr cpu_addr{}; | ||
| 76 | std::size_t size{}; | ||
| 77 | std::size_t capacity{}; | ||
| 78 | bool is_internal{}; | ||
| 79 | OGLBuffer buffer; | ||
| 80 | }; | ||
| 81 | |||
| 82 | class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { | ||
| 83 | using BufferInfo = std::pair<GLuint, GLintptr>; | ||
| 84 | |||
| 85 | public: | ||
| 86 | explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system, std::size_t size); | ||
| 87 | ~OGLBufferCache(); | 28 | ~OGLBufferCache(); |
| 88 | 29 | ||
| 89 | void Unregister(const std::shared_ptr<CachedBufferEntry>& entry) override; | ||
| 90 | |||
| 91 | /// Uploads data from a guest GPU address. Returns the OpenGL buffer where it's located and its | ||
| 92 | /// offset. | ||
| 93 | BufferInfo UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | ||
| 94 | bool internalize = false, bool is_written = false); | ||
| 95 | |||
| 96 | /// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset. | ||
| 97 | BufferInfo UploadHostMemory(const void* raw_pointer, std::size_t size, | ||
| 98 | std::size_t alignment = 4); | ||
| 99 | |||
| 100 | bool Map(std::size_t max_size); | ||
| 101 | void Unmap(); | ||
| 102 | |||
| 103 | protected: | 30 | protected: |
| 104 | // We do not have to flush this cache as things in it are never modified by us. | 31 | OGLBuffer CreateBuffer(std::size_t size) override; |
| 105 | void FlushObjectInner(const std::shared_ptr<CachedBufferEntry>& entry) override; | ||
| 106 | |||
| 107 | private: | ||
| 108 | BufferInfo StreamBufferUpload(const void* raw_pointer, std::size_t size, std::size_t alignment); | ||
| 109 | |||
| 110 | BufferInfo FixedBufferUpload(GPUVAddr gpu_addr, u8* host_ptr, std::size_t size, | ||
| 111 | bool internalize, bool is_written); | ||
| 112 | |||
| 113 | void GrowBuffer(std::shared_ptr<CachedBufferEntry>& entry, std::size_t new_size); | ||
| 114 | |||
| 115 | std::shared_ptr<CachedBufferEntry> GetUncachedBuffer(VAddr cpu_addr, u8* host_ptr); | ||
| 116 | |||
| 117 | std::shared_ptr<CachedBufferEntry> TryGetReservedBuffer(u8* host_ptr); | ||
| 118 | 32 | ||
| 119 | void ReserveBuffer(std::shared_ptr<CachedBufferEntry> entry); | 33 | const GLuint* ToHandle(const OGLBuffer& buffer) override; |
| 120 | 34 | ||
| 121 | void AlignBuffer(std::size_t alignment); | 35 | const GLuint* GetEmptyBuffer(std::size_t) override; |
| 122 | 36 | ||
| 123 | Core::System& system; | 37 | void UploadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, |
| 38 | const u8* data) override; | ||
| 124 | 39 | ||
| 125 | u8* buffer_ptr = nullptr; | 40 | void DownloadBufferData(const OGLBuffer& buffer, std::size_t offset, std::size_t size, |
| 126 | GLintptr buffer_offset = 0; | 41 | u8* data) override; |
| 127 | GLintptr buffer_offset_base = 0; | ||
| 128 | 42 | ||
| 129 | OGLStreamBuffer stream_buffer; | 43 | void CopyBufferData(const OGLBuffer& src, const OGLBuffer& dst, std::size_t src_offset, |
| 130 | std::unordered_set<CacheAddr> internalized_entries; | 44 | std::size_t dst_offset, std::size_t size) override; |
| 131 | std::unordered_map<CacheAddr, std::vector<std::shared_ptr<CachedBufferEntry>>> buffer_reserve; | ||
| 132 | }; | 45 | }; |
| 133 | 46 | ||
| 134 | } // namespace OpenGL | 47 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 35ba84235..b57d60856 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -198,7 +198,8 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) { | |||
| 198 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); | 198 | const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size); |
| 199 | 199 | ||
| 200 | // Bind the vertex array to the buffer at the current offset. | 200 | // Bind the vertex array to the buffer at the current offset. |
| 201 | glVertexArrayVertexBuffer(vao, index, vertex_buffer, vertex_buffer_offset, | 201 | // FIXME(Rodrigo): This dereferenced pointer might be invalidated in future uploads. |
| 202 | glVertexArrayVertexBuffer(vao, index, *vertex_buffer, vertex_buffer_offset, | ||
| 202 | vertex_array.stride); | 203 | vertex_array.stride); |
| 203 | 204 | ||
| 204 | if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { | 205 | if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) { |
| @@ -221,7 +222,8 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer(GLuint vao) { | |||
| 221 | const auto& regs = system.GPU().Maxwell3D().regs; | 222 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 222 | const std::size_t size = CalculateIndexBufferSize(); | 223 | const std::size_t size = CalculateIndexBufferSize(); |
| 223 | const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); | 224 | const auto [buffer, offset] = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size); |
| 224 | glVertexArrayElementBuffer(vao, buffer); | 225 | // FIXME(Rodrigo): This dereferenced pointer might be invalidated in future uploads. |
| 226 | glVertexArrayElementBuffer(vao, *buffer); | ||
| 225 | return offset; | 227 | return offset; |
| 226 | } | 228 | } |
| 227 | 229 | ||
| @@ -255,10 +257,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 255 | BaseBindings base_bindings; | 257 | BaseBindings base_bindings; |
| 256 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | 258 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; |
| 257 | 259 | ||
| 258 | // Prepare packed bindings | ||
| 259 | bind_ubo_pushbuffer.Setup(base_bindings.cbuf); | ||
| 260 | bind_ssbo_pushbuffer.Setup(base_bindings.gmem); | ||
| 261 | |||
| 262 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 260 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 263 | const auto& shader_config = gpu.regs.shader_config[index]; | 261 | const auto& shader_config = gpu.regs.shader_config[index]; |
| 264 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; | 262 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; |
| @@ -328,9 +326,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 328 | base_bindings = next_bindings; | 326 | base_bindings = next_bindings; |
| 329 | } | 327 | } |
| 330 | 328 | ||
| 331 | bind_ubo_pushbuffer.Bind(); | ||
| 332 | bind_ssbo_pushbuffer.Bind(); | ||
| 333 | |||
| 334 | SyncClipEnabled(clip_distances); | 329 | SyncClipEnabled(clip_distances); |
| 335 | 330 | ||
| 336 | gpu.dirty_flags.shaders = false; | 331 | gpu.dirty_flags.shaders = false; |
| @@ -644,11 +639,8 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 644 | buffer_size += | 639 | buffer_size += |
| 645 | Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment()); | 640 | Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment()); |
| 646 | 641 | ||
| 647 | const bool invalidate = buffer_cache.Map(buffer_size); | 642 | // Prepare the vertex array. |
| 648 | if (invalidate) { | 643 | buffer_cache.Map(buffer_size); |
| 649 | // As all cached buffers are invalidated, we need to recheck their state. | ||
| 650 | gpu.dirty_flags.vertex_array.set(); | ||
| 651 | } | ||
| 652 | 644 | ||
| 653 | // Prepare vertex array format. | 645 | // Prepare vertex array format. |
| 654 | const GLuint vao = SetupVertexFormat(); | 646 | const GLuint vao = SetupVertexFormat(); |
| @@ -660,6 +652,10 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 660 | // Setup draw parameters. It will automatically choose what glDraw* method to use. | 652 | // Setup draw parameters. It will automatically choose what glDraw* method to use. |
| 661 | const DrawParameters params = SetupDraw(index_buffer_offset); | 653 | const DrawParameters params = SetupDraw(index_buffer_offset); |
| 662 | 654 | ||
| 655 | // Prepare packed bindings. | ||
| 656 | bind_ubo_pushbuffer.Setup(0); | ||
| 657 | bind_ssbo_pushbuffer.Setup(0); | ||
| 658 | |||
| 663 | // Setup shaders and their used resources. | 659 | // Setup shaders and their used resources. |
| 664 | texture_cache.GuardSamplers(true); | 660 | texture_cache.GuardSamplers(true); |
| 665 | SetupShaders(params.primitive_mode); | 661 | SetupShaders(params.primitive_mode); |
| @@ -667,7 +663,17 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 667 | 663 | ||
| 668 | ConfigureFramebuffers(state); | 664 | ConfigureFramebuffers(state); |
| 669 | 665 | ||
| 670 | buffer_cache.Unmap(); | 666 | // Signal the buffer cache that we are not going to upload more things. |
| 667 | const bool invalidate = buffer_cache.Unmap(); | ||
| 668 | |||
| 669 | // Now that we are no longer uploading data, we can safely bind the buffers to OpenGL. | ||
| 670 | bind_ubo_pushbuffer.Bind(); | ||
| 671 | bind_ssbo_pushbuffer.Bind(); | ||
| 672 | |||
| 673 | if (invalidate) { | ||
| 674 | // As all cached buffers are invalidated, we need to recheck their state. | ||
| 675 | gpu.dirty_flags.vertex_array.set(); | ||
| 676 | } | ||
| 671 | 677 | ||
| 672 | shader_program_manager->ApplyTo(state); | 678 | shader_program_manager->ApplyTo(state); |
| 673 | state.Apply(); | 679 | state.Apply(); |
| @@ -709,6 +715,10 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | |||
| 709 | InvalidateRegion(addr, size); | 715 | InvalidateRegion(addr, size); |
| 710 | } | 716 | } |
| 711 | 717 | ||
| 718 | void RasterizerOpenGL::TickFrame() { | ||
| 719 | buffer_cache.TickFrame(); | ||
| 720 | } | ||
| 721 | |||
| 712 | bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 722 | bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
| 713 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, | 723 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, |
| 714 | const Tegra::Engines::Fermi2D::Config& copy_config) { | 724 | const Tegra::Engines::Fermi2D::Config& copy_config) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bc988727b..7067ad5b4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -62,6 +62,7 @@ public: | |||
| 62 | void FlushRegion(CacheAddr addr, u64 size) override; | 62 | void FlushRegion(CacheAddr addr, u64 size) override; |
| 63 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 63 | void InvalidateRegion(CacheAddr addr, u64 size) override; |
| 64 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; | 64 | void FlushAndInvalidateRegion(CacheAddr addr, u64 size) override; |
| 65 | void TickFrame() override; | ||
| 65 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, | 66 | bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, |
| 66 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, | 67 | const Tegra::Engines::Fermi2D::Regs::Surface& dst, |
| 67 | const Tegra::Engines::Fermi2D::Config& copy_config) override; | 68 | const Tegra::Engines::Fermi2D::Config& copy_config) override; |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index b142521ec..9ecdddb0d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -101,7 +101,6 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::Syst | |||
| 101 | 101 | ||
| 102 | RendererOpenGL::~RendererOpenGL() = default; | 102 | RendererOpenGL::~RendererOpenGL() = default; |
| 103 | 103 | ||
| 104 | /// Swap buffers (render frame) | ||
| 105 | void RendererOpenGL::SwapBuffers( | 104 | void RendererOpenGL::SwapBuffers( |
| 106 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { | 105 | std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { |
| 107 | 106 | ||
| @@ -130,6 +129,8 @@ void RendererOpenGL::SwapBuffers( | |||
| 130 | 129 | ||
| 131 | DrawScreen(render_window.GetFramebufferLayout()); | 130 | DrawScreen(render_window.GetFramebufferLayout()); |
| 132 | 131 | ||
| 132 | rasterizer->TickFrame(); | ||
| 133 | |||
| 133 | render_window.SwapBuffers(); | 134 | render_window.SwapBuffers(); |
| 134 | } | 135 | } |
| 135 | 136 | ||
| @@ -262,7 +263,6 @@ void RendererOpenGL::CreateRasterizer() { | |||
| 262 | if (rasterizer) { | 263 | if (rasterizer) { |
| 263 | return; | 264 | return; |
| 264 | } | 265 | } |
| 265 | // Initialize sRGB Usage | ||
| 266 | OpenGLState::ClearsRGBUsed(); | 266 | OpenGLState::ClearsRGBUsed(); |
| 267 | rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); | 267 | rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info); |
| 268 | } | 268 | } |
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp index 68c36988d..22eefa1d7 100644 --- a/src/video_core/renderer_opengl/utils.cpp +++ b/src/video_core/renderer_opengl/utils.cpp | |||
| @@ -19,23 +19,30 @@ BindBuffersRangePushBuffer::~BindBuffersRangePushBuffer() = default; | |||
| 19 | 19 | ||
| 20 | void BindBuffersRangePushBuffer::Setup(GLuint first_) { | 20 | void BindBuffersRangePushBuffer::Setup(GLuint first_) { |
| 21 | first = first_; | 21 | first = first_; |
| 22 | buffers.clear(); | 22 | buffer_pointers.clear(); |
| 23 | offsets.clear(); | 23 | offsets.clear(); |
| 24 | sizes.clear(); | 24 | sizes.clear(); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | void BindBuffersRangePushBuffer::Push(GLuint buffer, GLintptr offset, GLsizeiptr size) { | 27 | void BindBuffersRangePushBuffer::Push(const GLuint* buffer, GLintptr offset, GLsizeiptr size) { |
| 28 | buffers.push_back(buffer); | 28 | buffer_pointers.push_back(buffer); |
| 29 | offsets.push_back(offset); | 29 | offsets.push_back(offset); |
| 30 | sizes.push_back(size); | 30 | sizes.push_back(size); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | void BindBuffersRangePushBuffer::Bind() const { | 33 | void BindBuffersRangePushBuffer::Bind() { |
| 34 | const std::size_t count{buffers.size()}; | 34 | // Ensure sizes are valid. |
| 35 | const std::size_t count{buffer_pointers.size()}; | ||
| 35 | DEBUG_ASSERT(count == offsets.size() && count == sizes.size()); | 36 | DEBUG_ASSERT(count == offsets.size() && count == sizes.size()); |
| 36 | if (count == 0) { | 37 | if (count == 0) { |
| 37 | return; | 38 | return; |
| 38 | } | 39 | } |
| 40 | |||
| 41 | // Dereference buffers. | ||
| 42 | buffers.resize(count); | ||
| 43 | std::transform(buffer_pointers.begin(), buffer_pointers.end(), buffers.begin(), | ||
| 44 | [](const GLuint* pointer) { return *pointer; }); | ||
| 45 | |||
| 39 | glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(), | 46 | glBindBuffersRange(target, first, static_cast<GLsizei>(count), buffers.data(), offsets.data(), |
| 40 | sizes.data()); | 47 | sizes.data()); |
| 41 | } | 48 | } |
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h index 4a752f3b4..d2a3d25d9 100644 --- a/src/video_core/renderer_opengl/utils.h +++ b/src/video_core/renderer_opengl/utils.h | |||
| @@ -11,20 +11,22 @@ | |||
| 11 | 11 | ||
| 12 | namespace OpenGL { | 12 | namespace OpenGL { |
| 13 | 13 | ||
| 14 | class BindBuffersRangePushBuffer { | 14 | class BindBuffersRangePushBuffer final { |
| 15 | public: | 15 | public: |
| 16 | BindBuffersRangePushBuffer(GLenum target); | 16 | explicit BindBuffersRangePushBuffer(GLenum target); |
| 17 | ~BindBuffersRangePushBuffer(); | 17 | ~BindBuffersRangePushBuffer(); |
| 18 | 18 | ||
| 19 | void Setup(GLuint first_); | 19 | void Setup(GLuint first_); |
| 20 | 20 | ||
| 21 | void Push(GLuint buffer, GLintptr offset, GLsizeiptr size); | 21 | void Push(const GLuint* buffer, GLintptr offset, GLsizeiptr size); |
| 22 | 22 | ||
| 23 | void Bind() const; | 23 | void Bind(); |
| 24 | 24 | ||
| 25 | private: | 25 | private: |
| 26 | GLenum target; | 26 | GLenum target{}; |
| 27 | GLuint first; | 27 | GLuint first{}; |
| 28 | std::vector<const GLuint*> buffer_pointers; | ||
| 29 | |||
| 28 | std::vector<GLuint> buffers; | 30 | std::vector<GLuint> buffers; |
| 29 | std::vector<GLintptr> offsets; | 31 | std::vector<GLintptr> offsets; |
| 30 | std::vector<GLsizeiptr> sizes; | 32 | std::vector<GLsizeiptr> sizes; |