mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-22 10:51:50 -05:00
Merge pull request #254 from bunnei/port-citra-renderer
Port Citra OpenGL rasterizer code
This commit is contained in:
commit
0b3ab30762
22 changed files with 24231 additions and 1236 deletions
2
externals/glad/Readme.md
vendored
2
externals/glad/Readme.md
vendored
|
@ -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:
|
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
|
||||||
```
|
```
|
||||||
|
|
6
externals/glad/include/KHR/khrplatform.h
vendored
6
externals/glad/include/KHR/khrplatform.h
vendored
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
/* Khronos platform-specific types and definitions.
|
/* Khronos platform-specific types and definitions.
|
||||||
*
|
*
|
||||||
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
|
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
|
||||||
*
|
*
|
||||||
* Adopters may modify this file to suit their platform. Adopters are
|
* Adopters may modify this file to suit their platform. Adopters are
|
||||||
* encouraged to submit platform specific modifications to the Khronos
|
* encouraged to submit platform specific modifications to the Khronos
|
||||||
|
@ -101,6 +101,8 @@
|
||||||
# define KHRONOS_APICALL __declspec(dllimport)
|
# define KHRONOS_APICALL __declspec(dllimport)
|
||||||
#elif defined (__SYMBIAN32__)
|
#elif defined (__SYMBIAN32__)
|
||||||
# define KHRONOS_APICALL IMPORT_C
|
# define KHRONOS_APICALL IMPORT_C
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||||
#else
|
#else
|
||||||
# define KHRONOS_APICALL
|
# define KHRONOS_APICALL
|
||||||
#endif
|
#endif
|
||||||
|
@ -223,7 +225,7 @@ typedef signed short int khronos_int16_t;
|
||||||
typedef unsigned short int khronos_uint16_t;
|
typedef unsigned short int khronos_uint16_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||||
* to be the only LLP64 architecture in current use.
|
* to be the only LLP64 architecture in current use.
|
||||||
*/
|
*/
|
||||||
|
|
13755
externals/glad/include/glad/glad.h
vendored
13755
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
8704
externals/glad/src/glad.c
vendored
8704
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
|
@ -11,13 +11,24 @@ add_library(video_core STATIC
|
||||||
gpu.h
|
gpu.h
|
||||||
memory_manager.cpp
|
memory_manager.cpp
|
||||||
memory_manager.h
|
memory_manager.h
|
||||||
|
rasterizer_interface.h
|
||||||
renderer_base.cpp
|
renderer_base.cpp
|
||||||
renderer_base.h
|
renderer_base.h
|
||||||
|
renderer_opengl/gl_rasterizer.cpp
|
||||||
|
renderer_opengl/gl_rasterizer.h
|
||||||
|
renderer_opengl/gl_rasterizer_cache.cpp
|
||||||
|
renderer_opengl/gl_rasterizer_cache.h
|
||||||
renderer_opengl/gl_resource_manager.h
|
renderer_opengl/gl_resource_manager.h
|
||||||
|
renderer_opengl/gl_shader_decompiler.cpp
|
||||||
|
renderer_opengl/gl_shader_decompiler.h
|
||||||
|
renderer_opengl/gl_shader_gen.cpp
|
||||||
|
renderer_opengl/gl_shader_gen.h
|
||||||
renderer_opengl/gl_shader_util.cpp
|
renderer_opengl/gl_shader_util.cpp
|
||||||
renderer_opengl/gl_shader_util.h
|
renderer_opengl/gl_shader_util.h
|
||||||
renderer_opengl/gl_state.cpp
|
renderer_opengl/gl_state.cpp
|
||||||
renderer_opengl/gl_state.h
|
renderer_opengl/gl_state.h
|
||||||
|
renderer_opengl/gl_stream_buffer.cpp
|
||||||
|
renderer_opengl/gl_stream_buffer.h
|
||||||
renderer_opengl/renderer_opengl.cpp
|
renderer_opengl/renderer_opengl.cpp
|
||||||
renderer_opengl/renderer_opengl.h
|
renderer_opengl/renderer_opengl.h
|
||||||
utils.h
|
utils.h
|
||||||
|
|
61
src/video_core/rasterizer_interface.h
Normal file
61
src/video_core/rasterizer_interface.h
Normal 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
|
269
src/video_core/renderer_opengl/gl_rasterizer.cpp
Normal file
269
src/video_core/renderer_opengl/gl_rasterizer.cpp
Normal file
|
@ -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();
|
||||||
|
}
|
162
src/video_core/renderer_opengl/gl_rasterizer.h
Normal file
162
src/video_core/renderer_opengl/gl_rasterizer.h
Normal file
|
@ -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
Normal file
1361
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
Normal file
File diff suppressed because it is too large
Load diff
350
src/video_core/renderer_opengl/gl_rasterizer_cache.h
Normal file
350
src/video_core/renderer_opengl/gl_rasterizer_cache.h
Normal 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;
|
||||||
|
};
|
|
@ -36,7 +36,7 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteTextures(1, &handle);
|
glDeleteTextures(1, &handle);
|
||||||
OpenGLState::ResetTexture(handle);
|
OpenGLState::GetCurState().ResetTexture(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteSamplers(1, &handle);
|
glDeleteSamplers(1, &handle);
|
||||||
OpenGLState::ResetSampler(handle);
|
OpenGLState::GetCurState().ResetSampler(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,10 +91,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new internal OpenGL resource and stores the handle
|
/// Creates a new internal OpenGL resource and stores the handle
|
||||||
void Create(const char* vert_shader, const char* frag_shader) {
|
void Create(const char* vert_shader, const char* geo_shader, const char* frag_shader,
|
||||||
|
const std::vector<const char*>& feedback_vars = {},
|
||||||
|
bool separable_program = false) {
|
||||||
if (handle != 0)
|
if (handle != 0)
|
||||||
return;
|
return;
|
||||||
handle = GLShader::LoadProgram(vert_shader, frag_shader);
|
handle = GLShader::LoadProgram(vert_shader, geo_shader, frag_shader, feedback_vars,
|
||||||
|
separable_program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes the internal OpenGL resource
|
/// Deletes the internal OpenGL resource
|
||||||
|
@ -102,7 +105,40 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteProgram(handle);
|
glDeleteProgram(handle);
|
||||||
OpenGLState::ResetProgram(handle);
|
OpenGLState::GetCurState().ResetProgram(handle).Apply();
|
||||||
|
handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint handle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OGLPipeline : private NonCopyable {
|
||||||
|
public:
|
||||||
|
OGLPipeline() = default;
|
||||||
|
OGLPipeline(OGLPipeline&& o) {
|
||||||
|
handle = std::exchange<GLuint>(o.handle, 0);
|
||||||
|
}
|
||||||
|
~OGLPipeline() {
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
OGLPipeline& operator=(OGLPipeline&& o) {
|
||||||
|
handle = std::exchange<GLuint>(o.handle, 0);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new internal OpenGL resource and stores the handle
|
||||||
|
void Create() {
|
||||||
|
if (handle != 0)
|
||||||
|
return;
|
||||||
|
glGenProgramPipelines(1, &handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the internal OpenGL resource
|
||||||
|
void Release() {
|
||||||
|
if (handle == 0)
|
||||||
|
return;
|
||||||
|
glDeleteProgramPipelines(1, &handle);
|
||||||
|
OpenGLState::GetCurState().ResetPipeline(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,13 +171,46 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteBuffers(1, &handle);
|
glDeleteBuffers(1, &handle);
|
||||||
OpenGLState::ResetBuffer(handle);
|
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint handle = 0;
|
GLuint handle = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OGLSync : private NonCopyable {
|
||||||
|
public:
|
||||||
|
OGLSync() = default;
|
||||||
|
|
||||||
|
OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {}
|
||||||
|
|
||||||
|
~OGLSync() {
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
OGLSync& operator=(OGLSync&& o) {
|
||||||
|
Release();
|
||||||
|
handle = std::exchange(o.handle, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new internal OpenGL resource and stores the handle
|
||||||
|
void Create() {
|
||||||
|
if (handle != 0)
|
||||||
|
return;
|
||||||
|
handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the internal OpenGL resource
|
||||||
|
void Release() {
|
||||||
|
if (handle == 0)
|
||||||
|
return;
|
||||||
|
glDeleteSync(handle);
|
||||||
|
handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLsync handle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class OGLVertexArray : private NonCopyable {
|
class OGLVertexArray : private NonCopyable {
|
||||||
public:
|
public:
|
||||||
OGLVertexArray() = default;
|
OGLVertexArray() = default;
|
||||||
|
@ -168,7 +237,7 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteVertexArrays(1, &handle);
|
glDeleteVertexArrays(1, &handle);
|
||||||
OpenGLState::ResetVertexArray(handle);
|
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +270,7 @@ public:
|
||||||
if (handle == 0)
|
if (handle == 0)
|
||||||
return;
|
return;
|
||||||
glDeleteFramebuffers(1, &handle);
|
glDeleteFramebuffers(1, &handle);
|
||||||
OpenGLState::ResetFramebuffer(handle);
|
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
|
||||||
handle = 0;
|
handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
src/video_core/renderer_opengl/gl_shader_decompiler.cpp
Normal file
58
src/video_core/renderer_opengl/gl_shader_decompiler.cpp
Normal file
|
@ -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
|
27
src/video_core/renderer_opengl/gl_shader_decompiler.h
Normal file
27
src/video_core/renderer_opengl/gl_shader_decompiler.h
Normal file
|
@ -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
|
20
src/video_core/renderer_opengl/gl_shader_gen.cpp
Normal file
20
src/video_core/renderer_opengl/gl_shader_gen.cpp
Normal file
|
@ -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
|
66
src/video_core/renderer_opengl/gl_shader_gen.h
Normal file
66
src/video_core/renderer_opengl/gl_shader_gen.h
Normal file
|
@ -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
|
|
@ -10,53 +10,85 @@
|
||||||
|
|
||||||
namespace GLShader {
|
namespace GLShader {
|
||||||
|
|
||||||
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||||
|
const char* fragment_shader, const std::vector<const char*>& feedback_vars,
|
||||||
|
bool separable_program) {
|
||||||
// Create the shaders
|
// Create the shaders
|
||||||
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vertex_shader_id = vertex_shader ? glCreateShader(GL_VERTEX_SHADER) : 0;
|
||||||
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
|
GLuint geometry_shader_id = geometry_shader ? glCreateShader(GL_GEOMETRY_SHADER) : 0;
|
||||||
|
GLuint fragment_shader_id = fragment_shader ? glCreateShader(GL_FRAGMENT_SHADER) : 0;
|
||||||
|
|
||||||
GLint result = GL_FALSE;
|
GLint result = GL_FALSE;
|
||||||
int info_log_length;
|
int info_log_length;
|
||||||
|
|
||||||
// Compile Vertex Shader
|
if (vertex_shader) {
|
||||||
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
|
// Compile Vertex Shader
|
||||||
|
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
|
||||||
|
|
||||||
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
|
glShaderSource(vertex_shader_id, 1, &vertex_shader, nullptr);
|
||||||
glCompileShader(vertex_shader_id);
|
glCompileShader(vertex_shader_id);
|
||||||
|
|
||||||
// Check Vertex Shader
|
// Check Vertex Shader
|
||||||
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
|
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &result);
|
||||||
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
glGetShaderiv(vertex_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||||
|
|
||||||
if (info_log_length > 1) {
|
if (info_log_length > 1) {
|
||||||
std::vector<char> vertex_shader_error(info_log_length);
|
std::vector<char> vertex_shader_error(info_log_length);
|
||||||
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
|
glGetShaderInfoLog(vertex_shader_id, info_log_length, nullptr, &vertex_shader_error[0]);
|
||||||
if (result == GL_TRUE) {
|
if (result == GL_TRUE) {
|
||||||
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
|
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s", &vertex_shader_error[0]);
|
LOG_ERROR(Render_OpenGL, "Error compiling vertex shader:\n%s",
|
||||||
|
&vertex_shader_error[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile Fragment Shader
|
if (geometry_shader) {
|
||||||
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
|
// Compile Geometry Shader
|
||||||
|
LOG_DEBUG(Render_OpenGL, "Compiling geometry shader...");
|
||||||
|
|
||||||
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
|
glShaderSource(geometry_shader_id, 1, &geometry_shader, nullptr);
|
||||||
glCompileShader(fragment_shader_id);
|
glCompileShader(geometry_shader_id);
|
||||||
|
|
||||||
// Check Fragment Shader
|
// Check Geometry Shader
|
||||||
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
|
glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &result);
|
||||||
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||||
|
|
||||||
if (info_log_length > 1) {
|
if (info_log_length > 1) {
|
||||||
std::vector<char> fragment_shader_error(info_log_length);
|
std::vector<char> geometry_shader_error(info_log_length);
|
||||||
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr, &fragment_shader_error[0]);
|
glGetShaderInfoLog(geometry_shader_id, info_log_length, nullptr,
|
||||||
if (result == GL_TRUE) {
|
&geometry_shader_error[0]);
|
||||||
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
|
if (result == GL_TRUE) {
|
||||||
} else {
|
LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]);
|
||||||
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
|
} else {
|
||||||
&fragment_shader_error[0]);
|
LOG_ERROR(Render_OpenGL, "Error compiling geometry shader:\n%s",
|
||||||
|
&geometry_shader_error[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fragment_shader) {
|
||||||
|
// Compile Fragment Shader
|
||||||
|
LOG_DEBUG(Render_OpenGL, "Compiling fragment shader...");
|
||||||
|
|
||||||
|
glShaderSource(fragment_shader_id, 1, &fragment_shader, nullptr);
|
||||||
|
glCompileShader(fragment_shader_id);
|
||||||
|
|
||||||
|
// Check Fragment Shader
|
||||||
|
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &result);
|
||||||
|
glGetShaderiv(fragment_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||||
|
|
||||||
|
if (info_log_length > 1) {
|
||||||
|
std::vector<char> fragment_shader_error(info_log_length);
|
||||||
|
glGetShaderInfoLog(fragment_shader_id, info_log_length, nullptr,
|
||||||
|
&fragment_shader_error[0]);
|
||||||
|
if (result == GL_TRUE) {
|
||||||
|
LOG_DEBUG(Render_OpenGL, "%s", &fragment_shader_error[0]);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Render_OpenGL, "Error compiling fragment shader:\n%s",
|
||||||
|
&fragment_shader_error[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +96,25 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
||||||
LOG_DEBUG(Render_OpenGL, "Linking program...");
|
LOG_DEBUG(Render_OpenGL, "Linking program...");
|
||||||
|
|
||||||
GLuint program_id = glCreateProgram();
|
GLuint program_id = glCreateProgram();
|
||||||
glAttachShader(program_id, vertex_shader_id);
|
if (vertex_shader) {
|
||||||
glAttachShader(program_id, fragment_shader_id);
|
glAttachShader(program_id, vertex_shader_id);
|
||||||
|
}
|
||||||
|
if (geometry_shader) {
|
||||||
|
glAttachShader(program_id, geometry_shader_id);
|
||||||
|
}
|
||||||
|
if (fragment_shader) {
|
||||||
|
glAttachShader(program_id, fragment_shader_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!feedback_vars.empty()) {
|
||||||
|
auto varyings = feedback_vars;
|
||||||
|
glTransformFeedbackVaryings(program_id, static_cast<GLsizei>(feedback_vars.size()),
|
||||||
|
&varyings[0], GL_INTERLEAVED_ATTRIBS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (separable_program) {
|
||||||
|
glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
glLinkProgram(program_id);
|
glLinkProgram(program_id);
|
||||||
|
|
||||||
|
@ -85,13 +134,30 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
||||||
|
|
||||||
// If the program linking failed at least one of the shaders was probably bad
|
// If the program linking failed at least one of the shaders was probably bad
|
||||||
if (result == GL_FALSE) {
|
if (result == GL_FALSE) {
|
||||||
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
|
if (vertex_shader) {
|
||||||
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
|
LOG_ERROR(Render_OpenGL, "Vertex shader:\n%s", vertex_shader);
|
||||||
|
}
|
||||||
|
if (geometry_shader) {
|
||||||
|
LOG_ERROR(Render_OpenGL, "Geometry shader:\n%s", geometry_shader);
|
||||||
|
}
|
||||||
|
if (fragment_shader) {
|
||||||
|
LOG_ERROR(Render_OpenGL, "Fragment shader:\n%s", fragment_shader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
|
ASSERT_MSG(result == GL_TRUE, "Shader not linked");
|
||||||
|
|
||||||
glDeleteShader(vertex_shader_id);
|
if (vertex_shader) {
|
||||||
glDeleteShader(fragment_shader_id);
|
glDetachShader(program_id, vertex_shader_id);
|
||||||
|
glDeleteShader(vertex_shader_id);
|
||||||
|
}
|
||||||
|
if (geometry_shader) {
|
||||||
|
glDetachShader(program_id, geometry_shader_id);
|
||||||
|
glDeleteShader(geometry_shader_id);
|
||||||
|
}
|
||||||
|
if (fragment_shader) {
|
||||||
|
glDetachShader(program_id, fragment_shader_id);
|
||||||
|
glDeleteShader(fragment_shader_id);
|
||||||
|
}
|
||||||
|
|
||||||
return program_id;
|
return program_id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
namespace GLShader {
|
namespace GLShader {
|
||||||
|
@ -11,9 +12,12 @@ namespace GLShader {
|
||||||
/**
|
/**
|
||||||
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
|
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
|
||||||
* @param vertex_shader String of the GLSL vertex shader program
|
* @param vertex_shader String of the GLSL vertex shader program
|
||||||
|
* @param geometry_shader String of the GLSL geometry shader program
|
||||||
* @param fragment_shader String of the GLSL fragment shader program
|
* @param fragment_shader String of the GLSL fragment shader program
|
||||||
* @returns Handle of the newly created OpenGL shader object
|
* @returns Handle of the newly created OpenGL shader object
|
||||||
*/
|
*/
|
||||||
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
|
GLuint LoadProgram(const char* vertex_shader, const char* geometry_shader,
|
||||||
|
const char* fragment_shader, const std::vector<const char*>& feedback_vars = {},
|
||||||
|
bool separable_program = false);
|
||||||
|
|
||||||
} // namespace GLShader
|
} // namespace GLShader
|
||||||
|
|
|
@ -33,7 +33,7 @@ OpenGLState::OpenGLState() {
|
||||||
stencil.action_depth_pass = GL_KEEP;
|
stencil.action_depth_pass = GL_KEEP;
|
||||||
stencil.action_stencil_fail = GL_KEEP;
|
stencil.action_stencil_fail = GL_KEEP;
|
||||||
|
|
||||||
blend.enabled = false;
|
blend.enabled = true;
|
||||||
blend.rgb_equation = GL_FUNC_ADD;
|
blend.rgb_equation = GL_FUNC_ADD;
|
||||||
blend.a_equation = GL_FUNC_ADD;
|
blend.a_equation = GL_FUNC_ADD;
|
||||||
blend.src_rgb_func = GL_ONE;
|
blend.src_rgb_func = GL_ONE;
|
||||||
|
@ -68,6 +68,18 @@ OpenGLState::OpenGLState() {
|
||||||
draw.vertex_buffer = 0;
|
draw.vertex_buffer = 0;
|
||||||
draw.uniform_buffer = 0;
|
draw.uniform_buffer = 0;
|
||||||
draw.shader_program = 0;
|
draw.shader_program = 0;
|
||||||
|
draw.program_pipeline = 0;
|
||||||
|
|
||||||
|
scissor.enabled = false;
|
||||||
|
scissor.x = 0;
|
||||||
|
scissor.y = 0;
|
||||||
|
scissor.width = 0;
|
||||||
|
scissor.height = 0;
|
||||||
|
|
||||||
|
viewport.x = 0;
|
||||||
|
viewport.y = 0;
|
||||||
|
viewport.width = 0;
|
||||||
|
viewport.height = 0;
|
||||||
|
|
||||||
clip_distance = {};
|
clip_distance = {};
|
||||||
}
|
}
|
||||||
|
@ -148,9 +160,6 @@ void OpenGLState::Apply() const {
|
||||||
if (blend.enabled != cur_state.blend.enabled) {
|
if (blend.enabled != cur_state.blend.enabled) {
|
||||||
if (blend.enabled) {
|
if (blend.enabled) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
|
|
||||||
cur_state.logic_op = GL_COPY;
|
|
||||||
glLogicOp(cur_state.logic_op);
|
|
||||||
glDisable(GL_COLOR_LOGIC_OP);
|
glDisable(GL_COLOR_LOGIC_OP);
|
||||||
} else {
|
} else {
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
|
@ -196,7 +205,7 @@ void OpenGLState::Apply() const {
|
||||||
// Lighting LUTs
|
// Lighting LUTs
|
||||||
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
|
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
|
||||||
glActiveTexture(TextureUnits::LightingLUT.Enum());
|
glActiveTexture(TextureUnits::LightingLUT.Enum());
|
||||||
glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
|
glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fog LUT
|
// Fog LUT
|
||||||
|
@ -263,6 +272,31 @@ void OpenGLState::Apply() const {
|
||||||
glUseProgram(draw.shader_program);
|
glUseProgram(draw.shader_program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Program pipeline
|
||||||
|
if (draw.program_pipeline != cur_state.draw.program_pipeline) {
|
||||||
|
glBindProgramPipeline(draw.program_pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scissor test
|
||||||
|
if (scissor.enabled != cur_state.scissor.enabled) {
|
||||||
|
if (scissor.enabled) {
|
||||||
|
glEnable(GL_SCISSOR_TEST);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_SCISSOR_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
|
||||||
|
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
|
||||||
|
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
|
||||||
|
viewport.width != cur_state.viewport.width ||
|
||||||
|
viewport.height != cur_state.viewport.height) {
|
||||||
|
glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||||
|
}
|
||||||
|
|
||||||
// Clip distance
|
// Clip distance
|
||||||
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
||||||
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
||||||
|
@ -277,62 +311,75 @@ void OpenGLState::Apply() const {
|
||||||
cur_state = *this;
|
cur_state = *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetTexture(GLuint handle) {
|
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
|
||||||
for (auto& unit : cur_state.texture_units) {
|
for (auto& unit : texture_units) {
|
||||||
if (unit.texture_2d == handle) {
|
if (unit.texture_2d == handle) {
|
||||||
unit.texture_2d = 0;
|
unit.texture_2d = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cur_state.lighting_lut.texture_buffer == handle)
|
if (lighting_lut.texture_buffer == handle)
|
||||||
cur_state.lighting_lut.texture_buffer = 0;
|
lighting_lut.texture_buffer = 0;
|
||||||
if (cur_state.fog_lut.texture_buffer == handle)
|
if (fog_lut.texture_buffer == handle)
|
||||||
cur_state.fog_lut.texture_buffer = 0;
|
fog_lut.texture_buffer = 0;
|
||||||
if (cur_state.proctex_noise_lut.texture_buffer == handle)
|
if (proctex_noise_lut.texture_buffer == handle)
|
||||||
cur_state.proctex_noise_lut.texture_buffer = 0;
|
proctex_noise_lut.texture_buffer = 0;
|
||||||
if (cur_state.proctex_color_map.texture_buffer == handle)
|
if (proctex_color_map.texture_buffer == handle)
|
||||||
cur_state.proctex_color_map.texture_buffer = 0;
|
proctex_color_map.texture_buffer = 0;
|
||||||
if (cur_state.proctex_alpha_map.texture_buffer == handle)
|
if (proctex_alpha_map.texture_buffer == handle)
|
||||||
cur_state.proctex_alpha_map.texture_buffer = 0;
|
proctex_alpha_map.texture_buffer = 0;
|
||||||
if (cur_state.proctex_lut.texture_buffer == handle)
|
if (proctex_lut.texture_buffer == handle)
|
||||||
cur_state.proctex_lut.texture_buffer = 0;
|
proctex_lut.texture_buffer = 0;
|
||||||
if (cur_state.proctex_diff_lut.texture_buffer == handle)
|
if (proctex_diff_lut.texture_buffer == handle)
|
||||||
cur_state.proctex_diff_lut.texture_buffer = 0;
|
proctex_diff_lut.texture_buffer = 0;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetSampler(GLuint handle) {
|
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||||
for (auto& unit : cur_state.texture_units) {
|
for (auto& unit : texture_units) {
|
||||||
if (unit.sampler == handle) {
|
if (unit.sampler == handle) {
|
||||||
unit.sampler = 0;
|
unit.sampler = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetProgram(GLuint handle) {
|
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
|
||||||
if (cur_state.draw.shader_program == handle) {
|
if (draw.shader_program == handle) {
|
||||||
cur_state.draw.shader_program = 0;
|
draw.shader_program = 0;
|
||||||
}
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetBuffer(GLuint handle) {
|
OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
|
||||||
if (cur_state.draw.vertex_buffer == handle) {
|
if (draw.program_pipeline == handle) {
|
||||||
cur_state.draw.vertex_buffer = 0;
|
draw.program_pipeline = 0;
|
||||||
}
|
|
||||||
if (cur_state.draw.uniform_buffer == handle) {
|
|
||||||
cur_state.draw.uniform_buffer = 0;
|
|
||||||
}
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetVertexArray(GLuint handle) {
|
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
|
||||||
if (cur_state.draw.vertex_array == handle) {
|
if (draw.vertex_buffer == handle) {
|
||||||
cur_state.draw.vertex_array = 0;
|
draw.vertex_buffer = 0;
|
||||||
}
|
}
|
||||||
|
if (draw.uniform_buffer == handle) {
|
||||||
|
draw.uniform_buffer = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLState::ResetFramebuffer(GLuint handle) {
|
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||||
if (cur_state.draw.read_framebuffer == handle) {
|
if (draw.vertex_array == handle) {
|
||||||
cur_state.draw.read_framebuffer = 0;
|
draw.vertex_array = 0;
|
||||||
}
|
|
||||||
if (cur_state.draw.draw_framebuffer == handle) {
|
|
||||||
cur_state.draw.draw_framebuffer = 0;
|
|
||||||
}
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||||
|
if (draw.read_framebuffer == handle) {
|
||||||
|
draw.read_framebuffer = 0;
|
||||||
|
}
|
||||||
|
if (draw.draw_framebuffer == handle) {
|
||||||
|
draw.draw_framebuffer = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,27 +122,44 @@ public:
|
||||||
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
|
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
|
||||||
GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
|
GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
|
||||||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||||
|
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
|
||||||
} draw;
|
} draw;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool enabled; // GL_SCISSOR_TEST
|
||||||
|
GLint x;
|
||||||
|
GLint y;
|
||||||
|
GLsizei width;
|
||||||
|
GLsizei height;
|
||||||
|
} scissor;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
GLint x;
|
||||||
|
GLint y;
|
||||||
|
GLsizei width;
|
||||||
|
GLsizei height;
|
||||||
|
} viewport;
|
||||||
|
|
||||||
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
|
||||||
|
|
||||||
OpenGLState();
|
OpenGLState();
|
||||||
|
|
||||||
/// Get the currently active OpenGL state
|
/// Get the currently active OpenGL state
|
||||||
static const OpenGLState& GetCurState() {
|
static OpenGLState GetCurState() {
|
||||||
return cur_state;
|
return cur_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply this state as the current OpenGL state
|
/// Apply this state as the current OpenGL state
|
||||||
void Apply() const;
|
void Apply() const;
|
||||||
|
|
||||||
/// Resets and unbinds any references to the given resource in the current OpenGL state
|
/// Resets any references to the given resource
|
||||||
static void ResetTexture(GLuint handle);
|
OpenGLState& ResetTexture(GLuint handle);
|
||||||
static void ResetSampler(GLuint handle);
|
OpenGLState& ResetSampler(GLuint handle);
|
||||||
static void ResetProgram(GLuint handle);
|
OpenGLState& ResetProgram(GLuint handle);
|
||||||
static void ResetBuffer(GLuint handle);
|
OpenGLState& ResetPipeline(GLuint handle);
|
||||||
static void ResetVertexArray(GLuint handle);
|
OpenGLState& ResetBuffer(GLuint handle);
|
||||||
static void ResetFramebuffer(GLuint handle);
|
OpenGLState& ResetVertexArray(GLuint handle);
|
||||||
|
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static OpenGLState cur_state;
|
static OpenGLState cur_state;
|
||||||
|
|
182
src/video_core/renderer_opengl/gl_stream_buffer.cpp
Normal file
182
src/video_core/renderer_opengl/gl_stream_buffer.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
34
src/video_core/renderer_opengl/gl_stream_buffer.h
Normal file
34
src/video_core/renderer_opengl/gl_stream_buffer.h
Normal file
|
@ -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;
|
||||||
|
};
|
|
@ -318,7 +318,7 @@ void RendererOpenGL::InitOpenGLObjects() {
|
||||||
0.0f);
|
0.0f);
|
||||||
|
|
||||||
// Link shaders and get variable locations
|
// Link shaders and get variable locations
|
||||||
shader.Create(vertex_shader, fragment_shader);
|
shader.Create(vertex_shader, nullptr, fragment_shader);
|
||||||
state.draw.shader_program = shader.handle;
|
state.draw.shader_program = shader.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||||
|
|
Loading…
Reference in a new issue