mirror of
				https://github.com/yuzu-emu/yuzu.git
				synced 2025-11-04 09:53:43 +00:00 
			
		
		
		
	renderer_opengl: Refactor shader generation/caching to be more organized + various cleanups.
This commit is contained in:
		
							parent
							
								
									3c057bd3d8
								
							
						
					
					
						commit
						c86b9d4242
					
				@ -4,6 +4,9 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <functional>
 | 
			
		||||
 | 
			
		||||
#include "common_types.h"
 | 
			
		||||
 | 
			
		||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 | 
			
		||||
@ -95,3 +98,18 @@ inline u64 _rotr64(u64 x, unsigned int shift){
 | 
			
		||||
// This function might change the error code.
 | 
			
		||||
// Defined in Misc.cpp.
 | 
			
		||||
const char* GetLastErrorMsg();
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline std::size_t hash(const T& o) {
 | 
			
		||||
    return std::hash<T>()(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline std::size_t combine_hash(const T& o) {
 | 
			
		||||
    return hash(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename... Args>
 | 
			
		||||
inline std::size_t combine_hash(const T& o, const Args&... args) {
 | 
			
		||||
    return hash(o) * 3 + combine_hash(args...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
set(SRCS
 | 
			
		||||
            renderer_opengl/gl_rasterizer.cpp
 | 
			
		||||
            renderer_opengl/gl_rasterizer_cache.cpp
 | 
			
		||||
            renderer_opengl/gl_shader_gen.cpp
 | 
			
		||||
            renderer_opengl/gl_shader_util.cpp
 | 
			
		||||
            renderer_opengl/gl_state.cpp
 | 
			
		||||
            renderer_opengl/renderer_opengl.cpp
 | 
			
		||||
@ -21,8 +22,8 @@ set(HEADERS
 | 
			
		||||
            renderer_opengl/gl_rasterizer.h
 | 
			
		||||
            renderer_opengl/gl_rasterizer_cache.h
 | 
			
		||||
            renderer_opengl/gl_resource_manager.h
 | 
			
		||||
            renderer_opengl/gl_shader_gen.h
 | 
			
		||||
            renderer_opengl/gl_shader_util.h
 | 
			
		||||
            renderer_opengl/gl_shaders.h
 | 
			
		||||
            renderer_opengl/gl_state.h
 | 
			
		||||
            renderer_opengl/pica_to_gl.h
 | 
			
		||||
            renderer_opengl/renderer_opengl.h
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
 | 
			
		||||
#include "common/color.h"
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/make_unique.h"
 | 
			
		||||
#include "common/math_util.h"
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
#include "common/profiler.h"
 | 
			
		||||
@ -20,7 +21,7 @@
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
#include "video_core/utils.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shaders.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shader_util.h"
 | 
			
		||||
#include "video_core/renderer_opengl/pica_to_gl.h"
 | 
			
		||||
 | 
			
		||||
@ -54,20 +55,20 @@ void RasterizerOpenGL::InitObjects() {
 | 
			
		||||
    state.Apply();
 | 
			
		||||
 | 
			
		||||
    // Set vertex attributes
 | 
			
		||||
    glVertexAttribPointer(ShaderUtil::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
 | 
			
		||||
    glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_POSITION);
 | 
			
		||||
    glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
 | 
			
		||||
    glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION);
 | 
			
		||||
 | 
			
		||||
    glVertexAttribPointer(ShaderUtil::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
 | 
			
		||||
    glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_COLOR);
 | 
			
		||||
    glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
 | 
			
		||||
    glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR);
 | 
			
		||||
 | 
			
		||||
    glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
 | 
			
		||||
    glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
 | 
			
		||||
    glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
 | 
			
		||||
    glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS);
 | 
			
		||||
    glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1);
 | 
			
		||||
    glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2);
 | 
			
		||||
    glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
 | 
			
		||||
    glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
 | 
			
		||||
    glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
 | 
			
		||||
    glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 0);
 | 
			
		||||
    glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 1);
 | 
			
		||||
    glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 2);
 | 
			
		||||
 | 
			
		||||
    RegenerateShaders();
 | 
			
		||||
    SetShader();
 | 
			
		||||
 | 
			
		||||
    // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
 | 
			
		||||
    fb_color_texture.texture.Create();
 | 
			
		||||
@ -117,8 +118,6 @@ void RasterizerOpenGL::InitObjects() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::Reset() {
 | 
			
		||||
    const auto& regs = Pica::g_state.regs;
 | 
			
		||||
 | 
			
		||||
    SyncCullMode();
 | 
			
		||||
    SyncBlendEnabled();
 | 
			
		||||
    SyncBlendFuncs();
 | 
			
		||||
@ -127,7 +126,7 @@ void RasterizerOpenGL::Reset() {
 | 
			
		||||
    SyncStencilTest();
 | 
			
		||||
    SyncDepthTest();
 | 
			
		||||
 | 
			
		||||
    RegenerateShaders();
 | 
			
		||||
    SetShader();
 | 
			
		||||
 | 
			
		||||
    res_cache.FullFlush();
 | 
			
		||||
}
 | 
			
		||||
@ -140,70 +139,12 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
 | 
			
		||||
    vertex_batch.emplace_back(v2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ShaderCache {
 | 
			
		||||
extern std::string GenerateFragmentShader(const ShaderCacheKey& config);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::RegenerateShaders() {
 | 
			
		||||
    ShaderCacheKey config = ShaderCacheKey::CurrentShaderConfig();
 | 
			
		||||
 | 
			
		||||
    auto cached_shader = shader_cache.find(config);
 | 
			
		||||
    if (cached_shader != shader_cache.end()) {
 | 
			
		||||
        current_shader = &cached_shader->second;
 | 
			
		||||
        state.draw.shader_program = current_shader->shader.handle;
 | 
			
		||||
        state.Apply();
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Creating new shader: %08X", hash(config));
 | 
			
		||||
 | 
			
		||||
        TEVShader shader;
 | 
			
		||||
 | 
			
		||||
        std::string fragShader = ShaderCache::GenerateFragmentShader(config);
 | 
			
		||||
        shader.shader.Create(GLShaders::g_vertex_shader_hw, fragShader.c_str());
 | 
			
		||||
 | 
			
		||||
        shader.uniform_alphatest_ref = glGetUniformLocation(shader.shader.handle, "alphatest_ref");
 | 
			
		||||
        shader.uniform_tex = glGetUniformLocation(shader.shader.handle, "tex");
 | 
			
		||||
        shader.uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.shader.handle, "tev_combiner_buffer_color");
 | 
			
		||||
        shader.uniform_tev_const_colors = glGetUniformLocation(shader.shader.handle, "const_color");
 | 
			
		||||
 | 
			
		||||
        current_shader = &shader_cache.emplace(config, std::move(shader)).first->second;
 | 
			
		||||
 | 
			
		||||
        state.draw.shader_program = current_shader->shader.handle;
 | 
			
		||||
        state.Apply();
 | 
			
		||||
 | 
			
		||||
        // Set the texture samplers to correspond to different texture units
 | 
			
		||||
        if (shader.uniform_tex != -1) {
 | 
			
		||||
            glUniform1i(shader.uniform_tex, 0);
 | 
			
		||||
            glUniform1i(shader.uniform_tex + 1, 1);
 | 
			
		||||
            glUniform1i(shader.uniform_tex + 2, 2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sync alpha reference
 | 
			
		||||
    if (current_shader->uniform_alphatest_ref != -1)
 | 
			
		||||
        glUniform1i(current_shader->uniform_alphatest_ref, Pica::g_state.regs.output_merger.alpha_test.ref);
 | 
			
		||||
 | 
			
		||||
    // Sync combiner buffer color
 | 
			
		||||
    if (current_shader->uniform_tev_combiner_buffer_color != -1) {
 | 
			
		||||
        auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
 | 
			
		||||
        glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Sync TEV const colors
 | 
			
		||||
    if (current_shader->uniform_tev_const_colors != -1) {
 | 
			
		||||
        auto& tev_stages = Pica::g_state.regs.GetTevStages();
 | 
			
		||||
        for (int tev_index = 0; tev_index < tev_stages.size(); ++tev_index) {
 | 
			
		||||
            auto const_color = PicaToGL::ColorRGBA8(tev_stages[tev_index].const_color);
 | 
			
		||||
            glUniform4fv(current_shader->uniform_tev_const_colors + tev_index, 1, const_color.data());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::DrawTriangles() {
 | 
			
		||||
    SyncFramebuffer();
 | 
			
		||||
    SyncDrawState();
 | 
			
		||||
 | 
			
		||||
    if (state.draw.shader_dirty) {
 | 
			
		||||
        RegenerateShaders();
 | 
			
		||||
        SetShader();
 | 
			
		||||
        state.draw.shader_dirty = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -519,6 +460,48 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::
 | 
			
		||||
    state.Apply();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::SetShader() {
 | 
			
		||||
    ShaderCacheKey config = ShaderCacheKey::CurrentConfig();
 | 
			
		||||
 | 
			
		||||
    // Find (or generate) the GLSL shader for the current TEV state
 | 
			
		||||
    auto cached_shader = shader_cache.find(config);
 | 
			
		||||
    if (cached_shader != shader_cache.end()) {
 | 
			
		||||
        current_shader = cached_shader->second.get();
 | 
			
		||||
 | 
			
		||||
        state.draw.shader_program = current_shader->shader.handle;
 | 
			
		||||
        state.Apply();
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_DEBUG(Render_OpenGL, "Creating new shader: %08X", hash(config));
 | 
			
		||||
 | 
			
		||||
        std::unique_ptr<TEVShader> shader = Common::make_unique<TEVShader>();
 | 
			
		||||
 | 
			
		||||
        shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
 | 
			
		||||
        shader->uniform_alphatest_ref = glGetUniformLocation(shader->shader.handle, "alphatest_ref");
 | 
			
		||||
        shader->uniform_tex = glGetUniformLocation(shader->shader.handle, "tex");
 | 
			
		||||
        shader->uniform_tev_combiner_buffer_color = glGetUniformLocation(shader->shader.handle, "tev_combiner_buffer_color");
 | 
			
		||||
        shader->uniform_tev_const_colors = glGetUniformLocation(shader->shader.handle, "const_color");
 | 
			
		||||
 | 
			
		||||
        state.draw.shader_program = shader->shader.handle;
 | 
			
		||||
        state.Apply();
 | 
			
		||||
 | 
			
		||||
        // Set the texture samplers to correspond to different texture units
 | 
			
		||||
        if (shader->uniform_tex != -1) {
 | 
			
		||||
            glUniform1i(shader->uniform_tex, 0);
 | 
			
		||||
            glUniform1i(shader->uniform_tex + 1, 1);
 | 
			
		||||
            glUniform1i(shader->uniform_tex + 2, 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update uniforms
 | 
			
		||||
    SyncAlphaTest();
 | 
			
		||||
    SyncCombinerColor();
 | 
			
		||||
    auto& tev_stages = Pica::g_state.regs.GetTevStages();
 | 
			
		||||
    for (int index = 0; index < tev_stages.size(); ++index)
 | 
			
		||||
        SyncTevConstColor(index, tev_stages[index]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RasterizerOpenGL::SyncFramebuffer() {
 | 
			
		||||
    const auto& regs = Pica::g_state.regs;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
@ -15,21 +17,6 @@
 | 
			
		||||
#include "video_core/renderer_opengl/gl_state.h"
 | 
			
		||||
#include "video_core/shader/shader_interpreter.h"
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline size_t hash(const T& o) {
 | 
			
		||||
    return std::hash<T>()(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline size_t combine_hash(const T& o) {
 | 
			
		||||
    return hash(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename... Args>
 | 
			
		||||
inline size_t combine_hash(const T& o, const Args&... args) {
 | 
			
		||||
    return hash(o) * 3 + combine_hash(args...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ShaderCacheKey {
 | 
			
		||||
    using Regs = Pica::Regs;
 | 
			
		||||
 | 
			
		||||
@ -49,7 +36,7 @@ struct ShaderCacheKey {
 | 
			
		||||
        return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ShaderCacheKey CurrentShaderConfig() {
 | 
			
		||||
    static ShaderCacheKey CurrentConfig() {
 | 
			
		||||
        const auto& regs = Pica::g_state.regs;
 | 
			
		||||
        ShaderCacheKey config;
 | 
			
		||||
 | 
			
		||||
@ -94,8 +81,14 @@ struct ShaderCacheKey {
 | 
			
		||||
 | 
			
		||||
namespace std {
 | 
			
		||||
 | 
			
		||||
template<> struct hash<::Pica::Regs::CompareFunc> {
 | 
			
		||||
    std::size_t operator()(const ::Pica::Regs::CompareFunc& o) {
 | 
			
		||||
        return ::hash((unsigned)o);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<> struct hash<::Pica::Regs::TevStageConfig> {
 | 
			
		||||
    size_t operator()(const ::Pica::Regs::TevStageConfig& o) {
 | 
			
		||||
    std::size_t operator()(const ::Pica::Regs::TevStageConfig& o) {
 | 
			
		||||
        return ::combine_hash(
 | 
			
		||||
            ::hash(o.source_raw), ::hash(o.modifier_raw),
 | 
			
		||||
            ::hash(o.op_raw), ::hash(o.scale_raw));
 | 
			
		||||
@ -103,13 +96,14 @@ template<> struct hash<::Pica::Regs::TevStageConfig> {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<> struct hash<::ShaderCacheKey> {
 | 
			
		||||
    size_t operator()(const ::ShaderCacheKey& o) const {
 | 
			
		||||
    std::size_t operator()(const ::ShaderCacheKey& o) const {
 | 
			
		||||
        return ::combine_hash(o.alpha_test_func, o.combiner_buffer_input,
 | 
			
		||||
            o.tev_stages[0], o.tev_stages[1], o.tev_stages[2],
 | 
			
		||||
            o.tev_stages[3], o.tev_stages[4], o.tev_stages[5]);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace std
 | 
			
		||||
 | 
			
		||||
class RasterizerOpenGL : public HWRasterizer {
 | 
			
		||||
public:
 | 
			
		||||
@ -131,8 +125,6 @@ public:
 | 
			
		||||
    /// Draw the current batch of triangles
 | 
			
		||||
    void DrawTriangles() override;
 | 
			
		||||
 | 
			
		||||
    void RegenerateShaders();
 | 
			
		||||
 | 
			
		||||
    /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
 | 
			
		||||
    void CommitFramebuffer() override;
 | 
			
		||||
 | 
			
		||||
@ -245,6 +237,9 @@ private:
 | 
			
		||||
    /// Reconfigure the OpenGL depth texture to use the given format and dimensions
 | 
			
		||||
    void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
 | 
			
		||||
 | 
			
		||||
    /// Sets the OpenGL shader in accordance with the current PICA register state
 | 
			
		||||
    void SetShader();
 | 
			
		||||
 | 
			
		||||
    /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
 | 
			
		||||
    void SyncFramebuffer();
 | 
			
		||||
 | 
			
		||||
@ -315,8 +310,8 @@ private:
 | 
			
		||||
    TextureInfo fb_color_texture;
 | 
			
		||||
    DepthTextureInfo fb_depth_texture;
 | 
			
		||||
 | 
			
		||||
    std::unordered_map<ShaderCacheKey, TEVShader> shader_cache;
 | 
			
		||||
    TEVShader* current_shader = nullptr;
 | 
			
		||||
    std::unordered_map<ShaderCacheKey, std::unique_ptr<TEVShader>> shader_cache;
 | 
			
		||||
    const TEVShader* current_shader = nullptr;
 | 
			
		||||
 | 
			
		||||
    OGLVertexArray vertex_array;
 | 
			
		||||
    OGLBuffer vertex_buffer;
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,7 @@ public:
 | 
			
		||||
    /// Creates a new internal OpenGL resource and stores the handle
 | 
			
		||||
    void Create(const char* vert_shader, const char* frag_shader) {
 | 
			
		||||
        if (handle != 0) return;
 | 
			
		||||
        handle = ShaderUtil::LoadShaders(vert_shader, frag_shader);
 | 
			
		||||
        handle = GLShader::LoadProgram(vert_shader, frag_shader);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Deletes the internal OpenGL resource
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										371
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										371
									
								
								src/video_core/renderer_opengl/gl_shader_gen.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,371 @@
 | 
			
		||||
// Copyright 2015 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
 | 
			
		||||
 | 
			
		||||
namespace GLShader {
 | 
			
		||||
 | 
			
		||||
static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
 | 
			
		||||
    return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace &&
 | 
			
		||||
        stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace &&
 | 
			
		||||
        stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
 | 
			
		||||
        stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
 | 
			
		||||
        stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor &&
 | 
			
		||||
        stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha &&
 | 
			
		||||
        stage.GetColorMultiplier() == 1 &&
 | 
			
		||||
        stage.GetAlphaMultiplier() == 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using Source = Pica::Regs::TevStageConfig::Source;
 | 
			
		||||
    switch (source) {
 | 
			
		||||
    case Source::PrimaryColor:
 | 
			
		||||
        shader += "o[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::PrimaryFragmentColor:
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use primary_color
 | 
			
		||||
        shader += "o[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::SecondaryFragmentColor:
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use zero
 | 
			
		||||
        shader += "vec4(0.0, 0.0, 0.0, 0.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture0:
 | 
			
		||||
        shader += "texture(tex[0], o[3].xy)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture1:
 | 
			
		||||
        shader += "texture(tex[1], o[3].zw)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture2: // TODO: Unverified
 | 
			
		||||
        shader += "texture(tex[2], o[5].zw)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::PreviousBuffer:
 | 
			
		||||
        shader += "g_combiner_buffer";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Constant:
 | 
			
		||||
        shader += "const_color[" + index_name + "]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Previous:
 | 
			
		||||
        shader += "g_last_tex_env_out";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "vec4(0.0)";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier,
 | 
			
		||||
        Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
 | 
			
		||||
    switch (modifier) {
 | 
			
		||||
    case ColorModifier::SourceColor:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".rgb";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::OneMinusSourceColor:
 | 
			
		||||
        shader += "vec3(1.0) - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".rgb";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::SourceAlpha:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".aaa";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::OneMinusSourceAlpha:
 | 
			
		||||
        shader += "vec3(1.0) - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".aaa";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::SourceRed:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".rrr";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::OneMinusSourceRed:
 | 
			
		||||
        shader += "vec3(1.0) - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".rrr";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::SourceGreen:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".ggg";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::OneMinusSourceGreen:
 | 
			
		||||
        shader += "vec3(1.0) - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".ggg";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::SourceBlue:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".bbb";
 | 
			
		||||
        break;
 | 
			
		||||
    case ColorModifier::OneMinusSourceBlue:
 | 
			
		||||
        shader += "vec3(1.0) - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".bbb";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "vec3(0.0)";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier,
 | 
			
		||||
        Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
 | 
			
		||||
    switch (modifier) {
 | 
			
		||||
    case AlphaModifier::SourceAlpha:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".a";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::OneMinusSourceAlpha:
 | 
			
		||||
        shader += "1.0 - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".a";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::SourceRed:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".r";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::OneMinusSourceRed:
 | 
			
		||||
        shader += "1.0 - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".r";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::SourceGreen:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".g";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::OneMinusSourceGreen:
 | 
			
		||||
        shader += "1.0 - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".g";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::SourceBlue:
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".b";
 | 
			
		||||
        break;
 | 
			
		||||
    case AlphaModifier::OneMinusSourceBlue:
 | 
			
		||||
        shader += "1.0 - ";
 | 
			
		||||
        AppendSource(shader, source, index_name);
 | 
			
		||||
        shader += ".b";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "vec3(0.0)";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation,
 | 
			
		||||
        const std::string& variable_name) {
 | 
			
		||||
    using Operation = Pica::Regs::TevStageConfig::Operation;
 | 
			
		||||
 | 
			
		||||
    switch (operation) {
 | 
			
		||||
    case Operation::Replace:
 | 
			
		||||
        shader += variable_name + "[0]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Modulate:
 | 
			
		||||
        shader += variable_name + "[0] * " + variable_name + "[1]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Add:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::AddSigned:
 | 
			
		||||
        shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Lerp:
 | 
			
		||||
        shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Subtract:
 | 
			
		||||
        shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::MultiplyThenAdd:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::AddThenMultiply:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "vec3(0.0)";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation,
 | 
			
		||||
        const std::string& variable_name) {
 | 
			
		||||
    using Operation = Pica::Regs::TevStageConfig::Operation;
 | 
			
		||||
    switch (operation) {
 | 
			
		||||
    case Operation::Replace:
 | 
			
		||||
        shader += variable_name + "[0]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Modulate:
 | 
			
		||||
        shader += variable_name + "[0] * " + variable_name + "[1]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Add:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::AddSigned:
 | 
			
		||||
        shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Lerp:
 | 
			
		||||
        shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::Subtract:
 | 
			
		||||
        shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::MultiplyThenAdd:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Operation::AddThenMultiply:
 | 
			
		||||
        shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "0.0";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) {
 | 
			
		||||
    using CompareFunc = Pica::Regs::CompareFunc;
 | 
			
		||||
    switch (func) {
 | 
			
		||||
    case CompareFunc::Never:
 | 
			
		||||
        shader += "true";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::Always:
 | 
			
		||||
        shader += "false";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::Equal:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::NotEqual:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::LessThan:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::LessThanOrEqual:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::GreaterThan:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    case CompareFunc::GreaterThanOrEqual:
 | 
			
		||||
        shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "false";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GenerateFragmentShader(const ShaderCacheKey& config) {
 | 
			
		||||
    std::string shader = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
#define NUM_VTX_ATTR 7
 | 
			
		||||
#define NUM_TEV_STAGES 6
 | 
			
		||||
 | 
			
		||||
in vec4 o[NUM_VTX_ATTR];
 | 
			
		||||
out vec4 color;
 | 
			
		||||
 | 
			
		||||
uniform int alphatest_ref;
 | 
			
		||||
uniform vec4 const_color[NUM_TEV_STAGES];
 | 
			
		||||
uniform sampler2D tex[3];
 | 
			
		||||
 | 
			
		||||
uniform vec4 tev_combiner_buffer_color;
 | 
			
		||||
 | 
			
		||||
void main(void) {
 | 
			
		||||
vec4 g_combiner_buffer = tev_combiner_buffer_color;
 | 
			
		||||
vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
    // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | 
			
		||||
    if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) {
 | 
			
		||||
        shader += "discard;";
 | 
			
		||||
        return shader;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto& tev_stages = config.tev_stages;
 | 
			
		||||
    for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
 | 
			
		||||
        auto& tev_stage = tev_stages[tev_stage_index];
 | 
			
		||||
        if (!IsPassThroughTevStage(tev_stage)) {
 | 
			
		||||
            std::string index_name = std::to_string(tev_stage_index);
 | 
			
		||||
 | 
			
		||||
            shader += "vec3 color_results_" + index_name + "[3] = vec3[3](";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name);
 | 
			
		||||
            shader += ");\n";
 | 
			
		||||
 | 
			
		||||
            shader += "vec3 color_output_" + index_name + " = ";
 | 
			
		||||
            AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name);
 | 
			
		||||
            shader += ";\n";
 | 
			
		||||
 | 
			
		||||
            shader += "float alpha_results_" + index_name + "[3] = float[3](";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name);
 | 
			
		||||
            shader += ");\n";
 | 
			
		||||
 | 
			
		||||
            shader += "float alpha_output_" + index_name + " = ";
 | 
			
		||||
            AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name);
 | 
			
		||||
            shader += ";\n";
 | 
			
		||||
 | 
			
		||||
            shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index))
 | 
			
		||||
            shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n";
 | 
			
		||||
 | 
			
		||||
        if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index))
 | 
			
		||||
            shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) {
 | 
			
		||||
        shader += "if (";
 | 
			
		||||
        AppendAlphaTestCondition(shader, config.alpha_test_func);
 | 
			
		||||
        shader += ") {\n discard;\n }\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shader += "color = g_last_tex_env_out;\n}";
 | 
			
		||||
    return shader;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GenerateVertexShader() {
 | 
			
		||||
    static const std::string shader_str = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
#define NUM_VTX_ATTR 7
 | 
			
		||||
 | 
			
		||||
in vec4 vert_position;
 | 
			
		||||
in vec4 vert_color;
 | 
			
		||||
in vec2 vert_texcoords0;
 | 
			
		||||
in vec2 vert_texcoords1;
 | 
			
		||||
in vec2 vert_texcoords2;
 | 
			
		||||
 | 
			
		||||
out vec4 o[NUM_VTX_ATTR];
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    o[2] = vert_color;
 | 
			
		||||
    o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy);
 | 
			
		||||
    o[5] = vec4(0.0, 0.0, vert_texcoords2.xy);
 | 
			
		||||
 | 
			
		||||
    gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
    return shader_str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace GLShaderGen
 | 
			
		||||
							
								
								
									
										17
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/video_core/renderer_opengl/gl_shader_gen.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
// Copyright 2015 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
			
		||||
 | 
			
		||||
namespace GLShader {
 | 
			
		||||
 | 
			
		||||
std::string GenerateVertexShader();
 | 
			
		||||
 | 
			
		||||
std::string GenerateFragmentShader(const ShaderCacheKey& config);
 | 
			
		||||
 | 
			
		||||
} // namespace GLShader
 | 
			
		||||
@ -2,22 +2,15 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "gl_shader_util.h"
 | 
			
		||||
#include "gl_rasterizer.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
#include "video_core/pica.h"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shader_util.h"
 | 
			
		||||
 | 
			
		||||
namespace ShaderUtil {
 | 
			
		||||
namespace GLShader {
 | 
			
		||||
 | 
			
		||||
GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
 | 
			
		||||
GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
 | 
			
		||||
 | 
			
		||||
    // Create the shaders
 | 
			
		||||
    GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
@ -101,339 +94,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
 | 
			
		||||
    return program_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ShaderCache
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
 | 
			
		||||
    return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace &&
 | 
			
		||||
            stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace &&
 | 
			
		||||
            stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
 | 
			
		||||
            stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
 | 
			
		||||
            stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor &&
 | 
			
		||||
            stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha &&
 | 
			
		||||
            stage.GetColorMultiplier() == 1 &&
 | 
			
		||||
            stage.GetAlphaMultiplier() == 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using Source = Pica::Regs::TevStageConfig::Source;
 | 
			
		||||
    switch (source) {
 | 
			
		||||
    case Source::PrimaryColor:
 | 
			
		||||
        shader += "o[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::PrimaryFragmentColor:
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use primary_color
 | 
			
		||||
        shader += "o[2]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::SecondaryFragmentColor:
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use zero
 | 
			
		||||
        shader += "vec4(0.0, 0.0, 0.0, 0.0)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture0:
 | 
			
		||||
        shader += "texture(tex[0], o[3].xy)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture1:
 | 
			
		||||
        shader += "texture(tex[1], o[3].zw)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Texture2: // TODO: Unverified
 | 
			
		||||
        shader += "texture(tex[2], o[5].zw)";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::PreviousBuffer:
 | 
			
		||||
        shader += "g_combiner_buffer";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Constant:
 | 
			
		||||
        shader += "const_color[" + index_name + "]";
 | 
			
		||||
        break;
 | 
			
		||||
    case Source::Previous:
 | 
			
		||||
        shader += "g_last_tex_env_out";
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        shader += "vec4(0.0)";
 | 
			
		||||
        LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
 | 
			
		||||
    switch (modifier) {
 | 
			
		||||
        case ColorModifier::SourceColor:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".rgb";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::OneMinusSourceColor:
 | 
			
		||||
            shader += "vec3(1.0) - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".rgb";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::SourceAlpha:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".aaa";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::OneMinusSourceAlpha:
 | 
			
		||||
            shader += "vec3(1.0) - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".aaa";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::SourceRed:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".rrr";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::OneMinusSourceRed:
 | 
			
		||||
            shader += "vec3(1.0) - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".rrr";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::SourceGreen:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".ggg";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::OneMinusSourceGreen:
 | 
			
		||||
            shader += "vec3(1.0) - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".ggg";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::SourceBlue:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".bbb";
 | 
			
		||||
            break;
 | 
			
		||||
        case ColorModifier::OneMinusSourceBlue:
 | 
			
		||||
            shader += "vec3(1.0) - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".bbb";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            shader += "vec3(0.0)";
 | 
			
		||||
            LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) {
 | 
			
		||||
    using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
 | 
			
		||||
    switch (modifier) {
 | 
			
		||||
        case AlphaModifier::SourceAlpha:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".a";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::OneMinusSourceAlpha:
 | 
			
		||||
            shader += "1.0 - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".a";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::SourceRed:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".r";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::OneMinusSourceRed:
 | 
			
		||||
            shader += "1.0 - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".r";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::SourceGreen:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".g";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::OneMinusSourceGreen:
 | 
			
		||||
            shader += "1.0 - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".g";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::SourceBlue:
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".b";
 | 
			
		||||
            break;
 | 
			
		||||
        case AlphaModifier::OneMinusSourceBlue:
 | 
			
		||||
            shader += "1.0 - ";
 | 
			
		||||
            AppendSource(shader, source, index_name);
 | 
			
		||||
            shader += ".b";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            shader += "vec3(0.0)";
 | 
			
		||||
            LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) {
 | 
			
		||||
    using Operation = Pica::Regs::TevStageConfig::Operation;
 | 
			
		||||
 | 
			
		||||
    switch (operation) {
 | 
			
		||||
        case Operation::Replace:
 | 
			
		||||
            shader += variable_name + "[0]";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Modulate:
 | 
			
		||||
            shader += variable_name + "[0] * " + variable_name + "[1]";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Add:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::AddSigned:
 | 
			
		||||
            shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Lerp:
 | 
			
		||||
            shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Subtract:
 | 
			
		||||
            shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::MultiplyThenAdd:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::AddThenMultiply:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            shader += "vec3(0.0)";
 | 
			
		||||
            LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) {
 | 
			
		||||
    using Operation = Pica::Regs::TevStageConfig::Operation;
 | 
			
		||||
    switch (operation) {
 | 
			
		||||
        case Operation::Replace:
 | 
			
		||||
            shader += variable_name + "[0]";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Modulate:
 | 
			
		||||
            shader += variable_name + "[0] * " + variable_name + "[1]";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Add:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::AddSigned:
 | 
			
		||||
            shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Lerp:
 | 
			
		||||
            shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::Subtract:
 | 
			
		||||
            shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::MultiplyThenAdd:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)";
 | 
			
		||||
            break;
 | 
			
		||||
        case Operation::AddThenMultiply:
 | 
			
		||||
            shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            shader += "0.0";
 | 
			
		||||
            LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) {
 | 
			
		||||
    using CompareFunc = Pica::Regs::CompareFunc;
 | 
			
		||||
    switch (func) {
 | 
			
		||||
        case CompareFunc::Never:
 | 
			
		||||
            shader += "true";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::Always:
 | 
			
		||||
            shader += "false";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::Equal:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::NotEqual:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::LessThan:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::LessThanOrEqual:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::GreaterThan:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        case CompareFunc::GreaterThanOrEqual:
 | 
			
		||||
            shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            shader += "false";
 | 
			
		||||
            LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GenerateFragmentShader(const ShaderCacheKey& config) {
 | 
			
		||||
    std::string shader = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
#define NUM_VTX_ATTR 7
 | 
			
		||||
#define NUM_TEV_STAGES 6
 | 
			
		||||
 | 
			
		||||
in vec4 o[NUM_VTX_ATTR];
 | 
			
		||||
out vec4 color;
 | 
			
		||||
 | 
			
		||||
uniform int alphatest_ref;
 | 
			
		||||
uniform vec4 const_color[NUM_TEV_STAGES];
 | 
			
		||||
uniform sampler2D tex[3];
 | 
			
		||||
 | 
			
		||||
uniform vec4 tev_combiner_buffer_color;
 | 
			
		||||
 | 
			
		||||
void main(void) {
 | 
			
		||||
    vec4 g_combiner_buffer = tev_combiner_buffer_color;
 | 
			
		||||
    vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
    // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
 | 
			
		||||
    if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) {
 | 
			
		||||
        shader += "discard;";
 | 
			
		||||
        return shader;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto& tev_stages = config.tev_stages;
 | 
			
		||||
    for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
 | 
			
		||||
        auto& tev_stage = tev_stages[tev_stage_index];
 | 
			
		||||
        if (!IsPassThroughTevStage(tev_stage)) {
 | 
			
		||||
            std::string index_name = std::to_string(tev_stage_index);
 | 
			
		||||
 | 
			
		||||
            shader += "vec3 color_results_" + index_name + "[3] = vec3[3](";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name);
 | 
			
		||||
            shader += ");\n";
 | 
			
		||||
 | 
			
		||||
            shader += "vec3 color_output_" + index_name + " = ";
 | 
			
		||||
            AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name);
 | 
			
		||||
            shader += ";\n";
 | 
			
		||||
 | 
			
		||||
            shader += "float alpha_results_" + index_name + "[3] = float[3](";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name);
 | 
			
		||||
            shader += ", ";
 | 
			
		||||
            AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name);
 | 
			
		||||
            shader += ");\n";
 | 
			
		||||
 | 
			
		||||
            shader += "float alpha_output_" + index_name + " = ";
 | 
			
		||||
            AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name);
 | 
			
		||||
            shader += ";\n";
 | 
			
		||||
 | 
			
		||||
            shader += "g_last_tex_env_out = vec4(min(color_output_" + index_name + " * " + std::to_string(tev_stage.GetColorMultiplier()) + ".0, 1.0), min(alpha_output_" + index_name + " * " + std::to_string(tev_stage.GetAlphaMultiplier()) + ".0, 1.0));\n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index))
 | 
			
		||||
            shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n";
 | 
			
		||||
 | 
			
		||||
        if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index))
 | 
			
		||||
            shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) {
 | 
			
		||||
        shader += "if (";
 | 
			
		||||
        AppendAlphaTestCondition(shader, config.alpha_test_func);
 | 
			
		||||
        shader += ") {\n discard;\n }\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    shader += "color = g_last_tex_env_out;\n}";
 | 
			
		||||
    return shader;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
} // namespace GLShader
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
 | 
			
		||||
namespace ShaderUtil {
 | 
			
		||||
namespace GLShader {
 | 
			
		||||
 | 
			
		||||
enum Attributes {
 | 
			
		||||
    ATTRIBUTE_POSITION  = 0,
 | 
			
		||||
@ -14,6 +14,6 @@ enum Attributes {
 | 
			
		||||
    ATTRIBUTE_TEXCOORDS = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path);
 | 
			
		||||
GLuint LoadProgram(const char* vertex_file_path, const char* fragment_file_path);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
@ -1,339 +0,0 @@
 | 
			
		||||
// Copyright 2014 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace GLShaders {
 | 
			
		||||
 | 
			
		||||
const char g_vertex_shader[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
in vec2 vert_position;
 | 
			
		||||
in vec2 vert_tex_coord;
 | 
			
		||||
out vec2 frag_tex_coord;
 | 
			
		||||
 | 
			
		||||
// This is a truncated 3x3 matrix for 2D transformations:
 | 
			
		||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
 | 
			
		||||
// The third column performs translation.
 | 
			
		||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
 | 
			
		||||
// implicitly be [0, 0, 1]
 | 
			
		||||
uniform mat3x2 modelview_matrix;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    // Multiply input position by the rotscale part of the matrix and then manually translate by
 | 
			
		||||
    // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
 | 
			
		||||
    // to `vec3(vert_position.xy, 1.0)`
 | 
			
		||||
    gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
 | 
			
		||||
    frag_tex_coord = vert_tex_coord;
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
const char g_fragment_shader[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
in vec2 frag_tex_coord;
 | 
			
		||||
out vec4 color;
 | 
			
		||||
 | 
			
		||||
uniform sampler2D color_texture;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    color = texture(color_texture, frag_tex_coord);
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
const char g_vertex_shader_hw[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
#define NUM_VTX_ATTR 7
 | 
			
		||||
 | 
			
		||||
in vec4 vert_position;
 | 
			
		||||
in vec4 vert_color;
 | 
			
		||||
in vec2 vert_texcoords0;
 | 
			
		||||
in vec2 vert_texcoords1;
 | 
			
		||||
in vec2 vert_texcoords2;
 | 
			
		||||
 | 
			
		||||
out vec4 o[NUM_VTX_ATTR];
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    o[2] = vert_color;
 | 
			
		||||
    o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy);
 | 
			
		||||
    o[5] = vec4(0.0, 0.0, vert_texcoords2.xy);
 | 
			
		||||
 | 
			
		||||
    gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
 | 
			
		||||
const char g_fragment_shader_hw[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
#define NUM_VTX_ATTR 7
 | 
			
		||||
#define NUM_TEV_STAGES 6
 | 
			
		||||
 | 
			
		||||
#define SOURCE_PRIMARYCOLOR           0x0
 | 
			
		||||
#define SOURCE_PRIMARYFRAGMENTCOLOR   0x1
 | 
			
		||||
#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
 | 
			
		||||
#define SOURCE_TEXTURE0               0x3
 | 
			
		||||
#define SOURCE_TEXTURE1               0x4
 | 
			
		||||
#define SOURCE_TEXTURE2               0x5
 | 
			
		||||
#define SOURCE_TEXTURE3               0x6
 | 
			
		||||
#define SOURCE_PREVIOUSBUFFER         0xd
 | 
			
		||||
#define SOURCE_CONSTANT               0xe
 | 
			
		||||
#define SOURCE_PREVIOUS               0xf
 | 
			
		||||
 | 
			
		||||
#define COLORMODIFIER_SOURCECOLOR         0x0
 | 
			
		||||
#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
 | 
			
		||||
#define COLORMODIFIER_SOURCEALPHA         0x2
 | 
			
		||||
#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
 | 
			
		||||
#define COLORMODIFIER_SOURCERED           0x4
 | 
			
		||||
#define COLORMODIFIER_ONEMINUSSOURCERED   0x5
 | 
			
		||||
#define COLORMODIFIER_SOURCEGREEN         0x8
 | 
			
		||||
#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
 | 
			
		||||
#define COLORMODIFIER_SOURCEBLUE          0xc
 | 
			
		||||
#define COLORMODIFIER_ONEMINUSSOURCEBLUE  0xd
 | 
			
		||||
 | 
			
		||||
#define ALPHAMODIFIER_SOURCEALPHA         0x0
 | 
			
		||||
#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
 | 
			
		||||
#define ALPHAMODIFIER_SOURCERED           0x2
 | 
			
		||||
#define ALPHAMODIFIER_ONEMINUSSOURCERED   0x3
 | 
			
		||||
#define ALPHAMODIFIER_SOURCEGREEN         0x4
 | 
			
		||||
#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
 | 
			
		||||
#define ALPHAMODIFIER_SOURCEBLUE          0x6
 | 
			
		||||
#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE  0x7
 | 
			
		||||
 | 
			
		||||
#define OPERATION_REPLACE         0
 | 
			
		||||
#define OPERATION_MODULATE        1
 | 
			
		||||
#define OPERATION_ADD             2
 | 
			
		||||
#define OPERATION_ADDSIGNED       3
 | 
			
		||||
#define OPERATION_LERP            4
 | 
			
		||||
#define OPERATION_SUBTRACT        5
 | 
			
		||||
#define OPERATION_MULTIPLYTHENADD 8
 | 
			
		||||
#define OPERATION_ADDTHENMULTIPLY 9
 | 
			
		||||
 | 
			
		||||
#define COMPAREFUNC_NEVER              0
 | 
			
		||||
#define COMPAREFUNC_ALWAYS             1
 | 
			
		||||
#define COMPAREFUNC_EQUAL              2
 | 
			
		||||
#define COMPAREFUNC_NOTEQUAL           3
 | 
			
		||||
#define COMPAREFUNC_LESSTHAN           4
 | 
			
		||||
#define COMPAREFUNC_LESSTHANOREQUAL    5
 | 
			
		||||
#define COMPAREFUNC_GREATERTHAN        6
 | 
			
		||||
#define COMPAREFUNC_GREATERTHANOREQUAL 7
 | 
			
		||||
 | 
			
		||||
in vec4 o[NUM_VTX_ATTR];
 | 
			
		||||
out vec4 color;
 | 
			
		||||
 | 
			
		||||
uniform bool alphatest_enabled;
 | 
			
		||||
uniform int alphatest_func;
 | 
			
		||||
uniform float alphatest_ref;
 | 
			
		||||
 | 
			
		||||
uniform sampler2D tex[3];
 | 
			
		||||
 | 
			
		||||
uniform vec4 tev_combiner_buffer_color;
 | 
			
		||||
 | 
			
		||||
struct TEVConfig
 | 
			
		||||
{
 | 
			
		||||
    bool enabled;
 | 
			
		||||
    ivec3 color_sources;
 | 
			
		||||
    ivec3 alpha_sources;
 | 
			
		||||
    ivec3 color_modifiers;
 | 
			
		||||
    ivec3 alpha_modifiers;
 | 
			
		||||
    ivec2 color_alpha_op;
 | 
			
		||||
    ivec2 color_alpha_multiplier;
 | 
			
		||||
    vec4 const_color;
 | 
			
		||||
    bvec2 updates_combiner_buffer_color_alpha;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
 | 
			
		||||
 | 
			
		||||
vec4 g_combiner_buffer;
 | 
			
		||||
vec4 g_last_tex_env_out;
 | 
			
		||||
vec4 g_const_color;
 | 
			
		||||
 | 
			
		||||
vec4 GetSource(int source) {
 | 
			
		||||
    if (source == SOURCE_PRIMARYCOLOR) {
 | 
			
		||||
        return o[2];
 | 
			
		||||
    } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use primary_color
 | 
			
		||||
        return o[2];
 | 
			
		||||
    } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
 | 
			
		||||
        // HACK: Until we implement fragment lighting, use zero
 | 
			
		||||
        return vec4(0.0, 0.0, 0.0, 0.0);
 | 
			
		||||
    } else if (source == SOURCE_TEXTURE0) {
 | 
			
		||||
        return texture(tex[0], o[3].xy);
 | 
			
		||||
    } else if (source == SOURCE_TEXTURE1) {
 | 
			
		||||
        return texture(tex[1], o[3].zw);
 | 
			
		||||
    } else if (source == SOURCE_TEXTURE2) {
 | 
			
		||||
        // TODO: Unverified
 | 
			
		||||
        return texture(tex[2], o[5].zw);
 | 
			
		||||
    } else if (source == SOURCE_TEXTURE3) {
 | 
			
		||||
        // TODO: no 4th texture?
 | 
			
		||||
    } else if (source == SOURCE_PREVIOUSBUFFER) {
 | 
			
		||||
        return g_combiner_buffer;
 | 
			
		||||
    } else if (source == SOURCE_CONSTANT) {
 | 
			
		||||
        return g_const_color;
 | 
			
		||||
    } else if (source == SOURCE_PREVIOUS) {
 | 
			
		||||
        return g_last_tex_env_out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return vec4(0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vec3 GetColorModifier(int factor, vec4 color) {
 | 
			
		||||
    if (factor == COLORMODIFIER_SOURCECOLOR) {
 | 
			
		||||
        return color.rgb;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
 | 
			
		||||
        return vec3(1.0) - color.rgb;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_SOURCEALPHA) {
 | 
			
		||||
        return color.aaa;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
 | 
			
		||||
        return vec3(1.0) - color.aaa;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_SOURCERED) {
 | 
			
		||||
        return color.rrr;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
 | 
			
		||||
        return vec3(1.0) - color.rrr;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_SOURCEGREEN) {
 | 
			
		||||
        return color.ggg;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
 | 
			
		||||
        return vec3(1.0) - color.ggg;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_SOURCEBLUE) {
 | 
			
		||||
        return color.bbb;
 | 
			
		||||
    } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
 | 
			
		||||
        return vec3(1.0) - color.bbb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return vec3(0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float GetAlphaModifier(int factor, vec4 color) {
 | 
			
		||||
    if (factor == ALPHAMODIFIER_SOURCEALPHA) {
 | 
			
		||||
        return color.a;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
 | 
			
		||||
        return 1.0 - color.a;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_SOURCERED) {
 | 
			
		||||
        return color.r;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
 | 
			
		||||
        return 1.0 - color.r;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
 | 
			
		||||
        return color.g;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
 | 
			
		||||
        return 1.0 - color.g;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
 | 
			
		||||
        return color.b;
 | 
			
		||||
    } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
 | 
			
		||||
        return 1.0 - color.b;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vec3 ColorCombine(int op, vec3 color[3]) {
 | 
			
		||||
    if (op == OPERATION_REPLACE) {
 | 
			
		||||
        return color[0];
 | 
			
		||||
    } else if (op == OPERATION_MODULATE) {
 | 
			
		||||
        return color[0] * color[1];
 | 
			
		||||
    } else if (op == OPERATION_ADD) {
 | 
			
		||||
        return min(color[0] + color[1], 1.0);
 | 
			
		||||
    } else if (op == OPERATION_ADDSIGNED) {
 | 
			
		||||
        return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
 | 
			
		||||
    } else if (op == OPERATION_LERP) {
 | 
			
		||||
        return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
 | 
			
		||||
    } else if (op == OPERATION_SUBTRACT) {
 | 
			
		||||
        return max(color[0] - color[1], 0.0);
 | 
			
		||||
    } else if (op == OPERATION_MULTIPLYTHENADD) {
 | 
			
		||||
        return min(color[0] * color[1] + color[2], 1.0);
 | 
			
		||||
    } else if (op == OPERATION_ADDTHENMULTIPLY) {
 | 
			
		||||
        return min(color[0] + color[1], 1.0) * color[2];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return vec3(0.0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float AlphaCombine(int op, float alpha[3]) {
 | 
			
		||||
    if (op == OPERATION_REPLACE) {
 | 
			
		||||
        return alpha[0];
 | 
			
		||||
    } else if (op == OPERATION_MODULATE) {
 | 
			
		||||
        return alpha[0] * alpha[1];
 | 
			
		||||
    } else if (op == OPERATION_ADD) {
 | 
			
		||||
        return min(alpha[0] + alpha[1], 1.0);
 | 
			
		||||
    } else if (op == OPERATION_ADDSIGNED) {
 | 
			
		||||
        return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
 | 
			
		||||
    } else if (op == OPERATION_LERP) {
 | 
			
		||||
        return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
 | 
			
		||||
    } else if (op == OPERATION_SUBTRACT) {
 | 
			
		||||
        return max(alpha[0] - alpha[1], 0.0);
 | 
			
		||||
    } else if (op == OPERATION_MULTIPLYTHENADD) {
 | 
			
		||||
        return min(alpha[0] * alpha[1] + alpha[2], 1.0);
 | 
			
		||||
    } else if (op == OPERATION_ADDTHENMULTIPLY) {
 | 
			
		||||
        return min(alpha[0] + alpha[1], 1.0) * alpha[2];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void main(void) {
 | 
			
		||||
    g_combiner_buffer = tev_combiner_buffer_color;
 | 
			
		||||
 | 
			
		||||
    for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
 | 
			
		||||
        if (tev_cfgs[tex_env_idx].enabled) {
 | 
			
		||||
            g_const_color = tev_cfgs[tex_env_idx].const_color;
 | 
			
		||||
 | 
			
		||||
            vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
 | 
			
		||||
                                            GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
 | 
			
		||||
                                            GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
 | 
			
		||||
            vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
 | 
			
		||||
 | 
			
		||||
            float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
 | 
			
		||||
                                              GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
 | 
			
		||||
                                              GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
 | 
			
		||||
            float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
 | 
			
		||||
 | 
			
		||||
            g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
 | 
			
		||||
            g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
 | 
			
		||||
            g_combiner_buffer.a = g_last_tex_env_out.a;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (alphatest_enabled) {
 | 
			
		||||
        if (alphatest_func == COMPAREFUNC_NEVER) {
 | 
			
		||||
            discard;
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
 | 
			
		||||
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_EQUAL) {
 | 
			
		||||
            if (g_last_tex_env_out.a != alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
 | 
			
		||||
            if (g_last_tex_env_out.a == alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
 | 
			
		||||
            if (g_last_tex_env_out.a >= alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
 | 
			
		||||
            if (g_last_tex_env_out.a > alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
 | 
			
		||||
            if (g_last_tex_env_out.a <= alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
 | 
			
		||||
            if (g_last_tex_env_out.a < alphatest_ref) {
 | 
			
		||||
                discard;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    color = g_last_tex_env_out;
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -21,9 +21,44 @@
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shader_util.h"
 | 
			
		||||
#include "video_core/renderer_opengl/gl_shaders.h"
 | 
			
		||||
#include "video_core/renderer_opengl/renderer_opengl.h"
 | 
			
		||||
 | 
			
		||||
static const char vertex_shader[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
in vec2 vert_position;
 | 
			
		||||
in vec2 vert_tex_coord;
 | 
			
		||||
out vec2 frag_tex_coord;
 | 
			
		||||
 | 
			
		||||
// This is a truncated 3x3 matrix for 2D transformations:
 | 
			
		||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
 | 
			
		||||
// The third column performs translation.
 | 
			
		||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
 | 
			
		||||
// implicitly be [0, 0, 1]
 | 
			
		||||
uniform mat3x2 modelview_matrix;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    // Multiply input position by the rotscale part of the matrix and then manually translate by
 | 
			
		||||
    // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
 | 
			
		||||
    // to `vec3(vert_position.xy, 1.0)`
 | 
			
		||||
    gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
 | 
			
		||||
    frag_tex_coord = vert_tex_coord;
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
static const char fragment_shader[] = R"(
 | 
			
		||||
#version 150 core
 | 
			
		||||
 | 
			
		||||
in vec2 frag_tex_coord;
 | 
			
		||||
out vec4 color;
 | 
			
		||||
 | 
			
		||||
uniform sampler2D color_texture;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    color = texture(color_texture, frag_tex_coord);
 | 
			
		||||
}
 | 
			
		||||
)";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Vertex structure that the drawn screen rectangles are composed of.
 | 
			
		||||
 */
 | 
			
		||||
@ -207,7 +242,7 @@ void RendererOpenGL::InitOpenGLObjects() {
 | 
			
		||||
    glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
 | 
			
		||||
 | 
			
		||||
    // Link shaders and get variable locations
 | 
			
		||||
    program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
 | 
			
		||||
    program_id = GLShader::LoadProgram(vertex_shader, fragment_shader);
 | 
			
		||||
    uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix");
 | 
			
		||||
    uniform_color_texture = glGetUniformLocation(program_id, "color_texture");
 | 
			
		||||
    attrib_position = glGetAttribLocation(program_id, "vert_position");
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user