summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h206
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp78
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h2
3 files changed, 164 insertions, 122 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 82fa61742..4f9a032fb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -39,14 +39,24 @@ struct ScreenInfo;
39 * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where 39 * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
40 * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) 40 * Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
41 * two separate shaders sharing the same key. 41 * two separate shaders sharing the same key.
42 *
43 * We use a union because "implicitly-defined copy/move constructor for a union X copies the object representation of X."
44 * and "implicitly-defined copy assignment operator for a union X copies the object representation (3.9) of X."
45 * = Bytewise copy instead of memberwise copy.
46 * This is important because the padding bytes are included in the hash and comparison between objects.
42 */ 47 */
43struct PicaShaderConfig { 48union PicaShaderConfig {
49
44 /// Construct a PicaShaderConfig with the current Pica register configuration. 50 /// Construct a PicaShaderConfig with the current Pica register configuration.
45 static PicaShaderConfig CurrentConfig() { 51 static PicaShaderConfig CurrentConfig() {
46 PicaShaderConfig res; 52 PicaShaderConfig res;
53
54 auto& state = res.state;
55 std::memset(&state, 0, sizeof(PicaShaderConfig::State));
56
47 const auto& regs = Pica::g_state.regs; 57 const auto& regs = Pica::g_state.regs;
48 58
49 res.alpha_test_func = regs.output_merger.alpha_test.enable ? 59 state.alpha_test_func = regs.output_merger.alpha_test.enable ?
50 regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always; 60 regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
51 61
52 // Copy relevant tev stages fields. 62 // Copy relevant tev stages fields.
@@ -56,123 +66,151 @@ struct PicaShaderConfig {
56 DEBUG_ASSERT(res.tev_stages.size() == tev_stages.size()); 66 DEBUG_ASSERT(res.tev_stages.size() == tev_stages.size());
57 for (size_t i = 0; i < tev_stages.size(); i++) { 67 for (size_t i = 0; i < tev_stages.size(); i++) {
58 const auto& tev_stage = tev_stages[i]; 68 const auto& tev_stage = tev_stages[i];
59 res.tev_stages[i].sources_raw = tev_stage.sources_raw; 69 state.tev_stages[i].sources_raw = tev_stage.sources_raw;
60 res.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw; 70 state.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw;
61 res.tev_stages[i].ops_raw = tev_stage.ops_raw; 71 state.tev_stages[i].ops_raw = tev_stage.ops_raw;
62 res.tev_stages[i].scales_raw = tev_stage.scales_raw; 72 state.tev_stages[i].scales_raw = tev_stage.scales_raw;
63 } 73 }
64 74
65 res.combiner_buffer_input = 75 state.combiner_buffer_input =
66 regs.tev_combiner_buffer_input.update_mask_rgb.Value() | 76 regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
67 regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; 77 regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
68 78
69 // Fragment lighting 79 // Fragment lighting
70 80
71 res.lighting.enable = !regs.lighting.disable; 81 state.lighting.enable = !regs.lighting.disable;
72 res.lighting.src_num = regs.lighting.num_lights + 1; 82 state.lighting.src_num = regs.lighting.num_lights + 1;
73 83
74 for (unsigned light_index = 0; light_index < res.lighting.src_num; ++light_index) { 84 for (unsigned light_index = 0; light_index < state.lighting.src_num; ++light_index) {
75 unsigned num = regs.lighting.light_enable.GetNum(light_index); 85 unsigned num = regs.lighting.light_enable.GetNum(light_index);
76 const auto& light = regs.lighting.light[num]; 86 const auto& light = regs.lighting.light[num];
77 res.lighting.light[light_index].num = num; 87 state.lighting.light[light_index].num = num;
78 res.lighting.light[light_index].directional = light.directional != 0; 88 state.lighting.light[light_index].directional = light.directional != 0;
79 res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0; 89 state.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
80 res.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num); 90 state.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num);
81 res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32(); 91 state.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
82 res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32(); 92 state.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
83 } 93 }
84 94
85 res.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0; 95 state.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0;
86 res.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0; 96 state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
87 res.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value(); 97 state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
88 res.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0); 98 state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
89 99
90 res.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0; 100 state.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0;
91 res.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0; 101 state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
92 res.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); 102 state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
93 res.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); 103 state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
94 104
95 res.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0; 105 state.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0;
96 res.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; 106 state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
97 res.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); 107 state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
98 res.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr); 108 state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
99 109
100 res.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0; 110 state.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0;
101 res.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0; 111 state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
102 res.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value(); 112 state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
103 res.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr); 113 state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
104 114
105 res.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0; 115 state.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0;
106 res.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0; 116 state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
107 res.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value(); 117 state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
108 res.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg); 118 state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
109 119
110 res.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0; 120 state.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0;
111 res.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0; 121 state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
112 res.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value(); 122 state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
113 res.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb); 123 state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
114 124
115 res.lighting.config = regs.lighting.config; 125 state.lighting.config = regs.lighting.config;
116 res.lighting.fresnel_selector = regs.lighting.fresnel_selector; 126 state.lighting.fresnel_selector = regs.lighting.fresnel_selector;
117 res.lighting.bump_mode = regs.lighting.bump_mode; 127 state.lighting.bump_mode = regs.lighting.bump_mode;
118 res.lighting.bump_selector = regs.lighting.bump_selector; 128 state.lighting.bump_selector = regs.lighting.bump_selector;
119 res.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0; 129 state.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0;
120 res.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0; 130 state.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0;
121 131
122 return res; 132 return res;
123 } 133 }
124 134
125 bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { 135 bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
126 return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index)); 136 return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index));
127 } 137 }
128 138
129 bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { 139 bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
130 return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index)); 140 return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index));
131 } 141 }
132 142
133 bool operator ==(const PicaShaderConfig& o) const { 143 bool operator ==(const PicaShaderConfig& o) const {
134 return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0; 144 return std::memcmp(&state, &o.state, sizeof(PicaShaderConfig::State)) == 0;
145 };
146
147 // NOTE: MSVC15 (Update 2) doesn't think `delete`'d constructors and operators are TC.
148 // This makes BitField not TC when used in a union or struct so we have to resort
149 // to this ugly hack.
150 // Once that bug is fixed we can use Pica::Regs::TevStageConfig here.
151 // Doesn't include const_color because we don't sync it, see comment in CurrentConfig()
152 struct TevStageConfigRaw {
153 u32 sources_raw;
154 u32 modifiers_raw;
155 u32 ops_raw;
156 u32 scales_raw;
157 explicit operator Pica::Regs::TevStageConfig() const noexcept {
158 Pica::Regs::TevStageConfig stage;
159 stage.sources_raw = sources_raw;
160 stage.modifiers_raw = modifiers_raw;
161 stage.ops_raw = ops_raw;
162 stage.const_color = 0;
163 stage.scales_raw = scales_raw;
164 return stage;
165 }
135 }; 166 };
136 167
137 Pica::Regs::CompareFunc alpha_test_func = Pica::Regs::CompareFunc::Never; 168 struct State {
138 std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
139 u8 combiner_buffer_input = 0;
140 169
141 struct { 170 Pica::Regs::CompareFunc alpha_test_func;
142 struct { 171 std::array<TevStageConfigRaw, 6> tev_stages;
143 unsigned num = 0; 172 u8 combiner_buffer_input;
144 bool directional = false;
145 bool two_sided_diffuse = false;
146 bool dist_atten_enable = false;
147 GLfloat dist_atten_scale = 0.0f;
148 GLfloat dist_atten_bias = 0.0f;
149 } light[8];
150
151 bool enable = false;
152 unsigned src_num = 0;
153 Pica::Regs::LightingBumpMode bump_mode = Pica::Regs::LightingBumpMode::None;
154 unsigned bump_selector = 0;
155 bool bump_renorm = false;
156 bool clamp_highlights = false;
157
158 Pica::Regs::LightingConfig config = Pica::Regs::LightingConfig::Config0;
159 Pica::Regs::LightingFresnelSelector fresnel_selector = Pica::Regs::LightingFresnelSelector::None;
160 173
161 struct { 174 struct {
162 bool enable = false; 175 struct {
163 bool abs_input = false; 176 unsigned num;
164 Pica::Regs::LightingLutInput type = Pica::Regs::LightingLutInput::NH; 177 bool directional;
165 float scale = 1.0f; 178 bool two_sided_diffuse;
166 } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; 179 bool dist_atten_enable;
167 } lighting; 180 GLfloat dist_atten_scale;
181 GLfloat dist_atten_bias;
182 } light[8];
183
184 bool enable;
185 unsigned src_num;
186 Pica::Regs::LightingBumpMode bump_mode;
187 unsigned bump_selector;
188 bool bump_renorm;
189 bool clamp_highlights;
190
191 Pica::Regs::LightingConfig config;
192 Pica::Regs::LightingFresnelSelector fresnel_selector;
193
194 struct {
195 bool enable;
196 bool abs_input;
197 Pica::Regs::LightingLutInput type;
198 float scale;
199 } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
200 } lighting;
201
202 } state;
168}; 203};
204#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
205static_assert(std::is_trivially_copyable<PicaShaderConfig::State>::value, "PicaShaderConfig::State must be trivially copyable");
206#endif
169 207
170namespace std { 208namespace std {
171 209
172template <> 210template <>
173struct hash<PicaShaderConfig> { 211struct hash<PicaShaderConfig> {
174 size_t operator()(const PicaShaderConfig& k) const { 212 size_t operator()(const PicaShaderConfig& k) const {
175 return Common::ComputeHash64(&k, sizeof(PicaShaderConfig)); 213 return Common::ComputeHash64(&k.state, sizeof(PicaShaderConfig::State));
176 } 214 }
177}; 215};
178 216
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 9011caa39..0890adb12 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -287,7 +287,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
287 287
288/// Writes the code to emulate the specified TEV stage 288/// Writes the code to emulate the specified TEV stage
289static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) { 289static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
290 auto& stage = config.tev_stages[index]; 290 const auto stage = static_cast<const Pica::Regs::TevStageConfig>(config.state.tev_stages[index]);
291 if (!IsPassThroughTevStage(stage)) { 291 if (!IsPassThroughTevStage(stage)) {
292 std::string index_name = std::to_string(index); 292 std::string index_name = std::to_string(index);
293 293
@@ -331,6 +331,8 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
331 331
332/// Writes the code to emulate fragment lighting 332/// Writes the code to emulate fragment lighting
333static void WriteLighting(std::string& out, const PicaShaderConfig& config) { 333static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
334 const auto& lighting = config.state.lighting;
335
334 // Define lighting globals 336 // Define lighting globals
335 out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" 337 out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
336 "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" 338 "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
@@ -338,17 +340,17 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
338 "vec3 refl_value = vec3(0.0);\n"; 340 "vec3 refl_value = vec3(0.0);\n";
339 341
340 // Compute fragment normals 342 // Compute fragment normals
341 if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) { 343 if (lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
342 // Bump mapping is enabled using a normal map, read perturbation vector from the selected texture 344 // Bump mapping is enabled using a normal map, read perturbation vector from the selected texture
343 std::string bump_selector = std::to_string(config.lighting.bump_selector); 345 std::string bump_selector = std::to_string(lighting.bump_selector);
344 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n"; 346 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n";
345 347
346 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result 348 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result
347 if (config.lighting.bump_renorm) { 349 if (lighting.bump_renorm) {
348 std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; 350 std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
349 out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; 351 out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
350 } 352 }
351 } else if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) { 353 } else if (lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
352 // Bump mapping is enabled using a tangent map 354 // Bump mapping is enabled using a tangent map
353 LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)"); 355 LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
354 UNIMPLEMENTED(); 356 UNIMPLEMENTED();
@@ -361,7 +363,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
361 out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n"; 363 out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
362 364
363 // Gets the index into the specified lookup table for specular lighting 365 // Gets the index into the specified lookup table for specular lighting
364 auto GetLutIndex = [config](unsigned light_num, Regs::LightingLutInput input, bool abs) { 366 auto GetLutIndex = [&lighting](unsigned light_num, Regs::LightingLutInput input, bool abs) {
365 const std::string half_angle = "normalize(normalize(view) + light_vector)"; 367 const std::string half_angle = "normalize(normalize(view) + light_vector)";
366 std::string index; 368 std::string index;
367 switch (input) { 369 switch (input) {
@@ -389,7 +391,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
389 391
390 if (abs) { 392 if (abs) {
391 // LUT index is in the range of (0.0, 1.0) 393 // LUT index is in the range of (0.0, 1.0)
392 index = config.lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)"; 394 index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
393 return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))"; 395 return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))";
394 } else { 396 } else {
395 // LUT index is in the range of (-1.0, 1.0) 397 // LUT index is in the range of (-1.0, 1.0)
@@ -407,8 +409,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
407 }; 409 };
408 410
409 // Write the code to emulate each enabled light 411 // Write the code to emulate each enabled light
410 for (unsigned light_index = 0; light_index < config.lighting.src_num; ++light_index) { 412 for (unsigned light_index = 0; light_index < lighting.src_num; ++light_index) {
411 const auto& light_config = config.lighting.light[light_index]; 413 const auto& light_config = lighting.light[light_index];
412 std::string light_src = "light_src[" + std::to_string(light_config.num) + "]"; 414 std::string light_src = "light_src[" + std::to_string(light_config.num) + "]";
413 415
414 // Compute light vector (directional or positional) 416 // Compute light vector (directional or positional)
@@ -432,39 +434,39 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
432 } 434 }
433 435
434 // If enabled, clamp specular component if lighting result is negative 436 // If enabled, clamp specular component if lighting result is negative
435 std::string clamp_highlights = config.lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; 437 std::string clamp_highlights = lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
436 438
437 // Specular 0 component 439 // Specular 0 component
438 std::string d0_lut_value = "1.0"; 440 std::string d0_lut_value = "1.0";
439 if (config.lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution0)) { 441 if (lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Distribution0)) {
440 // Lookup specular "distribution 0" LUT value 442 // Lookup specular "distribution 0" LUT value
441 std::string index = GetLutIndex(light_config.num, config.lighting.lut_d0.type, config.lighting.lut_d0.abs_input); 443 std::string index = GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input);
442 d0_lut_value = "(" + std::to_string(config.lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")"; 444 d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
443 } 445 }
444 std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; 446 std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
445 447
446 // If enabled, lookup ReflectRed value, otherwise, 1.0 is used 448 // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
447 if (config.lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectRed)) { 449 if (lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectRed)) {
448 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rr.type, config.lighting.lut_rr.abs_input); 450 std::string index = GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input);
449 std::string value = "(" + std::to_string(config.lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")"; 451 std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
450 out += "refl_value.r = " + value + ";\n"; 452 out += "refl_value.r = " + value + ";\n";
451 } else { 453 } else {
452 out += "refl_value.r = 1.0;\n"; 454 out += "refl_value.r = 1.0;\n";
453 } 455 }
454 456
455 // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used 457 // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
456 if (config.lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) { 458 if (lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) {
457 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rg.type, config.lighting.lut_rg.abs_input); 459 std::string index = GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input);
458 std::string value = "(" + std::to_string(config.lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")"; 460 std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
459 out += "refl_value.g = " + value + ";\n"; 461 out += "refl_value.g = " + value + ";\n";
460 } else { 462 } else {
461 out += "refl_value.g = refl_value.r;\n"; 463 out += "refl_value.g = refl_value.r;\n";
462 } 464 }
463 465
464 // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used 466 // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
465 if (config.lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) { 467 if (lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) {
466 std::string index = GetLutIndex(light_config.num, config.lighting.lut_rb.type, config.lighting.lut_rb.abs_input); 468 std::string index = GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input);
467 std::string value = "(" + std::to_string(config.lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")"; 469 std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
468 out += "refl_value.b = " + value + ";\n"; 470 out += "refl_value.b = " + value + ";\n";
469 } else { 471 } else {
470 out += "refl_value.b = refl_value.r;\n"; 472 out += "refl_value.b = refl_value.r;\n";
@@ -472,27 +474,27 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
472 474
473 // Specular 1 component 475 // Specular 1 component
474 std::string d1_lut_value = "1.0"; 476 std::string d1_lut_value = "1.0";
475 if (config.lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution1)) { 477 if (lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Distribution1)) {
476 // Lookup specular "distribution 1" LUT value 478 // Lookup specular "distribution 1" LUT value
477 std::string index = GetLutIndex(light_config.num, config.lighting.lut_d1.type, config.lighting.lut_d1.abs_input); 479 std::string index = GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input);
478 d1_lut_value = "(" + std::to_string(config.lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")"; 480 d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
479 } 481 }
480 std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; 482 std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
481 483
482 // Fresnel 484 // Fresnel
483 if (config.lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Fresnel)) { 485 if (lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
484 // Lookup fresnel LUT value 486 // Lookup fresnel LUT value
485 std::string index = GetLutIndex(light_config.num, config.lighting.lut_fr.type, config.lighting.lut_fr.abs_input); 487 std::string index = GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input);
486 std::string value = "(" + std::to_string(config.lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")"; 488 std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
487 489
488 // Enabled for difffuse lighting alpha component 490 // Enabled for difffuse lighting alpha component
489 if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha || 491 if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
490 config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both) 492 lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
491 out += "diffuse_sum.a *= " + value + ";\n"; 493 out += "diffuse_sum.a *= " + value + ";\n";
492 494
493 // Enabled for the specular lighting alpha component 495 // Enabled for the specular lighting alpha component
494 if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha || 496 if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
495 config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both) 497 lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
496 out += "specular_sum.a *= " + value + ";\n"; 498 out += "specular_sum.a *= " + value + ";\n";
497 } 499 }
498 500
@@ -510,6 +512,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
510} 512}
511 513
512std::string GenerateFragmentShader(const PicaShaderConfig& config) { 514std::string GenerateFragmentShader(const PicaShaderConfig& config) {
515 const auto& state = config.state;
516
513 std::string out = R"( 517 std::string out = R"(
514#version 330 core 518#version 330 core
515#define NUM_TEV_STAGES 6 519#define NUM_TEV_STAGES 6
@@ -555,24 +559,24 @@ vec4 secondary_fragment_color = vec4(0.0);
555)"; 559)";
556 560
557 // Do not do any sort of processing if it's obvious we're not going to pass the alpha test 561 // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
558 if (config.alpha_test_func == Regs::CompareFunc::Never) { 562 if (state.alpha_test_func == Regs::CompareFunc::Never) {
559 out += "discard; }"; 563 out += "discard; }";
560 return out; 564 return out;
561 } 565 }
562 566
563 if (config.lighting.enable) 567 if (state.lighting.enable)
564 WriteLighting(out, config); 568 WriteLighting(out, config);
565 569
566 out += "vec4 combiner_buffer = vec4(0.0);\n"; 570 out += "vec4 combiner_buffer = vec4(0.0);\n";
567 out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n"; 571 out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n";
568 out += "vec4 last_tex_env_out = vec4(0.0);\n"; 572 out += "vec4 last_tex_env_out = vec4(0.0);\n";
569 573
570 for (size_t index = 0; index < config.tev_stages.size(); ++index) 574 for (size_t index = 0; index < state.tev_stages.size(); ++index)
571 WriteTevStage(out, config, (unsigned)index); 575 WriteTevStage(out, config, (unsigned)index);
572 576
573 if (config.alpha_test_func != Regs::CompareFunc::Always) { 577 if (state.alpha_test_func != Regs::CompareFunc::Always) {
574 out += "if ("; 578 out += "if (";
575 AppendAlphaTestCondition(out, config.alpha_test_func); 579 AppendAlphaTestCondition(out, state.alpha_test_func);
576 out += ") discard;\n"; 580 out += ") discard;\n";
577 } 581 }
578 582
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 3eb07d57a..bef3249cf 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -6,7 +6,7 @@
6 6
7#include <string> 7#include <string>
8 8
9struct PicaShaderConfig; 9union PicaShaderConfig;
10 10
11namespace GLShader { 11namespace GLShader {
12 12