diff options
| author | 2015-10-05 22:33:47 -0400 | |
|---|---|---|
| committer | 2015-10-21 21:53:14 -0400 | |
| commit | c86b9d42423b5a83ccba40f828b7ad9dafe3e317 (patch) | |
| tree | 04bdd0a99fa7fa36946422d0119b3d537dd99526 /src | |
| parent | gl_rasterizer: Move logic for creating ShaderCacheKey to a static function. (diff) | |
| download | yuzu-c86b9d42423b5a83ccba40f828b7ad9dafe3e317.tar.gz yuzu-c86b9d42423b5a83ccba40f828b7ad9dafe3e317.tar.xz yuzu-c86b9d42423b5a83ccba40f828b7ad9dafe3e317.zip | |
renderer_opengl: Refactor shader generation/caching to be more organized + various cleanups.
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/common_funcs.h | 18 | ||||
| -rw-r--r-- | src/video_core/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 131 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 41 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_resource_manager.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 371 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 17 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_util.cpp | 348 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_util.h | 6 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shaders.h | 339 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 39 |
11 files changed, 527 insertions, 788 deletions
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index ed20c3629..7a8dd39a0 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | #include <functional> | ||
| 9 | |||
| 7 | #include "common_types.h" | 10 | #include "common_types.h" |
| 8 | 11 | ||
| 9 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) | 12 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| @@ -95,3 +98,18 @@ inline u64 _rotr64(u64 x, unsigned int shift){ | |||
| 95 | // This function might change the error code. | 98 | // This function might change the error code. |
| 96 | // Defined in Misc.cpp. | 99 | // Defined in Misc.cpp. |
| 97 | const char* GetLastErrorMsg(); | 100 | const char* GetLastErrorMsg(); |
| 101 | |||
| 102 | template <typename T> | ||
| 103 | inline std::size_t hash(const T& o) { | ||
| 104 | return std::hash<T>()(o); | ||
| 105 | } | ||
| 106 | |||
| 107 | template <typename T> | ||
| 108 | inline std::size_t combine_hash(const T& o) { | ||
| 109 | return hash(o); | ||
| 110 | } | ||
| 111 | |||
| 112 | template <typename T, typename... Args> | ||
| 113 | inline std::size_t combine_hash(const T& o, const Args&... args) { | ||
| 114 | return hash(o) * 3 + combine_hash(args...); | ||
| 115 | } | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 8c9d76ab4..2a924f4ad 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | set(SRCS | 1 | set(SRCS |
| 2 | renderer_opengl/gl_rasterizer.cpp | 2 | renderer_opengl/gl_rasterizer.cpp |
| 3 | renderer_opengl/gl_rasterizer_cache.cpp | 3 | renderer_opengl/gl_rasterizer_cache.cpp |
| 4 | renderer_opengl/gl_shader_gen.cpp | ||
| 4 | renderer_opengl/gl_shader_util.cpp | 5 | renderer_opengl/gl_shader_util.cpp |
| 5 | renderer_opengl/gl_state.cpp | 6 | renderer_opengl/gl_state.cpp |
| 6 | renderer_opengl/renderer_opengl.cpp | 7 | renderer_opengl/renderer_opengl.cpp |
| @@ -21,8 +22,8 @@ set(HEADERS | |||
| 21 | renderer_opengl/gl_rasterizer.h | 22 | renderer_opengl/gl_rasterizer.h |
| 22 | renderer_opengl/gl_rasterizer_cache.h | 23 | renderer_opengl/gl_rasterizer_cache.h |
| 23 | renderer_opengl/gl_resource_manager.h | 24 | renderer_opengl/gl_resource_manager.h |
| 25 | renderer_opengl/gl_shader_gen.h | ||
| 24 | renderer_opengl/gl_shader_util.h | 26 | renderer_opengl/gl_shader_util.h |
| 25 | renderer_opengl/gl_shaders.h | ||
| 26 | renderer_opengl/gl_state.h | 27 | renderer_opengl/gl_state.h |
| 27 | renderer_opengl/pica_to_gl.h | 28 | renderer_opengl/pica_to_gl.h |
| 28 | renderer_opengl/renderer_opengl.h | 29 | renderer_opengl/renderer_opengl.h |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 01b9c91c6..4f9865230 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/color.h" | 10 | #include "common/color.h" |
| 11 | #include "common/file_util.h" | 11 | #include "common/file_util.h" |
| 12 | #include "common/make_unique.h" | ||
| 12 | #include "common/math_util.h" | 13 | #include "common/math_util.h" |
| 13 | #include "common/microprofile.h" | 14 | #include "common/microprofile.h" |
| 14 | #include "common/profiler.h" | 15 | #include "common/profiler.h" |
| @@ -20,7 +21,7 @@ | |||
| 20 | #include "video_core/pica.h" | 21 | #include "video_core/pica.h" |
| 21 | #include "video_core/utils.h" | 22 | #include "video_core/utils.h" |
| 22 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 23 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 23 | #include "video_core/renderer_opengl/gl_shaders.h" | 24 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 24 | #include "video_core/renderer_opengl/gl_shader_util.h" | 25 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 25 | #include "video_core/renderer_opengl/pica_to_gl.h" | 26 | #include "video_core/renderer_opengl/pica_to_gl.h" |
| 26 | 27 | ||
| @@ -54,20 +55,20 @@ void RasterizerOpenGL::InitObjects() { | |||
| 54 | state.Apply(); | 55 | state.Apply(); |
| 55 | 56 | ||
| 56 | // Set vertex attributes | 57 | // Set vertex attributes |
| 57 | glVertexAttribPointer(ShaderUtil::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); | 58 | glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); |
| 58 | glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_POSITION); | 59 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); |
| 59 | 60 | ||
| 60 | glVertexAttribPointer(ShaderUtil::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); | 61 | glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color)); |
| 61 | glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_COLOR); | 62 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); |
| 62 | 63 | ||
| 63 | glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); | 64 | glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); |
| 64 | glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); | 65 | glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); |
| 65 | glVertexAttribPointer(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); | 66 | glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORDS + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); |
| 66 | glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS); | 67 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 0); |
| 67 | glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 1); | 68 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 1); |
| 68 | glEnableVertexAttribArray(ShaderUtil::ATTRIBUTE_TEXCOORDS + 2); | 69 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORDS + 2); |
| 69 | 70 | ||
| 70 | RegenerateShaders(); | 71 | SetShader(); |
| 71 | 72 | ||
| 72 | // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation | 73 | // Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation |
| 73 | fb_color_texture.texture.Create(); | 74 | fb_color_texture.texture.Create(); |
| @@ -117,8 +118,6 @@ void RasterizerOpenGL::InitObjects() { | |||
| 117 | } | 118 | } |
| 118 | 119 | ||
| 119 | void RasterizerOpenGL::Reset() { | 120 | void RasterizerOpenGL::Reset() { |
| 120 | const auto& regs = Pica::g_state.regs; | ||
| 121 | |||
| 122 | SyncCullMode(); | 121 | SyncCullMode(); |
| 123 | SyncBlendEnabled(); | 122 | SyncBlendEnabled(); |
| 124 | SyncBlendFuncs(); | 123 | SyncBlendFuncs(); |
| @@ -127,7 +126,7 @@ void RasterizerOpenGL::Reset() { | |||
| 127 | SyncStencilTest(); | 126 | SyncStencilTest(); |
| 128 | SyncDepthTest(); | 127 | SyncDepthTest(); |
| 129 | 128 | ||
| 130 | RegenerateShaders(); | 129 | SetShader(); |
| 131 | 130 | ||
| 132 | res_cache.FullFlush(); | 131 | res_cache.FullFlush(); |
| 133 | } | 132 | } |
| @@ -140,70 +139,12 @@ void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, | |||
| 140 | vertex_batch.emplace_back(v2); | 139 | vertex_batch.emplace_back(v2); |
| 141 | } | 140 | } |
| 142 | 141 | ||
| 143 | namespace ShaderCache { | ||
| 144 | extern std::string GenerateFragmentShader(const ShaderCacheKey& config); | ||
| 145 | } | ||
| 146 | |||
| 147 | void RasterizerOpenGL::RegenerateShaders() { | ||
| 148 | ShaderCacheKey config = ShaderCacheKey::CurrentShaderConfig(); | ||
| 149 | |||
| 150 | auto cached_shader = shader_cache.find(config); | ||
| 151 | if (cached_shader != shader_cache.end()) { | ||
| 152 | current_shader = &cached_shader->second; | ||
| 153 | state.draw.shader_program = current_shader->shader.handle; | ||
| 154 | state.Apply(); | ||
| 155 | } else { | ||
| 156 | LOG_CRITICAL(Render_OpenGL, "Creating new shader: %08X", hash(config)); | ||
| 157 | |||
| 158 | TEVShader shader; | ||
| 159 | |||
| 160 | std::string fragShader = ShaderCache::GenerateFragmentShader(config); | ||
| 161 | shader.shader.Create(GLShaders::g_vertex_shader_hw, fragShader.c_str()); | ||
| 162 | |||
| 163 | shader.uniform_alphatest_ref = glGetUniformLocation(shader.shader.handle, "alphatest_ref"); | ||
| 164 | shader.uniform_tex = glGetUniformLocation(shader.shader.handle, "tex"); | ||
| 165 | shader.uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.shader.handle, "tev_combiner_buffer_color"); | ||
| 166 | shader.uniform_tev_const_colors = glGetUniformLocation(shader.shader.handle, "const_color"); | ||
| 167 | |||
| 168 | current_shader = &shader_cache.emplace(config, std::move(shader)).first->second; | ||
| 169 | |||
| 170 | state.draw.shader_program = current_shader->shader.handle; | ||
| 171 | state.Apply(); | ||
| 172 | |||
| 173 | // Set the texture samplers to correspond to different texture units | ||
| 174 | if (shader.uniform_tex != -1) { | ||
| 175 | glUniform1i(shader.uniform_tex, 0); | ||
| 176 | glUniform1i(shader.uniform_tex + 1, 1); | ||
| 177 | glUniform1i(shader.uniform_tex + 2, 2); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | // Sync alpha reference | ||
| 182 | if (current_shader->uniform_alphatest_ref != -1) | ||
| 183 | glUniform1i(current_shader->uniform_alphatest_ref, Pica::g_state.regs.output_merger.alpha_test.ref); | ||
| 184 | |||
| 185 | // Sync combiner buffer color | ||
| 186 | if (current_shader->uniform_tev_combiner_buffer_color != -1) { | ||
| 187 | auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw); | ||
| 188 | glUniform4fv(current_shader->uniform_tev_combiner_buffer_color, 1, combiner_color.data()); | ||
| 189 | } | ||
| 190 | |||
| 191 | // Sync TEV const colors | ||
| 192 | if (current_shader->uniform_tev_const_colors != -1) { | ||
| 193 | auto& tev_stages = Pica::g_state.regs.GetTevStages(); | ||
| 194 | for (int tev_index = 0; tev_index < tev_stages.size(); ++tev_index) { | ||
| 195 | auto const_color = PicaToGL::ColorRGBA8(tev_stages[tev_index].const_color); | ||
| 196 | glUniform4fv(current_shader->uniform_tev_const_colors + tev_index, 1, const_color.data()); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | void RasterizerOpenGL::DrawTriangles() { | 142 | void RasterizerOpenGL::DrawTriangles() { |
| 202 | SyncFramebuffer(); | 143 | SyncFramebuffer(); |
| 203 | SyncDrawState(); | 144 | SyncDrawState(); |
| 204 | 145 | ||
| 205 | if (state.draw.shader_dirty) { | 146 | if (state.draw.shader_dirty) { |
| 206 | RegenerateShaders(); | 147 | SetShader(); |
| 207 | state.draw.shader_dirty = false; | 148 | state.draw.shader_dirty = false; |
| 208 | } | 149 | } |
| 209 | 150 | ||
| @@ -519,6 +460,48 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica:: | |||
| 519 | state.Apply(); | 460 | state.Apply(); |
| 520 | } | 461 | } |
| 521 | 462 | ||
| 463 | void RasterizerOpenGL::SetShader() { | ||
| 464 | ShaderCacheKey config = ShaderCacheKey::CurrentConfig(); | ||
| 465 | |||
| 466 | // Find (or generate) the GLSL shader for the current TEV state | ||
| 467 | auto cached_shader = shader_cache.find(config); | ||
| 468 | if (cached_shader != shader_cache.end()) { | ||
| 469 | current_shader = cached_shader->second.get(); | ||
| 470 | |||
| 471 | state.draw.shader_program = current_shader->shader.handle; | ||
| 472 | state.Apply(); | ||
| 473 | } else { | ||
| 474 | LOG_DEBUG(Render_OpenGL, "Creating new shader: %08X", hash(config)); | ||
| 475 | |||
| 476 | std::unique_ptr<TEVShader> shader = Common::make_unique<TEVShader>(); | ||
| 477 | |||
| 478 | shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str()); | ||
| 479 | shader->uniform_alphatest_ref = glGetUniformLocation(shader->shader.handle, "alphatest_ref"); | ||
| 480 | shader->uniform_tex = glGetUniformLocation(shader->shader.handle, "tex"); | ||
| 481 | shader->uniform_tev_combiner_buffer_color = glGetUniformLocation(shader->shader.handle, "tev_combiner_buffer_color"); | ||
| 482 | shader->uniform_tev_const_colors = glGetUniformLocation(shader->shader.handle, "const_color"); | ||
| 483 | |||
| 484 | state.draw.shader_program = shader->shader.handle; | ||
| 485 | state.Apply(); | ||
| 486 | |||
| 487 | // Set the texture samplers to correspond to different texture units | ||
| 488 | if (shader->uniform_tex != -1) { | ||
| 489 | glUniform1i(shader->uniform_tex, 0); | ||
| 490 | glUniform1i(shader->uniform_tex + 1, 1); | ||
| 491 | glUniform1i(shader->uniform_tex + 2, 2); | ||
| 492 | } | ||
| 493 | |||
| 494 | current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); | ||
| 495 | } | ||
| 496 | |||
| 497 | // Update uniforms | ||
| 498 | SyncAlphaTest(); | ||
| 499 | SyncCombinerColor(); | ||
| 500 | auto& tev_stages = Pica::g_state.regs.GetTevStages(); | ||
| 501 | for (int index = 0; index < tev_stages.size(); ++index) | ||
| 502 | SyncTevConstColor(index, tev_stages[index]); | ||
| 503 | } | ||
| 504 | |||
| 522 | void RasterizerOpenGL::SyncFramebuffer() { | 505 | void RasterizerOpenGL::SyncFramebuffer() { |
| 523 | const auto& regs = Pica::g_state.regs; | 506 | const auto& regs = Pica::g_state.regs; |
| 524 | 507 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5bc4a319f..de9e4d22e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 7 | #include <vector> | 9 | #include <vector> |
| 8 | #include <unordered_map> | 10 | #include <unordered_map> |
| 9 | 11 | ||
| @@ -15,21 +17,6 @@ | |||
| 15 | #include "video_core/renderer_opengl/gl_state.h" | 17 | #include "video_core/renderer_opengl/gl_state.h" |
| 16 | #include "video_core/shader/shader_interpreter.h" | 18 | #include "video_core/shader/shader_interpreter.h" |
| 17 | 19 | ||
| 18 | template <typename T> | ||
| 19 | inline size_t hash(const T& o) { | ||
| 20 | return std::hash<T>()(o); | ||
| 21 | } | ||
| 22 | |||
| 23 | template <typename T> | ||
| 24 | inline size_t combine_hash(const T& o) { | ||
| 25 | return hash(o); | ||
| 26 | } | ||
| 27 | |||
| 28 | template <typename T, typename... Args> | ||
| 29 | inline size_t combine_hash(const T& o, const Args&... args) { | ||
| 30 | return hash(o) * 3 + combine_hash(args...); | ||
| 31 | } | ||
| 32 | |||
| 33 | struct ShaderCacheKey { | 20 | struct ShaderCacheKey { |
| 34 | using Regs = Pica::Regs; | 21 | using Regs = Pica::Regs; |
| 35 | 22 | ||
| @@ -49,7 +36,7 @@ struct ShaderCacheKey { | |||
| 49 | return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); | 36 | return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); |
| 50 | } | 37 | } |
| 51 | 38 | ||
| 52 | static ShaderCacheKey CurrentShaderConfig() { | 39 | static ShaderCacheKey CurrentConfig() { |
| 53 | const auto& regs = Pica::g_state.regs; | 40 | const auto& regs = Pica::g_state.regs; |
| 54 | ShaderCacheKey config; | 41 | ShaderCacheKey config; |
| 55 | 42 | ||
| @@ -94,8 +81,14 @@ struct ShaderCacheKey { | |||
| 94 | 81 | ||
| 95 | namespace std { | 82 | namespace std { |
| 96 | 83 | ||
| 84 | template<> struct hash<::Pica::Regs::CompareFunc> { | ||
| 85 | std::size_t operator()(const ::Pica::Regs::CompareFunc& o) { | ||
| 86 | return ::hash((unsigned)o); | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 97 | template<> struct hash<::Pica::Regs::TevStageConfig> { | 90 | template<> struct hash<::Pica::Regs::TevStageConfig> { |
| 98 | size_t operator()(const ::Pica::Regs::TevStageConfig& o) { | 91 | std::size_t operator()(const ::Pica::Regs::TevStageConfig& o) { |
| 99 | return ::combine_hash( | 92 | return ::combine_hash( |
| 100 | ::hash(o.source_raw), ::hash(o.modifier_raw), | 93 | ::hash(o.source_raw), ::hash(o.modifier_raw), |
| 101 | ::hash(o.op_raw), ::hash(o.scale_raw)); | 94 | ::hash(o.op_raw), ::hash(o.scale_raw)); |
| @@ -103,13 +96,14 @@ template<> struct hash<::Pica::Regs::TevStageConfig> { | |||
| 103 | }; | 96 | }; |
| 104 | 97 | ||
| 105 | template<> struct hash<::ShaderCacheKey> { | 98 | template<> struct hash<::ShaderCacheKey> { |
| 106 | size_t operator()(const ::ShaderCacheKey& o) const { | 99 | std::size_t operator()(const ::ShaderCacheKey& o) const { |
| 107 | return ::combine_hash(o.alpha_test_func, o.combiner_buffer_input, | 100 | return ::combine_hash(o.alpha_test_func, o.combiner_buffer_input, |
| 108 | o.tev_stages[0], o.tev_stages[1], o.tev_stages[2], | 101 | o.tev_stages[0], o.tev_stages[1], o.tev_stages[2], |
| 109 | o.tev_stages[3], o.tev_stages[4], o.tev_stages[5]); | 102 | o.tev_stages[3], o.tev_stages[4], o.tev_stages[5]); |
| 110 | } | 103 | } |
| 111 | }; | 104 | }; |
| 112 | } | 105 | |
| 106 | } // namespace std | ||
| 113 | 107 | ||
| 114 | class RasterizerOpenGL : public HWRasterizer { | 108 | class RasterizerOpenGL : public HWRasterizer { |
| 115 | public: | 109 | public: |
| @@ -131,8 +125,6 @@ public: | |||
| 131 | /// Draw the current batch of triangles | 125 | /// Draw the current batch of triangles |
| 132 | void DrawTriangles() override; | 126 | void DrawTriangles() override; |
| 133 | 127 | ||
| 134 | void RegenerateShaders(); | ||
| 135 | |||
| 136 | /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer | 128 | /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer |
| 137 | void CommitFramebuffer() override; | 129 | void CommitFramebuffer() override; |
| 138 | 130 | ||
| @@ -245,6 +237,9 @@ private: | |||
| 245 | /// Reconfigure the OpenGL depth texture to use the given format and dimensions | 237 | /// Reconfigure the OpenGL depth texture to use the given format and dimensions |
| 246 | void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); | 238 | void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height); |
| 247 | 239 | ||
| 240 | /// Sets the OpenGL shader in accordance with the current PICA register state | ||
| 241 | void SetShader(); | ||
| 242 | |||
| 248 | /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer | 243 | /// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer |
| 249 | void SyncFramebuffer(); | 244 | void SyncFramebuffer(); |
| 250 | 245 | ||
| @@ -315,8 +310,8 @@ private: | |||
| 315 | TextureInfo fb_color_texture; | 310 | TextureInfo fb_color_texture; |
| 316 | DepthTextureInfo fb_depth_texture; | 311 | DepthTextureInfo fb_depth_texture; |
| 317 | 312 | ||
| 318 | std::unordered_map<ShaderCacheKey, TEVShader> shader_cache; | 313 | std::unordered_map<ShaderCacheKey, std::unique_ptr<TEVShader>> shader_cache; |
| 319 | TEVShader* current_shader = nullptr; | 314 | const TEVShader* current_shader = nullptr; |
| 320 | 315 | ||
| 321 | OGLVertexArray vertex_array; | 316 | OGLVertexArray vertex_array; |
| 322 | OGLBuffer vertex_buffer; | 317 | OGLBuffer vertex_buffer; |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 65034d40d..eb128966c 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h | |||
| @@ -71,7 +71,7 @@ public: | |||
| 71 | /// Creates a new internal OpenGL resource and stores the handle | 71 | /// Creates a new internal OpenGL resource and stores the handle |
| 72 | void Create(const char* vert_shader, const char* frag_shader) { | 72 | void Create(const char* vert_shader, const char* frag_shader) { |
| 73 | if (handle != 0) return; | 73 | if (handle != 0) return; |
| 74 | handle = ShaderUtil::LoadShaders(vert_shader, frag_shader); | 74 | handle = GLShader::LoadProgram(vert_shader, frag_shader); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | /// Deletes the internal OpenGL resource | 77 | /// Deletes the internal OpenGL resource |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp new file mode 100644 index 000000000..059f127af --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -0,0 +1,371 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "video_core/pica.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||
| 7 | #include "video_core/renderer_opengl/gl_shader_gen.h" | ||
| 8 | |||
| 9 | namespace GLShader { | ||
| 10 | |||
| 11 | static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { | ||
| 12 | return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && | ||
| 13 | stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && | ||
| 14 | stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && | ||
| 15 | stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && | ||
| 16 | stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && | ||
| 17 | stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && | ||
| 18 | stage.GetColorMultiplier() == 1 && | ||
| 19 | stage.GetAlphaMultiplier() == 1); | ||
| 20 | } | ||
| 21 | |||
| 22 | static void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 23 | using Source = Pica::Regs::TevStageConfig::Source; | ||
| 24 | switch (source) { | ||
| 25 | case Source::PrimaryColor: | ||
| 26 | shader += "o[2]"; | ||
| 27 | break; | ||
| 28 | case Source::PrimaryFragmentColor: | ||
| 29 | // HACK: Until we implement fragment lighting, use primary_color | ||
| 30 | shader += "o[2]"; | ||
| 31 | break; | ||
| 32 | case Source::SecondaryFragmentColor: | ||
| 33 | // HACK: Until we implement fragment lighting, use zero | ||
| 34 | shader += "vec4(0.0, 0.0, 0.0, 0.0)"; | ||
| 35 | break; | ||
| 36 | case Source::Texture0: | ||
| 37 | shader += "texture(tex[0], o[3].xy)"; | ||
| 38 | break; | ||
| 39 | case Source::Texture1: | ||
| 40 | shader += "texture(tex[1], o[3].zw)"; | ||
| 41 | break; | ||
| 42 | case Source::Texture2: // TODO: Unverified | ||
| 43 | shader += "texture(tex[2], o[5].zw)"; | ||
| 44 | break; | ||
| 45 | case Source::PreviousBuffer: | ||
| 46 | shader += "g_combiner_buffer"; | ||
| 47 | break; | ||
| 48 | case Source::Constant: | ||
| 49 | shader += "const_color[" + index_name + "]"; | ||
| 50 | break; | ||
| 51 | case Source::Previous: | ||
| 52 | shader += "g_last_tex_env_out"; | ||
| 53 | break; | ||
| 54 | default: | ||
| 55 | shader += "vec4(0.0)"; | ||
| 56 | LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); | ||
| 57 | break; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | static void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, | ||
| 62 | Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 63 | using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; | ||
| 64 | switch (modifier) { | ||
| 65 | case ColorModifier::SourceColor: | ||
| 66 | AppendSource(shader, source, index_name); | ||
| 67 | shader += ".rgb"; | ||
| 68 | break; | ||
| 69 | case ColorModifier::OneMinusSourceColor: | ||
| 70 | shader += "vec3(1.0) - "; | ||
| 71 | AppendSource(shader, source, index_name); | ||
| 72 | shader += ".rgb"; | ||
| 73 | break; | ||
| 74 | case ColorModifier::SourceAlpha: | ||
| 75 | AppendSource(shader, source, index_name); | ||
| 76 | shader += ".aaa"; | ||
| 77 | break; | ||
| 78 | case ColorModifier::OneMinusSourceAlpha: | ||
| 79 | shader += "vec3(1.0) - "; | ||
| 80 | AppendSource(shader, source, index_name); | ||
| 81 | shader += ".aaa"; | ||
| 82 | break; | ||
| 83 | case ColorModifier::SourceRed: | ||
| 84 | AppendSource(shader, source, index_name); | ||
| 85 | shader += ".rrr"; | ||
| 86 | break; | ||
| 87 | case ColorModifier::OneMinusSourceRed: | ||
| 88 | shader += "vec3(1.0) - "; | ||
| 89 | AppendSource(shader, source, index_name); | ||
| 90 | shader += ".rrr"; | ||
| 91 | break; | ||
| 92 | case ColorModifier::SourceGreen: | ||
| 93 | AppendSource(shader, source, index_name); | ||
| 94 | shader += ".ggg"; | ||
| 95 | break; | ||
| 96 | case ColorModifier::OneMinusSourceGreen: | ||
| 97 | shader += "vec3(1.0) - "; | ||
| 98 | AppendSource(shader, source, index_name); | ||
| 99 | shader += ".ggg"; | ||
| 100 | break; | ||
| 101 | case ColorModifier::SourceBlue: | ||
| 102 | AppendSource(shader, source, index_name); | ||
| 103 | shader += ".bbb"; | ||
| 104 | break; | ||
| 105 | case ColorModifier::OneMinusSourceBlue: | ||
| 106 | shader += "vec3(1.0) - "; | ||
| 107 | AppendSource(shader, source, index_name); | ||
| 108 | shader += ".bbb"; | ||
| 109 | break; | ||
| 110 | default: | ||
| 111 | shader += "vec3(0.0)"; | ||
| 112 | LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); | ||
| 113 | break; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | static void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, | ||
| 118 | Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 119 | using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; | ||
| 120 | switch (modifier) { | ||
| 121 | case AlphaModifier::SourceAlpha: | ||
| 122 | AppendSource(shader, source, index_name); | ||
| 123 | shader += ".a"; | ||
| 124 | break; | ||
| 125 | case AlphaModifier::OneMinusSourceAlpha: | ||
| 126 | shader += "1.0 - "; | ||
| 127 | AppendSource(shader, source, index_name); | ||
| 128 | shader += ".a"; | ||
| 129 | break; | ||
| 130 | case AlphaModifier::SourceRed: | ||
| 131 | AppendSource(shader, source, index_name); | ||
| 132 | shader += ".r"; | ||
| 133 | break; | ||
| 134 | case AlphaModifier::OneMinusSourceRed: | ||
| 135 | shader += "1.0 - "; | ||
| 136 | AppendSource(shader, source, index_name); | ||
| 137 | shader += ".r"; | ||
| 138 | break; | ||
| 139 | case AlphaModifier::SourceGreen: | ||
| 140 | AppendSource(shader, source, index_name); | ||
| 141 | shader += ".g"; | ||
| 142 | break; | ||
| 143 | case AlphaModifier::OneMinusSourceGreen: | ||
| 144 | shader += "1.0 - "; | ||
| 145 | AppendSource(shader, source, index_name); | ||
| 146 | shader += ".g"; | ||
| 147 | break; | ||
| 148 | case AlphaModifier::SourceBlue: | ||
| 149 | AppendSource(shader, source, index_name); | ||
| 150 | shader += ".b"; | ||
| 151 | break; | ||
| 152 | case AlphaModifier::OneMinusSourceBlue: | ||
| 153 | shader += "1.0 - "; | ||
| 154 | AppendSource(shader, source, index_name); | ||
| 155 | shader += ".b"; | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | shader += "vec3(0.0)"; | ||
| 159 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); | ||
| 160 | break; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | static void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, | ||
| 165 | const std::string& variable_name) { | ||
| 166 | using Operation = Pica::Regs::TevStageConfig::Operation; | ||
| 167 | |||
| 168 | switch (operation) { | ||
| 169 | case Operation::Replace: | ||
| 170 | shader += variable_name + "[0]"; | ||
| 171 | break; | ||
| 172 | case Operation::Modulate: | ||
| 173 | shader += variable_name + "[0] * " + variable_name + "[1]"; | ||
| 174 | break; | ||
| 175 | case Operation::Add: | ||
| 176 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; | ||
| 177 | break; | ||
| 178 | case Operation::AddSigned: | ||
| 179 | shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; | ||
| 180 | break; | ||
| 181 | case Operation::Lerp: | ||
| 182 | shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; | ||
| 183 | break; | ||
| 184 | case Operation::Subtract: | ||
| 185 | shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; | ||
| 186 | break; | ||
| 187 | case Operation::MultiplyThenAdd: | ||
| 188 | shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; | ||
| 189 | break; | ||
| 190 | case Operation::AddThenMultiply: | ||
| 191 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; | ||
| 192 | break; | ||
| 193 | default: | ||
| 194 | shader += "vec3(0.0)"; | ||
| 195 | LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); | ||
| 196 | break; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | static void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, | ||
| 201 | const std::string& variable_name) { | ||
| 202 | using Operation = Pica::Regs::TevStageConfig::Operation; | ||
| 203 | switch (operation) { | ||
| 204 | case Operation::Replace: | ||
| 205 | shader += variable_name + "[0]"; | ||
| 206 | break; | ||
| 207 | case Operation::Modulate: | ||
| 208 | shader += variable_name + "[0] * " + variable_name + "[1]"; | ||
| 209 | break; | ||
| 210 | case Operation::Add: | ||
| 211 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; | ||
| 212 | break; | ||
| 213 | case Operation::AddSigned: | ||
| 214 | shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; | ||
| 215 | break; | ||
| 216 | case Operation::Lerp: | ||
| 217 | shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; | ||
| 218 | break; | ||
| 219 | case Operation::Subtract: | ||
| 220 | shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; | ||
| 221 | break; | ||
| 222 | case Operation::MultiplyThenAdd: | ||
| 223 | shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; | ||
| 224 | break; | ||
| 225 | case Operation::AddThenMultiply: | ||
| 226 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; | ||
| 227 | break; | ||
| 228 | default: | ||
| 229 | shader += "0.0"; | ||
| 230 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); | ||
| 231 | break; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | static void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { | ||
| 236 | using CompareFunc = Pica::Regs::CompareFunc; | ||
| 237 | switch (func) { | ||
| 238 | case CompareFunc::Never: | ||
| 239 | shader += "true"; | ||
| 240 | break; | ||
| 241 | case CompareFunc::Always: | ||
| 242 | shader += "false"; | ||
| 243 | break; | ||
| 244 | case CompareFunc::Equal: | ||
| 245 | shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; | ||
| 246 | break; | ||
| 247 | case CompareFunc::NotEqual: | ||
| 248 | shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; | ||
| 249 | break; | ||
| 250 | case CompareFunc::LessThan: | ||
| 251 | shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; | ||
| 252 | break; | ||
| 253 | case CompareFunc::LessThanOrEqual: | ||
| 254 | shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; | ||
| 255 | break; | ||
| 256 | case CompareFunc::GreaterThan: | ||
| 257 | shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; | ||
| 258 | break; | ||
| 259 | case CompareFunc::GreaterThanOrEqual: | ||
| 260 | shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; | ||
| 261 | break; | ||
| 262 | default: | ||
| 263 | shader += "false"; | ||
| 264 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | } | ||
| 268 | |||
| 269 | std::string GenerateFragmentShader(const ShaderCacheKey& config) { | ||
| 270 | std::string shader = R"( | ||
| 271 | #version 150 core | ||
| 272 | |||
| 273 | #define NUM_VTX_ATTR 7 | ||
| 274 | #define NUM_TEV_STAGES 6 | ||
| 275 | |||
| 276 | in vec4 o[NUM_VTX_ATTR]; | ||
| 277 | out vec4 color; | ||
| 278 | |||
| 279 | uniform int alphatest_ref; | ||
| 280 | uniform vec4 const_color[NUM_TEV_STAGES]; | ||
| 281 | uniform sampler2D tex[3]; | ||
| 282 | |||
| 283 | uniform vec4 tev_combiner_buffer_color; | ||
| 284 | |||
| 285 | void main(void) { | ||
| 286 | vec4 g_combiner_buffer = tev_combiner_buffer_color; | ||
| 287 | vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); | ||
| 288 | )"; | ||
| 289 | |||
| 290 | // Do not do any sort of processing if it's obvious we're not going to pass the alpha test | ||
| 291 | if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { | ||
| 292 | shader += "discard;"; | ||
| 293 | return shader; | ||
| 294 | } | ||
| 295 | |||
| 296 | auto& tev_stages = config.tev_stages; | ||
| 297 | for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { | ||
| 298 | auto& tev_stage = tev_stages[tev_stage_index]; | ||
| 299 | if (!IsPassThroughTevStage(tev_stage)) { | ||
| 300 | std::string index_name = std::to_string(tev_stage_index); | ||
| 301 | |||
| 302 | shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; | ||
| 303 | AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); | ||
| 304 | shader += ", "; | ||
| 305 | AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); | ||
| 306 | shader += ", "; | ||
| 307 | AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); | ||
| 308 | shader += ");\n"; | ||
| 309 | |||
| 310 | shader += "vec3 color_output_" + index_name + " = "; | ||
| 311 | AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); | ||
| 312 | shader += ";\n"; | ||
| 313 | |||
| 314 | shader += "float alpha_results_" + index_name + "[3] = float[3]("; | ||
| 315 | AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); | ||
| 316 | shader += ", "; | ||
| 317 | AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); | ||
| 318 | shader += ", "; | ||
| 319 | AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); | ||
| 320 | shader += ");\n"; | ||
| 321 | |||
| 322 | shader += "float alpha_output_" + index_name + " = "; | ||
| 323 | AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); | ||
| 324 | shader += ";\n"; | ||
| 325 | |||
| 326 | 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"; | ||
| 327 | } | ||
| 328 | |||
| 329 | if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) | ||
| 330 | shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; | ||
| 331 | |||
| 332 | if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) | ||
| 333 | shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; | ||
| 334 | } | ||
| 335 | |||
| 336 | if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { | ||
| 337 | shader += "if ("; | ||
| 338 | AppendAlphaTestCondition(shader, config.alpha_test_func); | ||
| 339 | shader += ") {\n discard;\n }\n"; | ||
| 340 | } | ||
| 341 | |||
| 342 | shader += "color = g_last_tex_env_out;\n}"; | ||
| 343 | return shader; | ||
| 344 | } | ||
| 345 | |||
| 346 | std::string GenerateVertexShader() { | ||
| 347 | static const std::string shader_str = R"( | ||
| 348 | #version 150 core | ||
| 349 | |||
| 350 | #define NUM_VTX_ATTR 7 | ||
| 351 | |||
| 352 | in vec4 vert_position; | ||
| 353 | in vec4 vert_color; | ||
| 354 | in vec2 vert_texcoords0; | ||
| 355 | in vec2 vert_texcoords1; | ||
| 356 | in vec2 vert_texcoords2; | ||
| 357 | |||
| 358 | out vec4 o[NUM_VTX_ATTR]; | ||
| 359 | |||
| 360 | void main() { | ||
| 361 | o[2] = vert_color; | ||
| 362 | o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); | ||
| 363 | o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); | ||
| 364 | |||
| 365 | gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); | ||
| 366 | } | ||
| 367 | )"; | ||
| 368 | return shader_str; | ||
| 369 | } | ||
| 370 | |||
| 371 | } // namespace GLShaderGen | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h new file mode 100644 index 000000000..7fd18de6d --- /dev/null +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | |||
| 9 | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||
| 10 | |||
| 11 | namespace GLShader { | ||
| 12 | |||
| 13 | std::string GenerateVertexShader(); | ||
| 14 | |||
| 15 | std::string GenerateFragmentShader(const ShaderCacheKey& config); | ||
| 16 | |||
| 17 | } // namespace GLShader | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 8a6a51ad4..ce218b857 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp | |||
| @@ -2,22 +2,15 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | |||
| 6 | #include "gl_shader_util.h" | ||
| 7 | #include "gl_rasterizer.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | |||
| 10 | #include "video_core/pica.h" | ||
| 11 | |||
| 12 | #include <algorithm> | 5 | #include <algorithm> |
| 13 | #include <vector> | 6 | #include <vector> |
| 14 | 7 | ||
| 15 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 16 | #include "video_core/renderer_opengl/gl_shader_util.h" | 9 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 17 | 10 | ||
| 18 | namespace ShaderUtil { | 11 | namespace GLShader { |
| 19 | 12 | ||
| 20 | GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | 13 | GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) { |
| 21 | 14 | ||
| 22 | // Create the shaders | 15 | // Create the shaders |
| 23 | GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); | 16 | GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER); |
| @@ -101,339 +94,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) { | |||
| 101 | return program_id; | 94 | return program_id; |
| 102 | } | 95 | } |
| 103 | 96 | ||
| 104 | } | 97 | } // namespace GLShader |
| 105 | |||
| 106 | namespace ShaderCache | ||
| 107 | { | ||
| 108 | |||
| 109 | static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) { | ||
| 110 | return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace && | ||
| 111 | stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace && | ||
| 112 | stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous && | ||
| 113 | stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous && | ||
| 114 | stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor && | ||
| 115 | stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha && | ||
| 116 | stage.GetColorMultiplier() == 1 && | ||
| 117 | stage.GetAlphaMultiplier() == 1); | ||
| 118 | } | ||
| 119 | |||
| 120 | void AppendSource(std::string& shader, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 121 | using Source = Pica::Regs::TevStageConfig::Source; | ||
| 122 | switch (source) { | ||
| 123 | case Source::PrimaryColor: | ||
| 124 | shader += "o[2]"; | ||
| 125 | break; | ||
| 126 | case Source::PrimaryFragmentColor: | ||
| 127 | // HACK: Until we implement fragment lighting, use primary_color | ||
| 128 | shader += "o[2]"; | ||
| 129 | break; | ||
| 130 | case Source::SecondaryFragmentColor: | ||
| 131 | // HACK: Until we implement fragment lighting, use zero | ||
| 132 | shader += "vec4(0.0, 0.0, 0.0, 0.0)"; | ||
| 133 | break; | ||
| 134 | case Source::Texture0: | ||
| 135 | shader += "texture(tex[0], o[3].xy)"; | ||
| 136 | break; | ||
| 137 | case Source::Texture1: | ||
| 138 | shader += "texture(tex[1], o[3].zw)"; | ||
| 139 | break; | ||
| 140 | case Source::Texture2: // TODO: Unverified | ||
| 141 | shader += "texture(tex[2], o[5].zw)"; | ||
| 142 | break; | ||
| 143 | case Source::PreviousBuffer: | ||
| 144 | shader += "g_combiner_buffer"; | ||
| 145 | break; | ||
| 146 | case Source::Constant: | ||
| 147 | shader += "const_color[" + index_name + "]"; | ||
| 148 | break; | ||
| 149 | case Source::Previous: | ||
| 150 | shader += "g_last_tex_env_out"; | ||
| 151 | break; | ||
| 152 | default: | ||
| 153 | shader += "vec4(0.0)"; | ||
| 154 | LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | void AppendColorModifier(std::string& shader, Pica::Regs::TevStageConfig::ColorModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 160 | using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier; | ||
| 161 | switch (modifier) { | ||
| 162 | case ColorModifier::SourceColor: | ||
| 163 | AppendSource(shader, source, index_name); | ||
| 164 | shader += ".rgb"; | ||
| 165 | break; | ||
| 166 | case ColorModifier::OneMinusSourceColor: | ||
| 167 | shader += "vec3(1.0) - "; | ||
| 168 | AppendSource(shader, source, index_name); | ||
| 169 | shader += ".rgb"; | ||
| 170 | break; | ||
| 171 | case ColorModifier::SourceAlpha: | ||
| 172 | AppendSource(shader, source, index_name); | ||
| 173 | shader += ".aaa"; | ||
| 174 | break; | ||
| 175 | case ColorModifier::OneMinusSourceAlpha: | ||
| 176 | shader += "vec3(1.0) - "; | ||
| 177 | AppendSource(shader, source, index_name); | ||
| 178 | shader += ".aaa"; | ||
| 179 | break; | ||
| 180 | case ColorModifier::SourceRed: | ||
| 181 | AppendSource(shader, source, index_name); | ||
| 182 | shader += ".rrr"; | ||
| 183 | break; | ||
| 184 | case ColorModifier::OneMinusSourceRed: | ||
| 185 | shader += "vec3(1.0) - "; | ||
| 186 | AppendSource(shader, source, index_name); | ||
| 187 | shader += ".rrr"; | ||
| 188 | break; | ||
| 189 | case ColorModifier::SourceGreen: | ||
| 190 | AppendSource(shader, source, index_name); | ||
| 191 | shader += ".ggg"; | ||
| 192 | break; | ||
| 193 | case ColorModifier::OneMinusSourceGreen: | ||
| 194 | shader += "vec3(1.0) - "; | ||
| 195 | AppendSource(shader, source, index_name); | ||
| 196 | shader += ".ggg"; | ||
| 197 | break; | ||
| 198 | case ColorModifier::SourceBlue: | ||
| 199 | AppendSource(shader, source, index_name); | ||
| 200 | shader += ".bbb"; | ||
| 201 | break; | ||
| 202 | case ColorModifier::OneMinusSourceBlue: | ||
| 203 | shader += "vec3(1.0) - "; | ||
| 204 | AppendSource(shader, source, index_name); | ||
| 205 | shader += ".bbb"; | ||
| 206 | break; | ||
| 207 | default: | ||
| 208 | shader += "vec3(0.0)"; | ||
| 209 | LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); | ||
| 210 | break; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | void AppendAlphaModifier(std::string& shader, Pica::Regs::TevStageConfig::AlphaModifier modifier, Pica::Regs::TevStageConfig::Source source, const std::string& index_name) { | ||
| 215 | using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier; | ||
| 216 | switch (modifier) { | ||
| 217 | case AlphaModifier::SourceAlpha: | ||
| 218 | AppendSource(shader, source, index_name); | ||
| 219 | shader += ".a"; | ||
| 220 | break; | ||
| 221 | case AlphaModifier::OneMinusSourceAlpha: | ||
| 222 | shader += "1.0 - "; | ||
| 223 | AppendSource(shader, source, index_name); | ||
| 224 | shader += ".a"; | ||
| 225 | break; | ||
| 226 | case AlphaModifier::SourceRed: | ||
| 227 | AppendSource(shader, source, index_name); | ||
| 228 | shader += ".r"; | ||
| 229 | break; | ||
| 230 | case AlphaModifier::OneMinusSourceRed: | ||
| 231 | shader += "1.0 - "; | ||
| 232 | AppendSource(shader, source, index_name); | ||
| 233 | shader += ".r"; | ||
| 234 | break; | ||
| 235 | case AlphaModifier::SourceGreen: | ||
| 236 | AppendSource(shader, source, index_name); | ||
| 237 | shader += ".g"; | ||
| 238 | break; | ||
| 239 | case AlphaModifier::OneMinusSourceGreen: | ||
| 240 | shader += "1.0 - "; | ||
| 241 | AppendSource(shader, source, index_name); | ||
| 242 | shader += ".g"; | ||
| 243 | break; | ||
| 244 | case AlphaModifier::SourceBlue: | ||
| 245 | AppendSource(shader, source, index_name); | ||
| 246 | shader += ".b"; | ||
| 247 | break; | ||
| 248 | case AlphaModifier::OneMinusSourceBlue: | ||
| 249 | shader += "1.0 - "; | ||
| 250 | AppendSource(shader, source, index_name); | ||
| 251 | shader += ".b"; | ||
| 252 | break; | ||
| 253 | default: | ||
| 254 | shader += "vec3(0.0)"; | ||
| 255 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void AppendColorCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { | ||
| 261 | using Operation = Pica::Regs::TevStageConfig::Operation; | ||
| 262 | |||
| 263 | switch (operation) { | ||
| 264 | case Operation::Replace: | ||
| 265 | shader += variable_name + "[0]"; | ||
| 266 | break; | ||
| 267 | case Operation::Modulate: | ||
| 268 | shader += variable_name + "[0] * " + variable_name + "[1]"; | ||
| 269 | break; | ||
| 270 | case Operation::Add: | ||
| 271 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0))"; | ||
| 272 | break; | ||
| 273 | case Operation::AddSigned: | ||
| 274 | shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - vec3(0.5), vec3(0.0), vec3(1.0))"; | ||
| 275 | break; | ||
| 276 | case Operation::Lerp: | ||
| 277 | shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])"; | ||
| 278 | break; | ||
| 279 | case Operation::Subtract: | ||
| 280 | shader += "max(" + variable_name + "[0] - " + variable_name + "[1], vec3(0.0))"; | ||
| 281 | break; | ||
| 282 | case Operation::MultiplyThenAdd: | ||
| 283 | shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], vec3(1.0))"; | ||
| 284 | break; | ||
| 285 | case Operation::AddThenMultiply: | ||
| 286 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]"; | ||
| 287 | break; | ||
| 288 | default: | ||
| 289 | shader += "vec3(0.0)"; | ||
| 290 | LOG_CRITICAL(Render_OpenGL, "Unknown color comb op %u", operation); | ||
| 291 | break; | ||
| 292 | } | ||
| 293 | } | ||
| 294 | |||
| 295 | void AppendAlphaCombiner(std::string& shader, Pica::Regs::TevStageConfig::Operation operation, const std::string& variable_name) { | ||
| 296 | using Operation = Pica::Regs::TevStageConfig::Operation; | ||
| 297 | switch (operation) { | ||
| 298 | case Operation::Replace: | ||
| 299 | shader += variable_name + "[0]"; | ||
| 300 | break; | ||
| 301 | case Operation::Modulate: | ||
| 302 | shader += variable_name + "[0] * " + variable_name + "[1]"; | ||
| 303 | break; | ||
| 304 | case Operation::Add: | ||
| 305 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0)"; | ||
| 306 | break; | ||
| 307 | case Operation::AddSigned: | ||
| 308 | shader += "clamp(" + variable_name + "[0] + " + variable_name + "[1] - 0.5, 0.0, 1.0)"; | ||
| 309 | break; | ||
| 310 | case Operation::Lerp: | ||
| 311 | shader += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])"; | ||
| 312 | break; | ||
| 313 | case Operation::Subtract: | ||
| 314 | shader += "max(" + variable_name + "[0] - " + variable_name + "[1], 0.0)"; | ||
| 315 | break; | ||
| 316 | case Operation::MultiplyThenAdd: | ||
| 317 | shader += "min(" + variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2], 1.0)"; | ||
| 318 | break; | ||
| 319 | case Operation::AddThenMultiply: | ||
| 320 | shader += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]"; | ||
| 321 | break; | ||
| 322 | default: | ||
| 323 | shader += "0.0"; | ||
| 324 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner op %u", operation); | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | void AppendAlphaTestCondition(std::string& shader, Pica::Regs::CompareFunc func) { | ||
| 330 | using CompareFunc = Pica::Regs::CompareFunc; | ||
| 331 | switch (func) { | ||
| 332 | case CompareFunc::Never: | ||
| 333 | shader += "true"; | ||
| 334 | break; | ||
| 335 | case CompareFunc::Always: | ||
| 336 | shader += "false"; | ||
| 337 | break; | ||
| 338 | case CompareFunc::Equal: | ||
| 339 | shader += "int(g_last_tex_env_out.a * 255.0f) != alphatest_ref"; | ||
| 340 | break; | ||
| 341 | case CompareFunc::NotEqual: | ||
| 342 | shader += "int(g_last_tex_env_out.a * 255.0f) == alphatest_ref"; | ||
| 343 | break; | ||
| 344 | case CompareFunc::LessThan: | ||
| 345 | shader += "int(g_last_tex_env_out.a * 255.0f) >= alphatest_ref"; | ||
| 346 | break; | ||
| 347 | case CompareFunc::LessThanOrEqual: | ||
| 348 | shader += "int(g_last_tex_env_out.a * 255.0f) > alphatest_ref"; | ||
| 349 | break; | ||
| 350 | case CompareFunc::GreaterThan: | ||
| 351 | shader += "int(g_last_tex_env_out.a * 255.0f) <= alphatest_ref"; | ||
| 352 | break; | ||
| 353 | case CompareFunc::GreaterThanOrEqual: | ||
| 354 | shader += "int(g_last_tex_env_out.a * 255.0f) < alphatest_ref"; | ||
| 355 | break; | ||
| 356 | default: | ||
| 357 | shader += "false"; | ||
| 358 | LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); | ||
| 359 | break; | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | std::string GenerateFragmentShader(const ShaderCacheKey& config) { | ||
| 364 | std::string shader = R"( | ||
| 365 | #version 150 core | ||
| 366 | |||
| 367 | #define NUM_VTX_ATTR 7 | ||
| 368 | #define NUM_TEV_STAGES 6 | ||
| 369 | |||
| 370 | in vec4 o[NUM_VTX_ATTR]; | ||
| 371 | out vec4 color; | ||
| 372 | |||
| 373 | uniform int alphatest_ref; | ||
| 374 | uniform vec4 const_color[NUM_TEV_STAGES]; | ||
| 375 | uniform sampler2D tex[3]; | ||
| 376 | |||
| 377 | uniform vec4 tev_combiner_buffer_color; | ||
| 378 | |||
| 379 | void main(void) { | ||
| 380 | vec4 g_combiner_buffer = tev_combiner_buffer_color; | ||
| 381 | vec4 g_last_tex_env_out = vec4(0.0, 0.0, 0.0, 0.0); | ||
| 382 | )"; | ||
| 383 | |||
| 384 | // Do not do any sort of processing if it's obvious we're not going to pass the alpha test | ||
| 385 | if (config.alpha_test_func == Pica::Regs::CompareFunc::Never) { | ||
| 386 | shader += "discard;"; | ||
| 387 | return shader; | ||
| 388 | } | ||
| 389 | |||
| 390 | auto& tev_stages = config.tev_stages; | ||
| 391 | for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) { | ||
| 392 | auto& tev_stage = tev_stages[tev_stage_index]; | ||
| 393 | if (!IsPassThroughTevStage(tev_stage)) { | ||
| 394 | std::string index_name = std::to_string(tev_stage_index); | ||
| 395 | |||
| 396 | shader += "vec3 color_results_" + index_name + "[3] = vec3[3]("; | ||
| 397 | AppendColorModifier(shader, tev_stage.color_modifier1, tev_stage.color_source1, index_name); | ||
| 398 | shader += ", "; | ||
| 399 | AppendColorModifier(shader, tev_stage.color_modifier2, tev_stage.color_source2, index_name); | ||
| 400 | shader += ", "; | ||
| 401 | AppendColorModifier(shader, tev_stage.color_modifier3, tev_stage.color_source3, index_name); | ||
| 402 | shader += ");\n"; | ||
| 403 | |||
| 404 | shader += "vec3 color_output_" + index_name + " = "; | ||
| 405 | AppendColorCombiner(shader, tev_stage.color_op, "color_results_" + index_name); | ||
| 406 | shader += ";\n"; | ||
| 407 | |||
| 408 | shader += "float alpha_results_" + index_name + "[3] = float[3]("; | ||
| 409 | AppendAlphaModifier(shader, tev_stage.alpha_modifier1, tev_stage.alpha_source1, index_name); | ||
| 410 | shader += ", "; | ||
| 411 | AppendAlphaModifier(shader, tev_stage.alpha_modifier2, tev_stage.alpha_source2, index_name); | ||
| 412 | shader += ", "; | ||
| 413 | AppendAlphaModifier(shader, tev_stage.alpha_modifier3, tev_stage.alpha_source3, index_name); | ||
| 414 | shader += ");\n"; | ||
| 415 | |||
| 416 | shader += "float alpha_output_" + index_name + " = "; | ||
| 417 | AppendAlphaCombiner(shader, tev_stage.alpha_op, "alpha_results_" + index_name); | ||
| 418 | shader += ";\n"; | ||
| 419 | |||
| 420 | 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"; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (config.TevStageUpdatesCombinerBufferColor(tev_stage_index)) | ||
| 424 | shader += "g_combiner_buffer.rgb = g_last_tex_env_out.rgb;\n"; | ||
| 425 | |||
| 426 | if (config.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) | ||
| 427 | shader += "g_combiner_buffer.a = g_last_tex_env_out.a;\n"; | ||
| 428 | } | ||
| 429 | |||
| 430 | if (config.alpha_test_func != Pica::Regs::CompareFunc::Always) { | ||
| 431 | shader += "if ("; | ||
| 432 | AppendAlphaTestCondition(shader, config.alpha_test_func); | ||
| 433 | shader += ") {\n discard;\n }\n"; | ||
| 434 | } | ||
| 435 | |||
| 436 | shader += "color = g_last_tex_env_out;\n}"; | ||
| 437 | return shader; | ||
| 438 | } | ||
| 439 | } | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index ca62c83ba..6e2d007f8 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <glad/glad.h> | 7 | #include <glad/glad.h> |
| 8 | 8 | ||
| 9 | namespace ShaderUtil { | 9 | namespace GLShader { |
| 10 | 10 | ||
| 11 | enum Attributes { | 11 | enum Attributes { |
| 12 | ATTRIBUTE_POSITION = 0, | 12 | ATTRIBUTE_POSITION = 0, |
| @@ -14,6 +14,6 @@ enum Attributes { | |||
| 14 | ATTRIBUTE_TEXCOORDS = 2, | 14 | ATTRIBUTE_TEXCOORDS = 2, |
| 15 | }; | 15 | }; |
| 16 | 16 | ||
| 17 | GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); | 17 | GLuint LoadProgram(const char* vertex_file_path, const char* fragment_file_path); |
| 18 | 18 | ||
| 19 | } | 19 | } // namespace |
diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h deleted file mode 100644 index 2ba2c6b0f..000000000 --- a/src/video_core/renderer_opengl/gl_shaders.h +++ /dev/null | |||
| @@ -1,339 +0,0 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | namespace GLShaders { | ||
| 8 | |||
| 9 | const char g_vertex_shader[] = R"( | ||
| 10 | #version 150 core | ||
| 11 | |||
| 12 | in vec2 vert_position; | ||
| 13 | in vec2 vert_tex_coord; | ||
| 14 | out vec2 frag_tex_coord; | ||
| 15 | |||
| 16 | // This is a truncated 3x3 matrix for 2D transformations: | ||
| 17 | // The upper-left 2x2 submatrix performs scaling/rotation/mirroring. | ||
| 18 | // The third column performs translation. | ||
| 19 | // The third row could be used for projection, which we don't need in 2D. It hence is assumed to | ||
| 20 | // implicitly be [0, 0, 1] | ||
| 21 | uniform mat3x2 modelview_matrix; | ||
| 22 | |||
| 23 | void main() { | ||
| 24 | // Multiply input position by the rotscale part of the matrix and then manually translate by | ||
| 25 | // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector | ||
| 26 | // to `vec3(vert_position.xy, 1.0)` | ||
| 27 | gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); | ||
| 28 | frag_tex_coord = vert_tex_coord; | ||
| 29 | } | ||
| 30 | )"; | ||
| 31 | |||
| 32 | const char g_fragment_shader[] = R"( | ||
| 33 | #version 150 core | ||
| 34 | |||
| 35 | in vec2 frag_tex_coord; | ||
| 36 | out vec4 color; | ||
| 37 | |||
| 38 | uniform sampler2D color_texture; | ||
| 39 | |||
| 40 | void main() { | ||
| 41 | color = texture(color_texture, frag_tex_coord); | ||
| 42 | } | ||
| 43 | )"; | ||
| 44 | |||
| 45 | const char g_vertex_shader_hw[] = R"( | ||
| 46 | #version 150 core | ||
| 47 | |||
| 48 | #define NUM_VTX_ATTR 7 | ||
| 49 | |||
| 50 | in vec4 vert_position; | ||
| 51 | in vec4 vert_color; | ||
| 52 | in vec2 vert_texcoords0; | ||
| 53 | in vec2 vert_texcoords1; | ||
| 54 | in vec2 vert_texcoords2; | ||
| 55 | |||
| 56 | out vec4 o[NUM_VTX_ATTR]; | ||
| 57 | |||
| 58 | void main() { | ||
| 59 | o[2] = vert_color; | ||
| 60 | o[3] = vec4(vert_texcoords0.xy, vert_texcoords1.xy); | ||
| 61 | o[5] = vec4(0.0, 0.0, vert_texcoords2.xy); | ||
| 62 | |||
| 63 | gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w); | ||
| 64 | } | ||
| 65 | )"; | ||
| 66 | |||
| 67 | // TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms | ||
| 68 | const char g_fragment_shader_hw[] = R"( | ||
| 69 | #version 150 core | ||
| 70 | |||
| 71 | #define NUM_VTX_ATTR 7 | ||
| 72 | #define NUM_TEV_STAGES 6 | ||
| 73 | |||
| 74 | #define SOURCE_PRIMARYCOLOR 0x0 | ||
| 75 | #define SOURCE_PRIMARYFRAGMENTCOLOR 0x1 | ||
| 76 | #define SOURCE_SECONDARYFRAGMENTCOLOR 0x2 | ||
| 77 | #define SOURCE_TEXTURE0 0x3 | ||
| 78 | #define SOURCE_TEXTURE1 0x4 | ||
| 79 | #define SOURCE_TEXTURE2 0x5 | ||
| 80 | #define SOURCE_TEXTURE3 0x6 | ||
| 81 | #define SOURCE_PREVIOUSBUFFER 0xd | ||
| 82 | #define SOURCE_CONSTANT 0xe | ||
| 83 | #define SOURCE_PREVIOUS 0xf | ||
| 84 | |||
| 85 | #define COLORMODIFIER_SOURCECOLOR 0x0 | ||
| 86 | #define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1 | ||
| 87 | #define COLORMODIFIER_SOURCEALPHA 0x2 | ||
| 88 | #define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3 | ||
| 89 | #define COLORMODIFIER_SOURCERED 0x4 | ||
| 90 | #define COLORMODIFIER_ONEMINUSSOURCERED 0x5 | ||
| 91 | #define COLORMODIFIER_SOURCEGREEN 0x8 | ||
| 92 | #define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9 | ||
| 93 | #define COLORMODIFIER_SOURCEBLUE 0xc | ||
| 94 | #define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd | ||
| 95 | |||
| 96 | #define ALPHAMODIFIER_SOURCEALPHA 0x0 | ||
| 97 | #define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1 | ||
| 98 | #define ALPHAMODIFIER_SOURCERED 0x2 | ||
| 99 | #define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3 | ||
| 100 | #define ALPHAMODIFIER_SOURCEGREEN 0x4 | ||
| 101 | #define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5 | ||
| 102 | #define ALPHAMODIFIER_SOURCEBLUE 0x6 | ||
| 103 | #define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7 | ||
| 104 | |||
| 105 | #define OPERATION_REPLACE 0 | ||
| 106 | #define OPERATION_MODULATE 1 | ||
| 107 | #define OPERATION_ADD 2 | ||
| 108 | #define OPERATION_ADDSIGNED 3 | ||
| 109 | #define OPERATION_LERP 4 | ||
| 110 | #define OPERATION_SUBTRACT 5 | ||
| 111 | #define OPERATION_MULTIPLYTHENADD 8 | ||
| 112 | #define OPERATION_ADDTHENMULTIPLY 9 | ||
| 113 | |||
| 114 | #define COMPAREFUNC_NEVER 0 | ||
| 115 | #define COMPAREFUNC_ALWAYS 1 | ||
| 116 | #define COMPAREFUNC_EQUAL 2 | ||
| 117 | #define COMPAREFUNC_NOTEQUAL 3 | ||
| 118 | #define COMPAREFUNC_LESSTHAN 4 | ||
| 119 | #define COMPAREFUNC_LESSTHANOREQUAL 5 | ||
| 120 | #define COMPAREFUNC_GREATERTHAN 6 | ||
| 121 | #define COMPAREFUNC_GREATERTHANOREQUAL 7 | ||
| 122 | |||
| 123 | in vec4 o[NUM_VTX_ATTR]; | ||
| 124 | out vec4 color; | ||
| 125 | |||
| 126 | uniform bool alphatest_enabled; | ||
| 127 | uniform int alphatest_func; | ||
| 128 | uniform float alphatest_ref; | ||
| 129 | |||
| 130 | uniform sampler2D tex[3]; | ||
| 131 | |||
| 132 | uniform vec4 tev_combiner_buffer_color; | ||
| 133 | |||
| 134 | struct TEVConfig | ||
| 135 | { | ||
| 136 | bool enabled; | ||
| 137 | ivec3 color_sources; | ||
| 138 | ivec3 alpha_sources; | ||
| 139 | ivec3 color_modifiers; | ||
| 140 | ivec3 alpha_modifiers; | ||
| 141 | ivec2 color_alpha_op; | ||
| 142 | ivec2 color_alpha_multiplier; | ||
| 143 | vec4 const_color; | ||
| 144 | bvec2 updates_combiner_buffer_color_alpha; | ||
| 145 | }; | ||
| 146 | |||
| 147 | uniform TEVConfig tev_cfgs[NUM_TEV_STAGES]; | ||
| 148 | |||
| 149 | vec4 g_combiner_buffer; | ||
| 150 | vec4 g_last_tex_env_out; | ||
| 151 | vec4 g_const_color; | ||
| 152 | |||
| 153 | vec4 GetSource(int source) { | ||
| 154 | if (source == SOURCE_PRIMARYCOLOR) { | ||
| 155 | return o[2]; | ||
| 156 | } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) { | ||
| 157 | // HACK: Until we implement fragment lighting, use primary_color | ||
| 158 | return o[2]; | ||
| 159 | } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) { | ||
| 160 | // HACK: Until we implement fragment lighting, use zero | ||
| 161 | return vec4(0.0, 0.0, 0.0, 0.0); | ||
| 162 | } else if (source == SOURCE_TEXTURE0) { | ||
| 163 | return texture(tex[0], o[3].xy); | ||
| 164 | } else if (source == SOURCE_TEXTURE1) { | ||
| 165 | return texture(tex[1], o[3].zw); | ||
| 166 | } else if (source == SOURCE_TEXTURE2) { | ||
| 167 | // TODO: Unverified | ||
| 168 | return texture(tex[2], o[5].zw); | ||
| 169 | } else if (source == SOURCE_TEXTURE3) { | ||
| 170 | // TODO: no 4th texture? | ||
| 171 | } else if (source == SOURCE_PREVIOUSBUFFER) { | ||
| 172 | return g_combiner_buffer; | ||
| 173 | } else if (source == SOURCE_CONSTANT) { | ||
| 174 | return g_const_color; | ||
| 175 | } else if (source == SOURCE_PREVIOUS) { | ||
| 176 | return g_last_tex_env_out; | ||
| 177 | } | ||
| 178 | |||
| 179 | return vec4(0.0); | ||
| 180 | } | ||
| 181 | |||
| 182 | vec3 GetColorModifier(int factor, vec4 color) { | ||
| 183 | if (factor == COLORMODIFIER_SOURCECOLOR) { | ||
| 184 | return color.rgb; | ||
| 185 | } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) { | ||
| 186 | return vec3(1.0) - color.rgb; | ||
| 187 | } else if (factor == COLORMODIFIER_SOURCEALPHA) { | ||
| 188 | return color.aaa; | ||
| 189 | } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) { | ||
| 190 | return vec3(1.0) - color.aaa; | ||
| 191 | } else if (factor == COLORMODIFIER_SOURCERED) { | ||
| 192 | return color.rrr; | ||
| 193 | } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) { | ||
| 194 | return vec3(1.0) - color.rrr; | ||
| 195 | } else if (factor == COLORMODIFIER_SOURCEGREEN) { | ||
| 196 | return color.ggg; | ||
| 197 | } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) { | ||
| 198 | return vec3(1.0) - color.ggg; | ||
| 199 | } else if (factor == COLORMODIFIER_SOURCEBLUE) { | ||
| 200 | return color.bbb; | ||
| 201 | } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) { | ||
| 202 | return vec3(1.0) - color.bbb; | ||
| 203 | } | ||
| 204 | |||
| 205 | return vec3(0.0); | ||
| 206 | } | ||
| 207 | |||
| 208 | float GetAlphaModifier(int factor, vec4 color) { | ||
| 209 | if (factor == ALPHAMODIFIER_SOURCEALPHA) { | ||
| 210 | return color.a; | ||
| 211 | } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) { | ||
| 212 | return 1.0 - color.a; | ||
| 213 | } else if (factor == ALPHAMODIFIER_SOURCERED) { | ||
| 214 | return color.r; | ||
| 215 | } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) { | ||
| 216 | return 1.0 - color.r; | ||
| 217 | } else if (factor == ALPHAMODIFIER_SOURCEGREEN) { | ||
| 218 | return color.g; | ||
| 219 | } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) { | ||
| 220 | return 1.0 - color.g; | ||
| 221 | } else if (factor == ALPHAMODIFIER_SOURCEBLUE) { | ||
| 222 | return color.b; | ||
| 223 | } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) { | ||
| 224 | return 1.0 - color.b; | ||
| 225 | } | ||
| 226 | |||
| 227 | return 0.0; | ||
| 228 | } | ||
| 229 | |||
| 230 | vec3 ColorCombine(int op, vec3 color[3]) { | ||
| 231 | if (op == OPERATION_REPLACE) { | ||
| 232 | return color[0]; | ||
| 233 | } else if (op == OPERATION_MODULATE) { | ||
| 234 | return color[0] * color[1]; | ||
| 235 | } else if (op == OPERATION_ADD) { | ||
| 236 | return min(color[0] + color[1], 1.0); | ||
| 237 | } else if (op == OPERATION_ADDSIGNED) { | ||
| 238 | return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0); | ||
| 239 | } else if (op == OPERATION_LERP) { | ||
| 240 | return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]); | ||
| 241 | } else if (op == OPERATION_SUBTRACT) { | ||
| 242 | return max(color[0] - color[1], 0.0); | ||
| 243 | } else if (op == OPERATION_MULTIPLYTHENADD) { | ||
| 244 | return min(color[0] * color[1] + color[2], 1.0); | ||
| 245 | } else if (op == OPERATION_ADDTHENMULTIPLY) { | ||
| 246 | return min(color[0] + color[1], 1.0) * color[2]; | ||
| 247 | } | ||
| 248 | |||
| 249 | return vec3(0.0); | ||
| 250 | } | ||
| 251 | |||
| 252 | float AlphaCombine(int op, float alpha[3]) { | ||
| 253 | if (op == OPERATION_REPLACE) { | ||
| 254 | return alpha[0]; | ||
| 255 | } else if (op == OPERATION_MODULATE) { | ||
| 256 | return alpha[0] * alpha[1]; | ||
| 257 | } else if (op == OPERATION_ADD) { | ||
| 258 | return min(alpha[0] + alpha[1], 1.0); | ||
| 259 | } else if (op == OPERATION_ADDSIGNED) { | ||
| 260 | return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0); | ||
| 261 | } else if (op == OPERATION_LERP) { | ||
| 262 | return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]); | ||
| 263 | } else if (op == OPERATION_SUBTRACT) { | ||
| 264 | return max(alpha[0] - alpha[1], 0.0); | ||
| 265 | } else if (op == OPERATION_MULTIPLYTHENADD) { | ||
| 266 | return min(alpha[0] * alpha[1] + alpha[2], 1.0); | ||
| 267 | } else if (op == OPERATION_ADDTHENMULTIPLY) { | ||
| 268 | return min(alpha[0] + alpha[1], 1.0) * alpha[2]; | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0.0; | ||
| 272 | } | ||
| 273 | |||
| 274 | void main(void) { | ||
| 275 | g_combiner_buffer = tev_combiner_buffer_color; | ||
| 276 | |||
| 277 | for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) { | ||
| 278 | if (tev_cfgs[tex_env_idx].enabled) { | ||
| 279 | g_const_color = tev_cfgs[tex_env_idx].const_color; | ||
| 280 | |||
| 281 | vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)), | ||
| 282 | GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)), | ||
| 283 | GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z))); | ||
| 284 | vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results); | ||
| 285 | |||
| 286 | float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)), | ||
| 287 | GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)), | ||
| 288 | GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z))); | ||
| 289 | float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results); | ||
| 290 | |||
| 291 | 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)); | ||
| 292 | } | ||
| 293 | |||
| 294 | if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) { | ||
| 295 | g_combiner_buffer.rgb = g_last_tex_env_out.rgb; | ||
| 296 | } | ||
| 297 | |||
| 298 | if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) { | ||
| 299 | g_combiner_buffer.a = g_last_tex_env_out.a; | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | if (alphatest_enabled) { | ||
| 304 | if (alphatest_func == COMPAREFUNC_NEVER) { | ||
| 305 | discard; | ||
| 306 | } else if (alphatest_func == COMPAREFUNC_ALWAYS) { | ||
| 307 | |||
| 308 | } else if (alphatest_func == COMPAREFUNC_EQUAL) { | ||
| 309 | if (g_last_tex_env_out.a != alphatest_ref) { | ||
| 310 | discard; | ||
| 311 | } | ||
| 312 | } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) { | ||
| 313 | if (g_last_tex_env_out.a == alphatest_ref) { | ||
| 314 | discard; | ||
| 315 | } | ||
| 316 | } else if (alphatest_func == COMPAREFUNC_LESSTHAN) { | ||
| 317 | if (g_last_tex_env_out.a >= alphatest_ref) { | ||
| 318 | discard; | ||
| 319 | } | ||
| 320 | } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) { | ||
| 321 | if (g_last_tex_env_out.a > alphatest_ref) { | ||
| 322 | discard; | ||
| 323 | } | ||
| 324 | } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) { | ||
| 325 | if (g_last_tex_env_out.a <= alphatest_ref) { | ||
| 326 | discard; | ||
| 327 | } | ||
| 328 | } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) { | ||
| 329 | if (g_last_tex_env_out.a < alphatest_ref) { | ||
| 330 | discard; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | color = g_last_tex_env_out; | ||
| 336 | } | ||
| 337 | )"; | ||
| 338 | |||
| 339 | } | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index f1313b54f..ac0a058db 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -21,9 +21,44 @@ | |||
| 21 | #include "video_core/debug_utils/debug_utils.h" | 21 | #include "video_core/debug_utils/debug_utils.h" |
| 22 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 22 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 23 | #include "video_core/renderer_opengl/gl_shader_util.h" | 23 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 24 | #include "video_core/renderer_opengl/gl_shaders.h" | ||
| 25 | #include "video_core/renderer_opengl/renderer_opengl.h" | 24 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 26 | 25 | ||
| 26 | static const char vertex_shader[] = R"( | ||
| 27 | #version 150 core | ||
| 28 | |||
| 29 | in vec2 vert_position; | ||
| 30 | in vec2 vert_tex_coord; | ||
| 31 | out vec2 frag_tex_coord; | ||
| 32 | |||
| 33 | // This is a truncated 3x3 matrix for 2D transformations: | ||
| 34 | // The upper-left 2x2 submatrix performs scaling/rotation/mirroring. | ||
| 35 | // The third column performs translation. | ||
| 36 | // The third row could be used for projection, which we don't need in 2D. It hence is assumed to | ||
| 37 | // implicitly be [0, 0, 1] | ||
| 38 | uniform mat3x2 modelview_matrix; | ||
| 39 | |||
| 40 | void main() { | ||
| 41 | // Multiply input position by the rotscale part of the matrix and then manually translate by | ||
| 42 | // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector | ||
| 43 | // to `vec3(vert_position.xy, 1.0)` | ||
| 44 | gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0); | ||
| 45 | frag_tex_coord = vert_tex_coord; | ||
| 46 | } | ||
| 47 | )"; | ||
| 48 | |||
| 49 | static const char fragment_shader[] = R"( | ||
| 50 | #version 150 core | ||
| 51 | |||
| 52 | in vec2 frag_tex_coord; | ||
| 53 | out vec4 color; | ||
| 54 | |||
| 55 | uniform sampler2D color_texture; | ||
| 56 | |||
| 57 | void main() { | ||
| 58 | color = texture(color_texture, frag_tex_coord); | ||
| 59 | } | ||
| 60 | )"; | ||
| 61 | |||
| 27 | /** | 62 | /** |
| 28 | * Vertex structure that the drawn screen rectangles are composed of. | 63 | * Vertex structure that the drawn screen rectangles are composed of. |
| 29 | */ | 64 | */ |
| @@ -207,7 +242,7 @@ void RendererOpenGL::InitOpenGLObjects() { | |||
| 207 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); | 242 | glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f); |
| 208 | 243 | ||
| 209 | // Link shaders and get variable locations | 244 | // Link shaders and get variable locations |
| 210 | program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader); | 245 | program_id = GLShader::LoadProgram(vertex_shader, fragment_shader); |
| 211 | uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); | 246 | uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix"); |
| 212 | uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); | 247 | uniform_color_texture = glGetUniformLocation(program_id, "color_texture"); |
| 213 | attrib_position = glGetAttribLocation(program_id, "vert_position"); | 248 | attrib_position = glGetAttribLocation(program_id, "vert_position"); |