diff options
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_manager.cpp | 46 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_manager.h | 161 | 
3 files changed, 209 insertions, 0 deletions
| diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 4defb5786..281810357 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -28,6 +28,8 @@ add_library(video_core STATIC      renderer_opengl/gl_shader_decompiler.h      renderer_opengl/gl_shader_gen.cpp      renderer_opengl/gl_shader_gen.h +    renderer_opengl/gl_shader_manager.cpp +    renderer_opengl/gl_shader_manager.h      renderer_opengl/gl_shader_util.cpp      renderer_opengl/gl_shader_util.h      renderer_opengl/gl_state.cpp diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp new file mode 100644 index 000000000..0da78bc65 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -0,0 +1,46 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/kernel/process.h" +#include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" + +namespace GLShader { + +namespace Impl { +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)); +    } +} + +void SetShaderUniformBlockBindings(GLuint shader) { +    SetShaderUniformBlockBinding(shader, "vs_config", UniformBindings::VS, sizeof(VSUniformData)); +} + +void SetShaderSamplerBindings(GLuint shader) { +    OpenGLState cur_state = OpenGLState::GetCurState(); +    GLuint old_program = std::exchange(cur_state.draw.shader_program, shader); +    cur_state.Apply(); + +    // Set the texture samplers to correspond to different texture units + +    cur_state.draw.shader_program = old_program; +    cur_state.Apply(); +} + +} // namespace Impl + +void MaxwellUniformData::SetFromRegs() { +} + +} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h new file mode 100644 index 000000000..10e8b8b3a --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -0,0 +1,161 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <tuple> +#include <unordered_map> +#include <boost/functional/hash.hpp> +#include <glad/glad.h> +#include "video_core/renderer_opengl/gl_resource_manager.h" +#include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/renderer_opengl/maxwell_to_gl.h" + +namespace GLShader { + +namespace Impl { +void SetShaderUniformBlockBindings(GLuint shader); +void SetShaderSamplerBindings(GLuint shader); +} // namespace Impl + +enum class UniformBindings : GLuint { Common, VS, GS, FS }; + +/// 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 MaxwellUniformData { +    void SetFromRegs(); + +    using ConstBuffer = std::array<GLvec4, 4>; +    using Regs = Tegra::Engines::Maxwell3D::Regs; + +    alignas(16) std::array<ConstBuffer, Regs::MaxConstBuffers> const_buffers; +}; +static_assert(sizeof(MaxwellUniformData) < 16384, +              "MaxwellUniformData structure must be less than 16kb as per the OpenGL spec"); + +struct VSUniformData { +    MaxwellUniformData uniforms; +}; +static_assert(sizeof(VSUniformData) < 16384, +              "VSUniformData structure must be less than 16kb as per the OpenGL spec"); + +struct FSUniformData { +    MaxwellUniformData uniforms; +}; +static_assert(sizeof(FSUniformData) < 16384, +              "VSUniformData structure must be less than 16kb as per the OpenGL spec"); + +class OGLShaderStage { +public: +    OGLShaderStage() = default; + +    void Create(const char* source, GLenum type) { +        OGLShader shader; +        shader.Create(source, type); +        program.Create(true, shader.handle); +        Impl::SetShaderUniformBlockBindings(program.handle); +        Impl::SetShaderSamplerBindings(program.handle); +    } +    GLuint GetHandle() const { +        return program.handle; +    } + +private: +    OGLProgram program; +}; + +// TODO(wwylele): beautify this doc +// This is a shader cache designed for translating PICA shader to GLSL shader. +// The double cache is needed because diffent KeyConfigType, which includes a hash of the code +// region (including its leftover unused code) can generate the same GLSL code. +template <typename KeyConfigType, +          std::string (*CodeGenerator)(const ShaderSetup&, const KeyConfigType&), GLenum ShaderType> +class ShaderCache { +public: +    ShaderCache() = default; + +    GLuint Get(const KeyConfigType& key, const ShaderSetup& setup) { +        auto map_it = shader_map.find(key); +        if (map_it == shader_map.end()) { +            std::string program = CodeGenerator(setup, key); + +            auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{}); +            OGLShaderStage& cached_shader = iter->second; +            if (new_shader) { +                cached_shader.Create(program.c_str(), ShaderType); +            } +            shader_map[key] = &cached_shader; +            return cached_shader.GetHandle(); +        } else { +            return map_it->second->GetHandle(); +        } +    } + +private: +    std::unordered_map<KeyConfigType, OGLShaderStage*> shader_map; +    std::unordered_map<std::string, OGLShaderStage> shader_cache; +}; + +using VertexShaders = ShaderCache<MaxwellVSConfig, &GenerateVertexShader, GL_VERTEX_SHADER>; + +using FragmentShaders = ShaderCache<MaxwellFSConfig, &GenerateFragmentShader, GL_FRAGMENT_SHADER>; + +class ProgramManager { +public: +    ProgramManager() { +        pipeline.Create(); +    } + +    void UseProgrammableVertexShader(const MaxwellVSConfig& config, const ShaderSetup setup) { +        current.vs = vertex_shaders.Get(config, setup); +    } + +    void UseTrivialGeometryShader() { +        current.gs = 0; +    } + +    void UseProgrammableFragmentShader(const MaxwellFSConfig& config, const ShaderSetup setup) { +        current.fs = fragment_shaders.Get(config, setup); +    } + +    void ApplyTo(OpenGLState& state) { +        // Workaround for AMD bug +        glUseProgramStages(pipeline.handle, +                           GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, +                           0); + +        glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current.vs); +        glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, current.gs); +        glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, current.fs); +        state.draw.shader_program = 0; +        state.draw.program_pipeline = pipeline.handle; +    } + +private: +    struct ShaderTuple { +        GLuint vs = 0, gs = 0, fs = 0; +        bool operator==(const ShaderTuple& rhs) const { +            return std::tie(vs, gs, fs) == std::tie(rhs.vs, rhs.gs, rhs.fs); +        } +        struct Hash { +            std::size_t operator()(const ShaderTuple& tuple) const { +                std::size_t hash = 0; +                boost::hash_combine(hash, tuple.vs); +                boost::hash_combine(hash, tuple.gs); +                boost::hash_combine(hash, tuple.fs); +                return hash; +            } +        }; +    }; +    ShaderTuple current; +    VertexShaders vertex_shaders; +    FragmentShaders fragment_shaders; + +    std::unordered_map<ShaderTuple, OGLProgram, ShaderTuple::Hash> program_cache; +    OGLPipeline pipeline; +}; + +} // namespace GLShader | 
