Browse Source
Merge pull request #254 from bunnei/port-citra-renderer
Merge pull request #254 from bunnei/port-citra-renderer
Port Citra OpenGL rasterizer codepull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 24260 additions and 1265 deletions
-
2externals/glad/Readme.md
-
6externals/glad/include/KHR/khrplatform.h
-
13761externals/glad/include/glad/glad.h
-
8750externals/glad/src/glad.c
-
11src/video_core/CMakeLists.txt
-
61src/video_core/rasterizer_interface.h
-
269src/video_core/renderer_opengl/gl_rasterizer.cpp
-
162src/video_core/renderer_opengl/gl_rasterizer.h
-
1361src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
-
350src/video_core/renderer_opengl/gl_rasterizer_cache.h
-
85src/video_core/renderer_opengl/gl_resource_manager.h
-
58src/video_core/renderer_opengl/gl_shader_decompiler.cpp
-
27src/video_core/renderer_opengl/gl_shader_decompiler.h
-
20src/video_core/renderer_opengl/gl_shader_gen.cpp
-
66src/video_core/renderer_opengl/gl_shader_gen.h
-
154src/video_core/renderer_opengl/gl_shader_util.cpp
-
6src/video_core/renderer_opengl/gl_shader_util.h
-
125src/video_core/renderer_opengl/gl_state.cpp
-
33src/video_core/renderer_opengl/gl_state.h
-
182src/video_core/renderer_opengl/gl_stream_buffer.cpp
-
34src/video_core/renderer_opengl/gl_stream_buffer.h
-
2src/video_core/renderer_opengl/renderer_opengl.cpp
@ -1,5 +1,5 @@ |
|||
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command: |
|||
|
|||
``` |
|||
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0 |
|||
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c |
|||
``` |
|||
13761
externals/glad/include/glad/glad.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
8750
externals/glad/src/glad.c
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,61 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include "common/common_types.h" |
|||
|
|||
struct ScreenInfo; |
|||
|
|||
namespace VideoCore { |
|||
|
|||
class RasterizerInterface { |
|||
public: |
|||
virtual ~RasterizerInterface() {} |
|||
|
|||
/// Draw the current batch of triangles |
|||
virtual void DrawTriangles() = 0; |
|||
|
|||
/// Notify rasterizer that the specified Maxwell register has been changed |
|||
virtual void NotifyMaxwellRegisterChanged(u32 id) = 0; |
|||
|
|||
/// Notify rasterizer that all caches should be flushed to 3DS memory |
|||
virtual void FlushAll() = 0; |
|||
|
|||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory |
|||
virtual void FlushRegion(PAddr addr, u32 size) = 0; |
|||
|
|||
/// Notify rasterizer that any caches of the specified region should be invalidated |
|||
virtual void InvalidateRegion(PAddr addr, u32 size) = 0; |
|||
|
|||
/// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory |
|||
/// and invalidated |
|||
virtual void FlushAndInvalidateRegion(PAddr addr, u32 size) = 0; |
|||
|
|||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0 |
|||
virtual bool AccelerateDisplayTransfer(const void* config) { |
|||
return false; |
|||
} |
|||
|
|||
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1 |
|||
virtual bool AccelerateTextureCopy(const void* config) { |
|||
return false; |
|||
} |
|||
|
|||
/// Attempt to use a faster method to fill a region |
|||
virtual bool AccelerateFill(const void* config) { |
|||
return false; |
|||
} |
|||
|
|||
/// Attempt to use a faster method to display the framebuffer to screen |
|||
virtual bool AccelerateDisplay(const void* config, PAddr framebuffer_addr, u32 pixel_stride, |
|||
ScreenInfo& screen_info) { |
|||
return false; |
|||
} |
|||
|
|||
virtual bool AccelerateDrawBatch(bool is_indexed) { |
|||
return false; |
|||
} |
|||
}; |
|||
} // namespace VideoCore |
|||
@ -0,0 +1,269 @@ |
|||
// Copyright 2015 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <memory>
|
|||
#include <string>
|
|||
#include <tuple>
|
|||
#include <utility>
|
|||
#include <glad/glad.h>
|
|||
#include "common/alignment.h"
|
|||
#include "common/assert.h"
|
|||
#include "common/logging/log.h"
|
|||
#include "common/math_util.h"
|
|||
#include "common/microprofile.h"
|
|||
#include "common/scope_exit.h"
|
|||
#include "common/vector_math.h"
|
|||
#include "core/settings.h"
|
|||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
|||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
|||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
|||
|
|||
using PixelFormat = SurfaceParams::PixelFormat; |
|||
using SurfaceType = SurfaceParams::SurfaceType; |
|||
|
|||
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); |
|||
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192)); |
|||
MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192)); |
|||
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); |
|||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); |
|||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); |
|||
|
|||
enum class UniformBindings : GLuint { Common, VS, FS }; |
|||
|
|||
static void SetShaderUniformBlockBinding(GLuint shader, const char* name, UniformBindings binding, |
|||
size_t expected_size) { |
|||
GLuint ub_index = glGetUniformBlockIndex(shader, name); |
|||
if (ub_index != GL_INVALID_INDEX) { |
|||
GLint ub_size = 0; |
|||
glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); |
|||
ASSERT_MSG(ub_size == expected_size, |
|||
"Uniform block size did not match! Got %d, expected %zu", |
|||
static_cast<int>(ub_size), expected_size); |
|||
glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); |
|||
} |
|||
} |
|||
|
|||
static void SetShaderUniformBlockBindings(GLuint shader) { |
|||
SetShaderUniformBlockBinding(shader, "shader_data", UniformBindings::Common, |
|||
sizeof(RasterizerOpenGL::UniformData)); |
|||
SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, |
|||
sizeof(RasterizerOpenGL::VSUniformData)); |
|||
SetShaderUniformBlockBinding(shader, "fs_config", UniformBindings::FS, |
|||
sizeof(RasterizerOpenGL::FSUniformData)); |
|||
} |
|||
|
|||
RasterizerOpenGL::RasterizerOpenGL() { |
|||
has_ARB_buffer_storage = false; |
|||
has_ARB_direct_state_access = false; |
|||
has_ARB_separate_shader_objects = false; |
|||
has_ARB_vertex_attrib_binding = false; |
|||
|
|||
GLint ext_num; |
|||
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); |
|||
for (GLint i = 0; i < ext_num; i++) { |
|||
std::string extension{reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))}; |
|||
|
|||
if (extension == "GL_ARB_buffer_storage") { |
|||
has_ARB_buffer_storage = true; |
|||
} else if (extension == "GL_ARB_direct_state_access") { |
|||
has_ARB_direct_state_access = true; |
|||
} else if (extension == "GL_ARB_separate_shader_objects") { |
|||
has_ARB_separate_shader_objects = true; |
|||
} else if (extension == "GL_ARB_vertex_attrib_binding") { |
|||
has_ARB_vertex_attrib_binding = true; |
|||
} |
|||
} |
|||
|
|||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
|||
state.clip_distance[0] = true; |
|||
|
|||
// Generate VBO, VAO and UBO
|
|||
vertex_buffer = OGLStreamBuffer::MakeBuffer(GLAD_GL_ARB_buffer_storage, GL_ARRAY_BUFFER); |
|||
vertex_buffer->Create(VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE / 2); |
|||
sw_vao.Create(); |
|||
uniform_buffer.Create(); |
|||
|
|||
state.draw.vertex_array = sw_vao.handle; |
|||
state.draw.vertex_buffer = vertex_buffer->GetHandle(); |
|||
state.draw.uniform_buffer = uniform_buffer.handle; |
|||
state.Apply(); |
|||
|
|||
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), nullptr, GL_STATIC_DRAW); |
|||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle); |
|||
|
|||
uniform_block_data.dirty = true; |
|||
|
|||
// Create render framebuffer
|
|||
framebuffer.Create(); |
|||
|
|||
if (has_ARB_separate_shader_objects) { |
|||
hw_vao.Create(); |
|||
hw_vao_enabled_attributes.fill(false); |
|||
|
|||
stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); |
|||
stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); |
|||
state.draw.vertex_buffer = stream_buffer->GetHandle(); |
|||
|
|||
pipeline.Create(); |
|||
vs_input_index_min = 0; |
|||
vs_input_index_max = 0; |
|||
state.draw.program_pipeline = pipeline.handle; |
|||
state.draw.shader_program = 0; |
|||
state.draw.vertex_array = hw_vao.handle; |
|||
state.Apply(); |
|||
|
|||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); |
|||
|
|||
vs_uniform_buffer.Create(); |
|||
glBindBuffer(GL_UNIFORM_BUFFER, vs_uniform_buffer.handle); |
|||
glBufferData(GL_UNIFORM_BUFFER, sizeof(VSUniformData), nullptr, GL_STREAM_COPY); |
|||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, vs_uniform_buffer.handle); |
|||
} else { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
accelerate_draw = AccelDraw::Disabled; |
|||
|
|||
glEnable(GL_BLEND); |
|||
|
|||
// Sync fixed function OpenGL state
|
|||
SyncClipEnabled(); |
|||
SyncClipCoef(); |
|||
SyncCullMode(); |
|||
SyncBlendEnabled(); |
|||
SyncBlendFuncs(); |
|||
SyncBlendColor(); |
|||
} |
|||
|
|||
RasterizerOpenGL::~RasterizerOpenGL() { |
|||
if (stream_buffer != nullptr) { |
|||
state.draw.vertex_buffer = stream_buffer->GetHandle(); |
|||
state.Apply(); |
|||
stream_buffer->Release(); |
|||
} |
|||
} |
|||
|
|||
static constexpr std::array<GLenum, 4> vs_attrib_types{ |
|||
GL_BYTE, // VertexAttributeFormat::BYTE
|
|||
GL_UNSIGNED_BYTE, // VertexAttributeFormat::UBYTE
|
|||
GL_SHORT, // VertexAttributeFormat::SHORT
|
|||
GL_FLOAT // VertexAttributeFormat::FLOAT
|
|||
}; |
|||
|
|||
void RasterizerOpenGL::AnalyzeVertexArray(bool is_indexed) { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset) { |
|||
MICROPROFILE_SCOPE(OpenGL_VAO); |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset) { |
|||
MICROPROFILE_SCOPE(OpenGL_VS); |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset) { |
|||
MICROPROFILE_SCOPE(OpenGL_FS); |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { |
|||
if (!has_ARB_separate_shader_objects) { |
|||
UNIMPLEMENTED(); |
|||
return false; |
|||
} |
|||
|
|||
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; |
|||
DrawTriangles(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void RasterizerOpenGL::DrawTriangles() { |
|||
MICROPROFILE_SCOPE(OpenGL_Drawing); |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {} |
|||
|
|||
void RasterizerOpenGL::FlushAll() { |
|||
MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
|||
res_cache.FlushAll(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { |
|||
MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
|||
res_cache.FlushRegion(addr, size); |
|||
} |
|||
|
|||
void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) { |
|||
MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
|||
res_cache.InvalidateRegion(addr, size, nullptr); |
|||
} |
|||
|
|||
void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { |
|||
MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
|||
res_cache.FlushRegion(addr, size); |
|||
res_cache.InvalidateRegion(addr, size, nullptr); |
|||
} |
|||
|
|||
bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) { |
|||
MICROPROFILE_SCOPE(OpenGL_Blits); |
|||
UNIMPLEMENTED(); |
|||
return true; |
|||
} |
|||
|
|||
bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) { |
|||
UNIMPLEMENTED(); |
|||
return true; |
|||
} |
|||
|
|||
bool RasterizerOpenGL::AccelerateFill(const void* config) { |
|||
UNIMPLEMENTED(); |
|||
return true; |
|||
} |
|||
|
|||
bool RasterizerOpenGL::AccelerateDisplay(const void* config, PAddr framebuffer_addr, |
|||
u32 pixel_stride, ScreenInfo& screen_info) { |
|||
UNIMPLEMENTED(); |
|||
return true; |
|||
} |
|||
|
|||
void RasterizerOpenGL::SetShader() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncClipEnabled() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncClipCoef() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncCullMode() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncDepthScale() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncDepthOffset() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncBlendEnabled() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncBlendFuncs() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
|
|||
void RasterizerOpenGL::SyncBlendColor() { |
|||
UNIMPLEMENTED(); |
|||
} |
|||
@ -0,0 +1,162 @@ |
|||
// Copyright 2015 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <cstddef> |
|||
#include <cstring> |
|||
#include <memory> |
|||
#include <unordered_map> |
|||
#include <vector> |
|||
#include <glad/glad.h> |
|||
#include "common/bit_field.h" |
|||
#include "common/common_types.h" |
|||
#include "common/hash.h" |
|||
#include "common/vector_math.h" |
|||
#include "video_core/rasterizer_interface.h" |
|||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
|||
#include "video_core/renderer_opengl/gl_resource_manager.h" |
|||
#include "video_core/renderer_opengl/gl_shader_gen.h" |
|||
#include "video_core/renderer_opengl/gl_state.h" |
|||
#include "video_core/renderer_opengl/gl_stream_buffer.h" |
|||
|
|||
struct ScreenInfo; |
|||
|
|||
class RasterizerOpenGL : public VideoCore::RasterizerInterface { |
|||
public: |
|||
RasterizerOpenGL(); |
|||
~RasterizerOpenGL() override; |
|||
|
|||
void DrawTriangles() override; |
|||
void NotifyMaxwellRegisterChanged(u32 id) override; |
|||
void FlushAll() override; |
|||
void FlushRegion(PAddr addr, u32 size) override; |
|||
void InvalidateRegion(PAddr addr, u32 size) override; |
|||
void FlushAndInvalidateRegion(PAddr addr, u32 size) override; |
|||
bool AccelerateDisplayTransfer(const void* config) override; |
|||
bool AccelerateTextureCopy(const void* config) override; |
|||
bool AccelerateFill(const void* config) override; |
|||
bool AccelerateDisplay(const void* config, PAddr framebuffer_addr, u32 pixel_stride, |
|||
ScreenInfo& screen_info) override; |
|||
bool AccelerateDrawBatch(bool is_indexed) override; |
|||
|
|||
struct VertexShader { |
|||
OGLShader shader; |
|||
}; |
|||
|
|||
struct FragmentShader { |
|||
OGLShader shader; |
|||
}; |
|||
|
|||
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned |
|||
// NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at |
|||
// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. |
|||
// Not following that rule will cause problems on some AMD drivers. |
|||
struct UniformData {}; |
|||
|
|||
// static_assert( |
|||
// sizeof(UniformData) == 0x460, |
|||
// "The size of the UniformData structure has changed, update the structure in the shader"); |
|||
static_assert(sizeof(UniformData) < 16384, |
|||
"UniformData structure must be less than 16kb as per the OpenGL spec"); |
|||
|
|||
struct VSUniformData {}; |
|||
// static_assert( |
|||
// sizeof(VSUniformData) == 1856, |
|||
// "The size of the VSUniformData structure has changed, update the structure in the |
|||
// shader"); |
|||
static_assert(sizeof(VSUniformData) < 16384, |
|||
"VSUniformData structure must be less than 16kb as per the OpenGL spec"); |
|||
|
|||
struct FSUniformData {}; |
|||
// static_assert( |
|||
// sizeof(FSUniformData) == 1856, |
|||
// "The size of the FSUniformData structure has changed, update the structure in the |
|||
// shader"); |
|||
static_assert(sizeof(FSUniformData) < 16384, |
|||
"FSUniformData structure must be less than 16kb as per the OpenGL spec"); |
|||
|
|||
private: |
|||
struct SamplerInfo {}; |
|||
|
|||
/// Syncs the clip enabled status to match the guest state |
|||
void SyncClipEnabled(); |
|||
|
|||
/// Syncs the clip coefficients to match the guest state |
|||
void SyncClipCoef(); |
|||
|
|||
/// Sets the OpenGL shader in accordance with the current guest state |
|||
void SetShader(); |
|||
|
|||
/// Syncs the cull mode to match the guest state |
|||
void SyncCullMode(); |
|||
|
|||
/// Syncs the depth scale to match the guest state |
|||
void SyncDepthScale(); |
|||
|
|||
/// Syncs the depth offset to match the guest state |
|||
void SyncDepthOffset(); |
|||
|
|||
/// Syncs the blend enabled status to match the guest state |
|||
void SyncBlendEnabled(); |
|||
|
|||
/// Syncs the blend functions to match the guest state |
|||
void SyncBlendFuncs(); |
|||
|
|||
/// Syncs the blend color to match the guest state |
|||
void SyncBlendColor(); |
|||
|
|||
bool has_ARB_buffer_storage; |
|||
bool has_ARB_direct_state_access; |
|||
bool has_ARB_separate_shader_objects; |
|||
bool has_ARB_vertex_attrib_binding; |
|||
|
|||
OpenGLState state; |
|||
|
|||
RasterizerCacheOpenGL res_cache; |
|||
|
|||
struct { |
|||
UniformData data; |
|||
bool dirty; |
|||
} uniform_block_data = {}; |
|||
|
|||
OGLPipeline pipeline; |
|||
OGLVertexArray sw_vao; |
|||
OGLVertexArray hw_vao; |
|||
std::array<bool, 16> hw_vao_enabled_attributes; |
|||
|
|||
std::array<SamplerInfo, 3> texture_samplers; |
|||
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024; |
|||
std::unique_ptr<OGLStreamBuffer> vertex_buffer; |
|||
OGLBuffer uniform_buffer; |
|||
OGLFramebuffer framebuffer; |
|||
|
|||
static constexpr size_t STREAM_BUFFER_SIZE = 4 * 1024 * 1024; |
|||
std::unique_ptr<OGLStreamBuffer> stream_buffer; |
|||
|
|||
GLint vs_input_index_min; |
|||
GLint vs_input_index_max; |
|||
GLsizeiptr vs_input_size; |
|||
|
|||
void AnalyzeVertexArray(bool is_indexed); |
|||
void SetupVertexArray(u8* array_ptr, GLintptr buffer_offset); |
|||
|
|||
OGLBuffer vs_uniform_buffer; |
|||
std::unordered_map<GLShader::MaxwellVSConfig, VertexShader*> vs_shader_map; |
|||
std::unordered_map<std::string, VertexShader> vs_shader_cache; |
|||
OGLShader vs_default_shader; |
|||
|
|||
void SetupVertexShader(VSUniformData* ub_ptr, GLintptr buffer_offset); |
|||
|
|||
OGLBuffer fs_uniform_buffer; |
|||
std::unordered_map<GLShader::MaxwellFSConfig, FragmentShader*> fs_shader_map; |
|||
std::unordered_map<std::string, FragmentShader> fs_shader_cache; |
|||
OGLShader fs_default_shader; |
|||
|
|||
void SetupFragmentShader(FSUniformData* ub_ptr, GLintptr buffer_offset); |
|||
|
|||
enum class AccelDraw { Disabled, Arrays, Indexed }; |
|||
AccelDraw accelerate_draw; |
|||
}; |
|||
1361
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,350 @@ |
|||
// Copyright 2015 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <array> |
|||
#include <memory> |
|||
#include <set> |
|||
#include <tuple> |
|||
#ifdef __GNUC__ |
|||
#pragma GCC diagnostic push |
|||
#pragma GCC diagnostic ignored "-Wunused-local-typedefs" |
|||
#endif |
|||
#include <boost/icl/interval_map.hpp> |
|||
#include <boost/icl/interval_set.hpp> |
|||
#ifdef __GNUC__ |
|||
#pragma GCC diagnostic pop |
|||
#endif |
|||
#include <glad/glad.h> |
|||
#include "common/assert.h" |
|||
#include "common/common_funcs.h" |
|||
#include "common/common_types.h" |
|||
#include "common/math_util.h" |
|||
#include "video_core/renderer_opengl/gl_resource_manager.h" |
|||
|
|||
struct CachedSurface; |
|||
using Surface = std::shared_ptr<CachedSurface>; |
|||
using SurfaceSet = std::set<Surface>; |
|||
|
|||
using SurfaceRegions = boost::icl::interval_set<PAddr>; |
|||
using SurfaceMap = boost::icl::interval_map<PAddr, Surface>; |
|||
using SurfaceCache = boost::icl::interval_map<PAddr, SurfaceSet>; |
|||
|
|||
using SurfaceInterval = SurfaceCache::interval_type; |
|||
static_assert(std::is_same<SurfaceRegions::interval_type, SurfaceCache::interval_type>() && |
|||
std::is_same<SurfaceMap::interval_type, SurfaceCache::interval_type>(), |
|||
"incorrect interval types"); |
|||
|
|||
using SurfaceRect_Tuple = std::tuple<Surface, MathUtil::Rectangle<u32>>; |
|||
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; |
|||
|
|||
using PageMap = boost::icl::interval_map<u32, int>; |
|||
|
|||
enum class ScaleMatch { |
|||
Exact, // only accept same res scale |
|||
Upscale, // only allow higher scale than params |
|||
Ignore // accept every scaled res |
|||
}; |
|||
|
|||
struct SurfaceParams { |
|||
enum class PixelFormat { |
|||
// First 5 formats are shared between textures and color buffers |
|||
RGBA8 = 0, |
|||
RGB8 = 1, |
|||
RGB5A1 = 2, |
|||
RGB565 = 3, |
|||
RGBA4 = 4, |
|||
|
|||
// Texture-only formats |
|||
IA8 = 5, |
|||
RG8 = 6, |
|||
I8 = 7, |
|||
A8 = 8, |
|||
IA4 = 9, |
|||
I4 = 10, |
|||
A4 = 11, |
|||
ETC1 = 12, |
|||
ETC1A4 = 13, |
|||
|
|||
// Depth buffer-only formats |
|||
D16 = 14, |
|||
// gap |
|||
D24 = 16, |
|||
D24S8 = 17, |
|||
|
|||
Invalid = 255, |
|||
}; |
|||
|
|||
enum class SurfaceType { |
|||
Color = 0, |
|||
Texture = 1, |
|||
Depth = 2, |
|||
DepthStencil = 3, |
|||
Fill = 4, |
|||
Invalid = 5 |
|||
}; |
|||
|
|||
static constexpr unsigned int GetFormatBpp(PixelFormat format) { |
|||
constexpr std::array<unsigned int, 18> bpp_table = { |
|||
32, // RGBA8 |
|||
24, // RGB8 |
|||
16, // RGB5A1 |
|||
16, // RGB565 |
|||
16, // RGBA4 |
|||
16, // IA8 |
|||
16, // RG8 |
|||
8, // I8 |
|||
8, // A8 |
|||
8, // IA4 |
|||
4, // I4 |
|||
4, // A4 |
|||
4, // ETC1 |
|||
8, // ETC1A4 |
|||
16, // D16 |
|||
0, |
|||
24, // D24 |
|||
32, // D24S8 |
|||
}; |
|||
|
|||
assert(static_cast<size_t>(format) < bpp_table.size()); |
|||
return bpp_table[static_cast<size_t>(format)]; |
|||
} |
|||
unsigned int GetFormatBpp() const { |
|||
return GetFormatBpp(pixel_format); |
|||
} |
|||
|
|||
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { |
|||
SurfaceType a_type = GetFormatType(pixel_format_a); |
|||
SurfaceType b_type = GetFormatType(pixel_format_b); |
|||
|
|||
if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && |
|||
(b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { |
|||
return true; |
|||
} |
|||
|
|||
if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { |
|||
return true; |
|||
} |
|||
|
|||
if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) { |
|||
if ((unsigned int)pixel_format < 5) { |
|||
return SurfaceType::Color; |
|||
} |
|||
|
|||
if ((unsigned int)pixel_format < 14) { |
|||
return SurfaceType::Texture; |
|||
} |
|||
|
|||
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { |
|||
return SurfaceType::Depth; |
|||
} |
|||
|
|||
if (pixel_format == PixelFormat::D24S8) { |
|||
return SurfaceType::DepthStencil; |
|||
} |
|||
|
|||
return SurfaceType::Invalid; |
|||
} |
|||
|
|||
/// Update the params "size", "end" and "type" from the already set "addr", "width", "height" |
|||
/// and "pixel_format" |
|||
void UpdateParams() { |
|||
if (stride == 0) { |
|||
stride = width; |
|||
} |
|||
type = GetFormatType(pixel_format); |
|||
size = !is_tiled ? BytesInPixels(stride * (height - 1) + width) |
|||
: BytesInPixels(stride * 8 * (height / 8 - 1) + width * 8); |
|||
end = addr + size; |
|||
} |
|||
|
|||
SurfaceInterval GetInterval() const { |
|||
return SurfaceInterval::right_open(addr, end); |
|||
} |
|||
|
|||
// Returns the outer rectangle containing "interval" |
|||
SurfaceParams FromInterval(SurfaceInterval interval) const; |
|||
|
|||
SurfaceInterval GetSubRectInterval(MathUtil::Rectangle<u32> unscaled_rect) const; |
|||
|
|||
// Returns the region of the biggest valid rectange within interval |
|||
SurfaceInterval GetCopyableInterval(const Surface& src_surface) const; |
|||
|
|||
u32 GetScaledWidth() const { |
|||
return width * res_scale; |
|||
} |
|||
|
|||
u32 GetScaledHeight() const { |
|||
return height * res_scale; |
|||
} |
|||
|
|||
MathUtil::Rectangle<u32> GetRect() const { |
|||
return {0, height, width, 0}; |
|||
} |
|||
|
|||
MathUtil::Rectangle<u32> GetScaledRect() const { |
|||
return {0, GetScaledHeight(), GetScaledWidth(), 0}; |
|||
} |
|||
|
|||
u64 PixelsInBytes(u64 size) const { |
|||
return size * CHAR_BIT / GetFormatBpp(pixel_format); |
|||
} |
|||
|
|||
u64 BytesInPixels(u64 pixels) const { |
|||
return pixels * GetFormatBpp(pixel_format) / CHAR_BIT; |
|||
} |
|||
|
|||
bool ExactMatch(const SurfaceParams& other_surface) const; |
|||
bool CanSubRect(const SurfaceParams& sub_surface) const; |
|||
bool CanExpand(const SurfaceParams& expanded_surface) const; |
|||
bool CanTexCopy(const SurfaceParams& texcopy_params) const; |
|||
|
|||
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const; |
|||
MathUtil::Rectangle<u32> GetScaledSubRect(const SurfaceParams& sub_surface) const; |
|||
|
|||
PAddr addr = 0; |
|||
PAddr end = 0; |
|||
u64 size = 0; |
|||
|
|||
u32 width = 0; |
|||
u32 height = 0; |
|||
u32 stride = 0; |
|||
u16 res_scale = 1; |
|||
|
|||
bool is_tiled = false; |
|||
PixelFormat pixel_format = PixelFormat::Invalid; |
|||
SurfaceType type = SurfaceType::Invalid; |
|||
}; |
|||
|
|||
struct CachedSurface : SurfaceParams { |
|||
bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; |
|||
bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; |
|||
|
|||
bool IsRegionValid(SurfaceInterval interval) const { |
|||
return (invalid_regions.find(interval) == invalid_regions.end()); |
|||
} |
|||
|
|||
bool IsSurfaceFullyInvalid() const { |
|||
return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval()); |
|||
} |
|||
|
|||
bool registered = false; |
|||
SurfaceRegions invalid_regions; |
|||
|
|||
u64 fill_size = 0; /// Number of bytes to read from fill_data |
|||
std::array<u8, 4> fill_data; |
|||
|
|||
OGLTexture texture; |
|||
|
|||
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { |
|||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type |
|||
return format == PixelFormat::Invalid |
|||
? 0 |
|||
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture) |
|||
? 4 |
|||
: SurfaceParams::GetFormatBpp(format) / 8; |
|||
} |
|||
|
|||
std::unique_ptr<u8[]> gl_buffer; |
|||
size_t gl_buffer_size = 0; |
|||
|
|||
// Read/Write data in 3DS memory to/from gl_buffer |
|||
void LoadGLBuffer(PAddr load_start, PAddr load_end); |
|||
void FlushGLBuffer(PAddr flush_start, PAddr flush_end); |
|||
|
|||
// Upload/Download data in gl_buffer in/to this surface's texture |
|||
void UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, |
|||
GLuint draw_fb_handle); |
|||
void DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint read_fb_handle, |
|||
GLuint draw_fb_handle); |
|||
}; |
|||
|
|||
class RasterizerCacheOpenGL : NonCopyable { |
|||
public: |
|||
RasterizerCacheOpenGL(); |
|||
~RasterizerCacheOpenGL(); |
|||
|
|||
/// Blit one surface's texture to another |
|||
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect, |
|||
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect); |
|||
|
|||
void ConvertD24S8toABGR(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, |
|||
GLuint dst_tex, const MathUtil::Rectangle<u32>& dst_rect); |
|||
|
|||
/// Copy one surface's region to another |
|||
void CopySurface(const Surface& src_surface, const Surface& dst_surface, |
|||
SurfaceInterval copy_interval); |
|||
|
|||
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached) |
|||
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, |
|||
bool load_if_create); |
|||
|
|||
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from |
|||
/// 3DS memory to OpenGL and caches it (if not already cached) |
|||
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale, |
|||
bool load_if_create); |
|||
|
|||
/// Get a surface based on the texture configuration |
|||
Surface GetTextureSurface(const void* config); |
|||
|
|||
/// Get the color and depth surfaces based on the framebuffer configuration |
|||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, |
|||
const MathUtil::Rectangle<s32>& viewport_rect); |
|||
|
|||
/// Get a surface that matches the fill config |
|||
Surface GetFillSurface(const void* config); |
|||
|
|||
/// Get a surface that matches a "texture copy" display transfer config |
|||
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); |
|||
|
|||
/// Write any cached resources overlapping the region back to memory (if dirty) |
|||
void FlushRegion(PAddr addr, u64 size, Surface flush_surface = nullptr); |
|||
|
|||
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory) |
|||
void InvalidateRegion(PAddr addr, u64 size, const Surface& region_owner); |
|||
|
|||
/// Flush all cached resources tracked by this cache manager |
|||
void FlushAll(); |
|||
|
|||
private: |
|||
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); |
|||
|
|||
/// Update surface's texture for given region when necessary |
|||
void ValidateSurface(const Surface& surface, PAddr addr, u64 size); |
|||
|
|||
/// Create a new surface |
|||
Surface CreateSurface(const SurfaceParams& params); |
|||
|
|||
/// Register surface into the cache |
|||
void RegisterSurface(const Surface& surface); |
|||
|
|||
/// Remove surface from the cache |
|||
void UnregisterSurface(const Surface& surface); |
|||
|
|||
/// Increase/decrease the number of surface in pages touching the specified region |
|||
void UpdatePagesCachedCount(PAddr addr, u64 size, int delta); |
|||
|
|||
SurfaceCache surface_cache; |
|||
PageMap cached_pages; |
|||
SurfaceMap dirty_regions; |
|||
SurfaceSet remove_surfaces; |
|||
|
|||
OGLFramebuffer read_framebuffer; |
|||
OGLFramebuffer draw_framebuffer; |
|||
|
|||
OGLVertexArray attributeless_vao; |
|||
OGLBuffer d24s8_abgr_buffer; |
|||
GLsizeiptr d24s8_abgr_buffer_size; |
|||
OGLShader d24s8_abgr_shader; |
|||
GLint d24s8_abgr_tbo_size_u_id; |
|||
GLint d24s8_abgr_viewport_u_id; |
|||
}; |
|||
@ -0,0 +1,58 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <string>
|
|||
#include <queue>
|
|||
#include "common/assert.h"
|
|||
#include "common/common_types.h"
|
|||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
|||
|
|||
namespace Maxwell3D { |
|||
namespace Shader { |
|||
namespace Decompiler { |
|||
|
|||
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
|||
|
|||
class Impl { |
|||
public: |
|||
Impl(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, |
|||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, u32 main_offset, |
|||
const std::function<std::string(u32)>& inputreg_getter, |
|||
const std::function<std::string(u32)>& outputreg_getter, bool sanitize_mul, |
|||
const std::string& emit_cb, const std::string& setemit_cb) |
|||
: program_code(program_code), swizzle_data(swizzle_data), main_offset(main_offset), |
|||
inputreg_getter(inputreg_getter), outputreg_getter(outputreg_getter), |
|||
sanitize_mul(sanitize_mul), emit_cb(emit_cb), setemit_cb(setemit_cb) {} |
|||
|
|||
std::string Decompile() { |
|||
UNIMPLEMENTED(); |
|||
return {}; |
|||
} |
|||
|
|||
private: |
|||
const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code; |
|||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data; |
|||
u32 main_offset; |
|||
const std::function<std::string(u32)>& inputreg_getter; |
|||
const std::function<std::string(u32)>& outputreg_getter; |
|||
bool sanitize_mul; |
|||
const std::string& emit_cb; |
|||
const std::string& setemit_cb; |
|||
}; |
|||
|
|||
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, |
|||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, |
|||
u32 main_offset, |
|||
const std::function<std::string(u32)>& inputreg_getter, |
|||
const std::function<std::string(u32)>& outputreg_getter, |
|||
bool sanitize_mul, const std::string& emit_cb, |
|||
const std::string& setemit_cb) { |
|||
Impl impl(program_code, swizzle_data, main_offset, inputreg_getter, outputreg_getter, |
|||
sanitize_mul, emit_cb, setemit_cb); |
|||
return impl.Decompile(); |
|||
} |
|||
|
|||
} // namespace Decompiler
|
|||
} // namespace Shader
|
|||
} // namespace Maxwell3D
|
|||
@ -0,0 +1,27 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#include <array> |
|||
#include <functional> |
|||
#include <string> |
|||
#include "common/common_types.h" |
|||
|
|||
namespace Maxwell3D { |
|||
namespace Shader { |
|||
namespace Decompiler { |
|||
|
|||
constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x100000}; |
|||
constexpr size_t MAX_SWIZZLE_DATA_LENGTH{0x100000}; |
|||
|
|||
std::string DecompileProgram(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>& program_code, |
|||
const std::array<u32, MAX_SWIZZLE_DATA_LENGTH>& swizzle_data, |
|||
u32 main_offset, |
|||
const std::function<std::string(u32)>& inputreg_getter, |
|||
const std::function<std::string(u32)>& outputreg_getter, |
|||
bool sanitize_mul, const std::string& emit_cb = "", |
|||
const std::string& setemit_cb = ""); |
|||
|
|||
} // namespace Decompiler |
|||
} // namespace Shader |
|||
} // namespace Maxwell3D |
|||
@ -0,0 +1,20 @@ |
|||
// Copyright 2018 yuzu Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include "common/assert.h"
|
|||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
|||
|
|||
namespace GLShader { |
|||
|
|||
std::string GenerateVertexShader(const MaxwellVSConfig& config) { |
|||
UNIMPLEMENTED(); |
|||
return {}; |
|||
} |
|||
|
|||
std::string GenerateFragmentShader(const MaxwellFSConfig& config) { |
|||
UNIMPLEMENTED(); |
|||
return {}; |
|||
} |
|||
|
|||
} // namespace GLShader
|
|||
@ -0,0 +1,66 @@ |
|||
// Copyright 2018 yuzu Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#pragma once |
|||
|
|||
#include <cstring> |
|||
#include <string> |
|||
#include <type_traits> |
|||
#include "common/hash.h" |
|||
|
|||
namespace GLShader { |
|||
|
|||
enum Attributes { |
|||
ATTRIBUTE_POSITION, |
|||
ATTRIBUTE_COLOR, |
|||
ATTRIBUTE_TEXCOORD0, |
|||
ATTRIBUTE_TEXCOORD1, |
|||
ATTRIBUTE_TEXCOORD2, |
|||
ATTRIBUTE_TEXCOORD0_W, |
|||
ATTRIBUTE_NORMQUAT, |
|||
ATTRIBUTE_VIEW, |
|||
}; |
|||
|
|||
struct MaxwellShaderConfigCommon { |
|||
explicit MaxwellShaderConfigCommon(){}; |
|||
}; |
|||
|
|||
struct MaxwellVSConfig : MaxwellShaderConfigCommon { |
|||
explicit MaxwellVSConfig() : MaxwellShaderConfigCommon() {} |
|||
|
|||
bool operator==(const MaxwellVSConfig& o) const { |
|||
return std::memcmp(this, &o, sizeof(MaxwellVSConfig)) == 0; |
|||
}; |
|||
}; |
|||
|
|||
struct MaxwellFSConfig : MaxwellShaderConfigCommon { |
|||
explicit MaxwellFSConfig() : MaxwellShaderConfigCommon() {} |
|||
|
|||
bool operator==(const MaxwellFSConfig& o) const { |
|||
return std::memcmp(this, &o, sizeof(MaxwellFSConfig)) == 0; |
|||
}; |
|||
}; |
|||
|
|||
std::string GenerateVertexShader(const MaxwellVSConfig& config); |
|||
std::string GenerateFragmentShader(const MaxwellFSConfig& config); |
|||
|
|||
} // namespace GLShader |
|||
|
|||
namespace std { |
|||
|
|||
template <> |
|||
struct hash<GLShader::MaxwellVSConfig> { |
|||
size_t operator()(const GLShader::MaxwellVSConfig& k) const { |
|||
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellVSConfig)); |
|||
} |
|||
}; |
|||
|
|||
template <> |
|||
struct hash<GLShader::MaxwellFSConfig> { |
|||
size_t operator()(const GLShader::MaxwellFSConfig& k) const { |
|||
return Common::ComputeHash64(&k, sizeof(GLShader::MaxwellFSConfig)); |
|||
} |
|||
}; |
|||
|
|||
} // namespace std |
|||
@ -0,0 +1,182 @@ |
|||
// Copyright 2018 Citra Emulator Project
|
|||
// Licensed under GPLv2 or any later version
|
|||
// Refer to the license.txt file included.
|
|||
|
|||
#include <deque>
|
|||
#include <vector>
|
|||
#include "common/alignment.h"
|
|||
#include "common/assert.h"
|
|||
#include "video_core/renderer_opengl/gl_state.h"
|
|||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
|||
|
|||
class OrphanBuffer : public OGLStreamBuffer { |
|||
public: |
|||
explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {} |
|||
~OrphanBuffer() override; |
|||
|
|||
private: |
|||
void Create(size_t size, size_t sync_subdivide) override; |
|||
void Release() override; |
|||
|
|||
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; |
|||
void Unmap() override; |
|||
|
|||
std::vector<u8> data; |
|||
}; |
|||
|
|||
class StorageBuffer : public OGLStreamBuffer { |
|||
public: |
|||
explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {} |
|||
~StorageBuffer() override; |
|||
|
|||
private: |
|||
void Create(size_t size, size_t sync_subdivide) override; |
|||
void Release() override; |
|||
|
|||
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override; |
|||
void Unmap() override; |
|||
|
|||
struct Fence { |
|||
OGLSync sync; |
|||
size_t offset; |
|||
}; |
|||
std::deque<Fence> head; |
|||
std::deque<Fence> tail; |
|||
|
|||
u8* mapped_ptr; |
|||
}; |
|||
|
|||
OGLStreamBuffer::OGLStreamBuffer(GLenum target) { |
|||
gl_target = target; |
|||
} |
|||
|
|||
GLuint OGLStreamBuffer::GetHandle() const { |
|||
return gl_buffer.handle; |
|||
} |
|||
|
|||
std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) { |
|||
if (storage_buffer) { |
|||
return std::make_unique<StorageBuffer>(target); |
|||
} |
|||
return std::make_unique<OrphanBuffer>(target); |
|||
} |
|||
|
|||
OrphanBuffer::~OrphanBuffer() { |
|||
Release(); |
|||
} |
|||
|
|||
void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) { |
|||
buffer_pos = 0; |
|||
buffer_size = size; |
|||
data.resize(buffer_size); |
|||
|
|||
if (gl_buffer.handle == 0) { |
|||
gl_buffer.Create(); |
|||
glBindBuffer(gl_target, gl_buffer.handle); |
|||
} |
|||
|
|||
glBufferData(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW); |
|||
} |
|||
|
|||
void OrphanBuffer::Release() { |
|||
gl_buffer.Release(); |
|||
} |
|||
|
|||
std::pair<u8*, GLintptr> OrphanBuffer::Map(size_t size, size_t alignment) { |
|||
buffer_pos = Common::AlignUp(buffer_pos, alignment); |
|||
|
|||
if (buffer_pos + size > buffer_size) { |
|||
Create(std::max(buffer_size, size), 0); |
|||
} |
|||
|
|||
mapped_size = size; |
|||
return std::make_pair(&data[buffer_pos], static_cast<GLintptr>(buffer_pos)); |
|||
} |
|||
|
|||
void OrphanBuffer::Unmap() { |
|||
glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos), |
|||
static_cast<GLsizeiptr>(mapped_size), &data[buffer_pos]); |
|||
buffer_pos += mapped_size; |
|||
} |
|||
|
|||
StorageBuffer::~StorageBuffer() { |
|||
Release(); |
|||
} |
|||
|
|||
void StorageBuffer::Create(size_t size, size_t sync_subdivide) { |
|||
if (gl_buffer.handle != 0) |
|||
return; |
|||
|
|||
buffer_pos = 0; |
|||
buffer_size = size; |
|||
buffer_sync_subdivide = std::max<size_t>(sync_subdivide, 1); |
|||
|
|||
gl_buffer.Create(); |
|||
glBindBuffer(gl_target, gl_buffer.handle); |
|||
|
|||
glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr, |
|||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT); |
|||
mapped_ptr = reinterpret_cast<u8*>( |
|||
glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(buffer_size), |
|||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); |
|||
} |
|||
|
|||
void StorageBuffer::Release() { |
|||
if (gl_buffer.handle == 0) |
|||
return; |
|||
|
|||
glUnmapBuffer(gl_target); |
|||
|
|||
gl_buffer.Release(); |
|||
head.clear(); |
|||
tail.clear(); |
|||
} |
|||
|
|||
std::pair<u8*, GLintptr> StorageBuffer::Map(size_t size, size_t alignment) { |
|||
ASSERT(size <= buffer_size); |
|||
|
|||
OGLSync sync; |
|||
|
|||
buffer_pos = Common::AlignUp(buffer_pos, alignment); |
|||
size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide); |
|||
|
|||
if (!head.empty() && |
|||
(effective_offset > head.back().offset || buffer_pos + size > buffer_size)) { |
|||
ASSERT(head.back().sync.handle == 0); |
|||
head.back().sync.Create(); |
|||
} |
|||
|
|||
if (buffer_pos + size > buffer_size) { |
|||
if (!tail.empty()) { |
|||
std::swap(sync, tail.back().sync); |
|||
tail.clear(); |
|||
} |
|||
std::swap(tail, head); |
|||
buffer_pos = 0; |
|||
effective_offset = 0; |
|||
} |
|||
|
|||
while (!tail.empty() && buffer_pos + size > tail.front().offset) { |
|||
std::swap(sync, tail.front().sync); |
|||
tail.pop_front(); |
|||
} |
|||
|
|||
if (sync.handle != 0) { |
|||
glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); |
|||
sync.Release(); |
|||
} |
|||
|
|||
if (head.empty() || effective_offset > head.back().offset) { |
|||
head.emplace_back(); |
|||
head.back().offset = effective_offset; |
|||
} |
|||
|
|||
mapped_size = size; |
|||
return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos)); |
|||
} |
|||
|
|||
void StorageBuffer::Unmap() { |
|||
glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos), |
|||
static_cast<GLsizeiptr>(mapped_size)); |
|||
buffer_pos += mapped_size; |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright 2018 Citra Emulator Project |
|||
// Licensed under GPLv2 or any later version |
|||
// Refer to the license.txt file included. |
|||
|
|||
#include <memory> |
|||
#include <glad/glad.h> |
|||
#include "common/common_types.h" |
|||
#include "video_core/renderer_opengl/gl_resource_manager.h" |
|||
|
|||
class OGLStreamBuffer : private NonCopyable { |
|||
public: |
|||
explicit OGLStreamBuffer(GLenum target); |
|||
virtual ~OGLStreamBuffer() = default; |
|||
|
|||
public: |
|||
static std::unique_ptr<OGLStreamBuffer> MakeBuffer(bool storage_buffer, GLenum target); |
|||
|
|||
virtual void Create(size_t size, size_t sync_subdivide) = 0; |
|||
virtual void Release() {} |
|||
|
|||
GLuint GetHandle() const; |
|||
|
|||
virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0; |
|||
virtual void Unmap() = 0; |
|||
|
|||
protected: |
|||
OGLBuffer gl_buffer; |
|||
GLenum gl_target; |
|||
|
|||
size_t buffer_pos = 0; |
|||
size_t buffer_size = 0; |
|||
size_t buffer_sync_subdivide = 0; |
|||
size_t mapped_size = 0; |
|||
}; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue