forked from suyu/suyu
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:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
|
|
4
externals/glad/include/KHR/khrplatform.h
vendored
4
externals/glad/include/KHR/khrplatform.h
vendored
|
@ -26,7 +26,7 @@
|
|||
|
||||
/* 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
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
|
@ -101,6 +101,8 @@
|
|||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
|
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
|
||||
memory_manager.cpp
|
||||
memory_manager.h
|
||||
rasterizer_interface.h
|
||||
renderer_base.cpp
|
||||
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_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.h
|
||||
renderer_opengl/gl_state.cpp
|
||||
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.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)
|
||||
return;
|
||||
glDeleteTextures(1, &handle);
|
||||
OpenGLState::ResetTexture(handle);
|
||||
OpenGLState::GetCurState().ResetTexture(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteSamplers(1, &handle);
|
||||
OpenGLState::ResetSampler(handle);
|
||||
OpenGLState::GetCurState().ResetSampler(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -91,10 +91,13 @@ public:
|
|||
}
|
||||
|
||||
/// 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)
|
||||
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
|
||||
|
@ -102,7 +105,40 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -135,13 +171,46 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteBuffers(1, &handle);
|
||||
OpenGLState::ResetBuffer(handle);
|
||||
OpenGLState::GetCurState().ResetBuffer(handle).Apply();
|
||||
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 {
|
||||
public:
|
||||
OGLVertexArray() = default;
|
||||
|
@ -168,7 +237,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteVertexArrays(1, &handle);
|
||||
OpenGLState::ResetVertexArray(handle);
|
||||
OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
|
@ -201,7 +270,7 @@ public:
|
|||
if (handle == 0)
|
||||
return;
|
||||
glDeleteFramebuffers(1, &handle);
|
||||
OpenGLState::ResetFramebuffer(handle);
|
||||
OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
|
||||
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,15 +10,18 @@
|
|||
|
||||
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
|
||||
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
GLuint vertex_shader_id = vertex_shader ? glCreateShader(GL_VERTEX_SHADER) : 0;
|
||||
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;
|
||||
int info_log_length;
|
||||
|
||||
if (vertex_shader) {
|
||||
// Compile Vertex Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling vertex shader...");
|
||||
|
||||
|
@ -35,10 +38,37 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
|||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &vertex_shader_error[0]);
|
||||
} 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (geometry_shader) {
|
||||
// Compile Geometry Shader
|
||||
LOG_DEBUG(Render_OpenGL, "Compiling geometry shader...");
|
||||
|
||||
glShaderSource(geometry_shader_id, 1, &geometry_shader, nullptr);
|
||||
glCompileShader(geometry_shader_id);
|
||||
|
||||
// Check Geometry Shader
|
||||
glGetShaderiv(geometry_shader_id, GL_COMPILE_STATUS, &result);
|
||||
glGetShaderiv(geometry_shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
if (info_log_length > 1) {
|
||||
std::vector<char> geometry_shader_error(info_log_length);
|
||||
glGetShaderInfoLog(geometry_shader_id, info_log_length, nullptr,
|
||||
&geometry_shader_error[0]);
|
||||
if (result == GL_TRUE) {
|
||||
LOG_DEBUG(Render_OpenGL, "%s", &geometry_shader_error[0]);
|
||||
} else {
|
||||
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...");
|
||||
|
||||
|
@ -51,7 +81,8 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
|||
|
||||
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]);
|
||||
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 {
|
||||
|
@ -59,13 +90,31 @@ GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
|
|||
&fragment_shader_error[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Link the program
|
||||
LOG_DEBUG(Render_OpenGL, "Linking program...");
|
||||
|
||||
GLuint program_id = glCreateProgram();
|
||||
if (vertex_shader) {
|
||||
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);
|
||||
|
||||
|
@ -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 (result == GL_FALSE) {
|
||||
if (vertex_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");
|
||||
|
||||
if (vertex_shader) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
|
||||
namespace GLShader {
|
||||
|
@ -11,9 +12,12 @@ namespace GLShader {
|
|||
/**
|
||||
* 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 geometry_shader String of the GLSL geometry shader program
|
||||
* @param fragment_shader String of the GLSL fragment shader program
|
||||
* @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
|
||||
|
|
|
@ -33,7 +33,7 @@ OpenGLState::OpenGLState() {
|
|||
stencil.action_depth_pass = GL_KEEP;
|
||||
stencil.action_stencil_fail = GL_KEEP;
|
||||
|
||||
blend.enabled = false;
|
||||
blend.enabled = true;
|
||||
blend.rgb_equation = GL_FUNC_ADD;
|
||||
blend.a_equation = GL_FUNC_ADD;
|
||||
blend.src_rgb_func = GL_ONE;
|
||||
|
@ -68,6 +68,18 @@ OpenGLState::OpenGLState() {
|
|||
draw.vertex_buffer = 0;
|
||||
draw.uniform_buffer = 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 = {};
|
||||
}
|
||||
|
@ -148,9 +160,6 @@ void OpenGLState::Apply() const {
|
|||
if (blend.enabled != cur_state.blend.enabled) {
|
||||
if (blend.enabled) {
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
cur_state.logic_op = GL_COPY;
|
||||
glLogicOp(cur_state.logic_op);
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
} else {
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -196,7 +205,7 @@ void OpenGLState::Apply() const {
|
|||
// Lighting LUTs
|
||||
if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) {
|
||||
glActiveTexture(TextureUnits::LightingLUT.Enum());
|
||||
glBindTexture(GL_TEXTURE_BUFFER, cur_state.lighting_lut.texture_buffer);
|
||||
glBindTexture(GL_TEXTURE_BUFFER, lighting_lut.texture_buffer);
|
||||
}
|
||||
|
||||
// Fog LUT
|
||||
|
@ -263,6 +272,31 @@ void OpenGLState::Apply() const {
|
|||
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
|
||||
for (size_t i = 0; i < clip_distance.size(); ++i) {
|
||||
if (clip_distance[i] != cur_state.clip_distance[i]) {
|
||||
|
@ -277,62 +311,75 @@ void OpenGLState::Apply() const {
|
|||
cur_state = *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
OpenGLState& OpenGLState::ResetTexture(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.texture_2d == handle) {
|
||||
unit.texture_2d = 0;
|
||||
}
|
||||
}
|
||||
if (cur_state.lighting_lut.texture_buffer == handle)
|
||||
cur_state.lighting_lut.texture_buffer = 0;
|
||||
if (cur_state.fog_lut.texture_buffer == handle)
|
||||
cur_state.fog_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_noise_lut.texture_buffer == handle)
|
||||
cur_state.proctex_noise_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_color_map.texture_buffer == handle)
|
||||
cur_state.proctex_color_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_alpha_map.texture_buffer == handle)
|
||||
cur_state.proctex_alpha_map.texture_buffer = 0;
|
||||
if (cur_state.proctex_lut.texture_buffer == handle)
|
||||
cur_state.proctex_lut.texture_buffer = 0;
|
||||
if (cur_state.proctex_diff_lut.texture_buffer == handle)
|
||||
cur_state.proctex_diff_lut.texture_buffer = 0;
|
||||
if (lighting_lut.texture_buffer == handle)
|
||||
lighting_lut.texture_buffer = 0;
|
||||
if (fog_lut.texture_buffer == handle)
|
||||
fog_lut.texture_buffer = 0;
|
||||
if (proctex_noise_lut.texture_buffer == handle)
|
||||
proctex_noise_lut.texture_buffer = 0;
|
||||
if (proctex_color_map.texture_buffer == handle)
|
||||
proctex_color_map.texture_buffer = 0;
|
||||
if (proctex_alpha_map.texture_buffer == handle)
|
||||
proctex_alpha_map.texture_buffer = 0;
|
||||
if (proctex_lut.texture_buffer == handle)
|
||||
proctex_lut.texture_buffer = 0;
|
||||
if (proctex_diff_lut.texture_buffer == handle)
|
||||
proctex_diff_lut.texture_buffer = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : cur_state.texture_units) {
|
||||
OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
|
||||
for (auto& unit : texture_units) {
|
||||
if (unit.sampler == handle) {
|
||||
unit.sampler = 0;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (cur_state.draw.shader_program == handle) {
|
||||
cur_state.draw.shader_program = 0;
|
||||
OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
|
||||
if (draw.shader_program == handle) {
|
||||
draw.shader_program = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (cur_state.draw.vertex_buffer == handle) {
|
||||
cur_state.draw.vertex_buffer = 0;
|
||||
}
|
||||
if (cur_state.draw.uniform_buffer == handle) {
|
||||
cur_state.draw.uniform_buffer = 0;
|
||||
OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
|
||||
if (draw.program_pipeline == handle) {
|
||||
draw.program_pipeline = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (cur_state.draw.vertex_array == handle) {
|
||||
cur_state.draw.vertex_array = 0;
|
||||
OpenGLState& OpenGLState::ResetBuffer(GLuint handle) {
|
||||
if (draw.vertex_buffer == handle) {
|
||||
draw.vertex_buffer = 0;
|
||||
}
|
||||
if (draw.uniform_buffer == handle) {
|
||||
draw.uniform_buffer = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void OpenGLState::ResetFramebuffer(GLuint handle) {
|
||||
if (cur_state.draw.read_framebuffer == handle) {
|
||||
cur_state.draw.read_framebuffer = 0;
|
||||
}
|
||||
if (cur_state.draw.draw_framebuffer == handle) {
|
||||
cur_state.draw.draw_framebuffer = 0;
|
||||
OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
|
||||
if (draw.vertex_array == handle) {
|
||||
draw.vertex_array = 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 uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
|
||||
GLuint shader_program; // GL_CURRENT_PROGRAM
|
||||
GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING
|
||||
} 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
|
||||
|
||||
OpenGLState();
|
||||
|
||||
/// Get the currently active OpenGL state
|
||||
static const OpenGLState& GetCurState() {
|
||||
static OpenGLState GetCurState() {
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
/// Apply this state as the current OpenGL state
|
||||
void Apply() const;
|
||||
|
||||
/// Resets and unbinds any references to the given resource in the current OpenGL state
|
||||
static void ResetTexture(GLuint handle);
|
||||
static void ResetSampler(GLuint handle);
|
||||
static void ResetProgram(GLuint handle);
|
||||
static void ResetBuffer(GLuint handle);
|
||||
static void ResetVertexArray(GLuint handle);
|
||||
static void ResetFramebuffer(GLuint handle);
|
||||
/// Resets any references to the given resource
|
||||
OpenGLState& ResetTexture(GLuint handle);
|
||||
OpenGLState& ResetSampler(GLuint handle);
|
||||
OpenGLState& ResetProgram(GLuint handle);
|
||||
OpenGLState& ResetPipeline(GLuint handle);
|
||||
OpenGLState& ResetBuffer(GLuint handle);
|
||||
OpenGLState& ResetVertexArray(GLuint handle);
|
||||
OpenGLState& ResetFramebuffer(GLuint handle);
|
||||
|
||||
private:
|
||||
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);
|
||||
|
||||
// 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.Apply();
|
||||
uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix");
|
||||
|
|
Loading…
Reference in a new issue