summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar wwylele2017-05-05 15:25:04 +0300
committerGravatar wwylele2017-05-20 13:50:50 +0300
commit4d62e75fb2438fea3e9199db1641a7fe2848222a (patch)
tree090803248fcbd501526c8a8ce802b6f847eee9a7 /src
parentpica/swrasterizer: implement procedural texture (diff)
downloadyuzu-4d62e75fb2438fea3e9199db1641a7fe2848222a.tar.gz
yuzu-4d62e75fb2438fea3e9199db1641a7fe2848222a.tar.xz
yuzu-4d62e75fb2438fea3e9199db1641a7fe2848222a.zip
gl_rasterizer: implement procedural texture
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp232
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h35
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp271
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h13
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_state.h20
6 files changed, 600 insertions, 7 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 12ac9bbd9..aa9b831dd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -55,6 +55,12 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
55 55
56 uniform_block_data.fog_lut_dirty = true; 56 uniform_block_data.fog_lut_dirty = true;
57 57
58 uniform_block_data.proctex_noise_lut_dirty = true;
59 uniform_block_data.proctex_color_map_dirty = true;
60 uniform_block_data.proctex_alpha_map_dirty = true;
61 uniform_block_data.proctex_lut_dirty = true;
62 uniform_block_data.proctex_diff_lut_dirty = true;
63
58 // Set vertex attributes 64 // Set vertex attributes
59 glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, 65 glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE,
60 sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); 66 sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
@@ -115,6 +121,51 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
115 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 121 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
116 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 122 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
117 123
124 // Setup the noise LUT for proctex
125 proctex_noise_lut.Create();
126 state.proctex_noise_lut.texture_1d = proctex_noise_lut.handle;
127 state.Apply();
128 glActiveTexture(GL_TEXTURE10);
129 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
130 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
131 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
132
133 // Setup the color map for proctex
134 proctex_color_map.Create();
135 state.proctex_color_map.texture_1d = proctex_color_map.handle;
136 state.Apply();
137 glActiveTexture(GL_TEXTURE11);
138 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
139 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
140 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
141
142 // Setup the alpha map for proctex
143 proctex_alpha_map.Create();
144 state.proctex_alpha_map.texture_1d = proctex_alpha_map.handle;
145 state.Apply();
146 glActiveTexture(GL_TEXTURE12);
147 glTexImage1D(GL_TEXTURE_1D, 0, GL_RG32F, 128, 0, GL_RG, GL_FLOAT, nullptr);
148 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
149 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
150
151 // Setup the LUT for proctex
152 proctex_lut.Create();
153 state.proctex_lut.texture_1d = proctex_lut.handle;
154 state.Apply();
155 glActiveTexture(GL_TEXTURE13);
156 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
157 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
158 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
159
160 // Setup the difference LUT for proctex
161 proctex_diff_lut.Create();
162 state.proctex_diff_lut.texture_1d = proctex_diff_lut.handle;
163 state.Apply();
164 glActiveTexture(GL_TEXTURE14);
165 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
166 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
167 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
168
118 // Sync fixed function OpenGL state 169 // Sync fixed function OpenGL state
119 SyncCullMode(); 170 SyncCullMode();
120 SyncBlendEnabled(); 171 SyncBlendEnabled();
@@ -272,6 +323,36 @@ void RasterizerOpenGL::DrawTriangles() {
272 uniform_block_data.fog_lut_dirty = false; 323 uniform_block_data.fog_lut_dirty = false;
273 } 324 }
274 325
326 // Sync the proctex noise lut
327 if (uniform_block_data.proctex_noise_lut_dirty) {
328 SyncProcTexNoiseLUT();
329 uniform_block_data.proctex_noise_lut_dirty = false;
330 }
331
332 // Sync the proctex color map
333 if (uniform_block_data.proctex_color_map_dirty) {
334 SyncProcTexColorMap();
335 uniform_block_data.proctex_color_map_dirty = false;
336 }
337
338 // Sync the proctex alpha map
339 if (uniform_block_data.proctex_alpha_map_dirty) {
340 SyncProcTexAlphaMap();
341 uniform_block_data.proctex_alpha_map_dirty = false;
342 }
343
344 // Sync the proctex lut
345 if (uniform_block_data.proctex_lut_dirty) {
346 SyncProcTexLUT();
347 uniform_block_data.proctex_lut_dirty = false;
348 }
349
350 // Sync the proctex difference lut
351 if (uniform_block_data.proctex_diff_lut_dirty) {
352 SyncProcTexDiffLUT();
353 uniform_block_data.proctex_diff_lut_dirty = false;
354 }
355
275 // Sync the uniform data 356 // Sync the uniform data
276 if (uniform_block_data.dirty) { 357 if (uniform_block_data.dirty) {
277 glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, 358 glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data,
@@ -354,6 +435,47 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
354 uniform_block_data.fog_lut_dirty = true; 435 uniform_block_data.fog_lut_dirty = true;
355 break; 436 break;
356 437
438 // ProcTex state
439 case PICA_REG_INDEX(texturing.proctex):
440 case PICA_REG_INDEX(texturing.proctex_lut):
441 case PICA_REG_INDEX(texturing.proctex_lut_offset):
442 shader_dirty = true;
443 break;
444
445 case PICA_REG_INDEX(texturing.proctex_noise_u):
446 case PICA_REG_INDEX(texturing.proctex_noise_v):
447 case PICA_REG_INDEX(texturing.proctex_noise_frequency):
448 SyncProcTexNoise();
449 break;
450
451 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[0], 0xb0):
452 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[1], 0xb1):
453 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[2], 0xb2):
454 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[3], 0xb3):
455 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[4], 0xb4):
456 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[5], 0xb5):
457 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[6], 0xb6):
458 case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[7], 0xb7):
459 using Pica::TexturingRegs;
460 switch (regs.texturing.proctex_lut_config.ref_table.Value()) {
461 case TexturingRegs::ProcTexLutTable::Noise:
462 uniform_block_data.proctex_noise_lut_dirty = true;
463 break;
464 case TexturingRegs::ProcTexLutTable::ColorMap:
465 uniform_block_data.proctex_color_map_dirty = true;
466 break;
467 case TexturingRegs::ProcTexLutTable::AlphaMap:
468 uniform_block_data.proctex_alpha_map_dirty = true;
469 break;
470 case TexturingRegs::ProcTexLutTable::Color:
471 uniform_block_data.proctex_lut_dirty = true;
472 break;
473 case TexturingRegs::ProcTexLutTable::ColorDiff:
474 uniform_block_data.proctex_diff_lut_dirty = true;
475 break;
476 }
477 break;
478
357 // Alpha test 479 // Alpha test
358 case PICA_REG_INDEX(framebuffer.output_merger.alpha_test): 480 case PICA_REG_INDEX(framebuffer.output_merger.alpha_test):
359 SyncAlphaTest(); 481 SyncAlphaTest();
@@ -1072,6 +1194,35 @@ void RasterizerOpenGL::SetShader() {
1072 glUniform1i(uniform_fog_lut, 9); 1194 glUniform1i(uniform_fog_lut, 9);
1073 } 1195 }
1074 1196
1197 GLuint uniform_proctex_noise_lut =
1198 glGetUniformLocation(shader->shader.handle, "proctex_noise_lut");
1199 if (uniform_proctex_noise_lut != -1) {
1200 glUniform1i(uniform_proctex_noise_lut, 10);
1201 }
1202
1203 GLuint uniform_proctex_color_map =
1204 glGetUniformLocation(shader->shader.handle, "proctex_color_map");
1205 if (uniform_proctex_color_map != -1) {
1206 glUniform1i(uniform_proctex_color_map, 11);
1207 }
1208
1209 GLuint uniform_proctex_alpha_map =
1210 glGetUniformLocation(shader->shader.handle, "proctex_alpha_map");
1211 if (uniform_proctex_alpha_map != -1) {
1212 glUniform1i(uniform_proctex_alpha_map, 12);
1213 }
1214
1215 GLuint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut");
1216 if (uniform_proctex_lut != -1) {
1217 glUniform1i(uniform_proctex_lut, 13);
1218 }
1219
1220 GLuint uniform_proctex_diff_lut =
1221 glGetUniformLocation(shader->shader.handle, "proctex_diff_lut");
1222 if (uniform_proctex_diff_lut != -1) {
1223 glUniform1i(uniform_proctex_diff_lut, 14);
1224 }
1225
1075 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); 1226 current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
1076 1227
1077 GLuint block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); 1228 GLuint block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
@@ -1105,6 +1256,7 @@ void RasterizerOpenGL::SetShader() {
1105 } 1256 }
1106 1257
1107 SyncFogColor(); 1258 SyncFogColor();
1259 SyncProcTexNoise();
1108 } 1260 }
1109 } 1261 }
1110} 1262}
@@ -1204,6 +1356,86 @@ void RasterizerOpenGL::SyncFogLUT() {
1204 } 1356 }
1205} 1357}
1206 1358
1359void RasterizerOpenGL::SyncProcTexNoise() {
1360 const auto& regs = Pica::g_state.regs.texturing;
1361 uniform_block_data.data.proctex_noise_f = {
1362 Pica::float16::FromRaw(regs.proctex_noise_frequency.u).ToFloat32(),
1363 Pica::float16::FromRaw(regs.proctex_noise_frequency.v).ToFloat32(),
1364 };
1365 uniform_block_data.data.proctex_noise_a = {
1366 regs.proctex_noise_u.amplitude / 4095.0f, regs.proctex_noise_v.amplitude / 4095.0f,
1367 };
1368 uniform_block_data.data.proctex_noise_p = {
1369 Pica::float16::FromRaw(regs.proctex_noise_u.phase).ToFloat32(),
1370 Pica::float16::FromRaw(regs.proctex_noise_v.phase).ToFloat32(),
1371 };
1372
1373 uniform_block_data.dirty = true;
1374}
1375
1376// helper function for SyncProcTexNoiseLUT/ColorMap/AlphaMap
1377static void SyncProcTexValueLUT(const std::array<Pica::State::ProcTex::ValueEntry, 128>& lut,
1378 std::array<GLvec2, 128>& lut_data, GLenum texture) {
1379 std::array<GLvec2, 128> new_data;
1380 std::transform(lut.begin(), lut.end(), new_data.begin(), [](const auto& entry) {
1381 return GLvec2{entry.ToFloat(), entry.DiffToFloat()};
1382 });
1383
1384 if (new_data != lut_data) {
1385 lut_data = new_data;
1386 glActiveTexture(texture);
1387 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RG, GL_FLOAT, lut_data.data());
1388 }
1389}
1390
1391void RasterizerOpenGL::SyncProcTexNoiseLUT() {
1392 SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, GL_TEXTURE10);
1393}
1394
1395void RasterizerOpenGL::SyncProcTexColorMap() {
1396 SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data,
1397 GL_TEXTURE11);
1398}
1399
1400void RasterizerOpenGL::SyncProcTexAlphaMap() {
1401 SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data,
1402 GL_TEXTURE12);
1403}
1404
1405void RasterizerOpenGL::SyncProcTexLUT() {
1406 std::array<GLvec4, 256> new_data;
1407
1408 std::transform(Pica::g_state.proctex.color_table.begin(),
1409 Pica::g_state.proctex.color_table.end(), new_data.begin(),
1410 [](const auto& entry) {
1411 auto rgba = entry.ToVector() / 255.0f;
1412 return GLvec4{rgba.r(), rgba.g(), rgba.b(), rgba.a()};
1413 });
1414
1415 if (new_data != proctex_lut_data) {
1416 proctex_lut_data = new_data;
1417 glActiveTexture(GL_TEXTURE13);
1418 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_lut_data.data());
1419 }
1420}
1421
1422void RasterizerOpenGL::SyncProcTexDiffLUT() {
1423 std::array<GLvec4, 256> new_data;
1424
1425 std::transform(Pica::g_state.proctex.color_diff_table.begin(),
1426 Pica::g_state.proctex.color_diff_table.end(), new_data.begin(),
1427 [](const auto& entry) {
1428 auto rgba = entry.ToVector() / 255.0f;
1429 return GLvec4{rgba.r(), rgba.g(), rgba.b(), rgba.a()};
1430 });
1431
1432 if (new_data != proctex_diff_lut_data) {
1433 proctex_diff_lut_data = new_data;
1434 glActiveTexture(GL_TEXTURE14);
1435 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, proctex_diff_lut_data.data());
1436 }
1437}
1438
1207void RasterizerOpenGL::SyncAlphaTest() { 1439void RasterizerOpenGL::SyncAlphaTest() {
1208 const auto& regs = Pica::g_state.regs; 1440 const auto& regs = Pica::g_state.regs;
1209 if (regs.framebuffer.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) { 1441 if (regs.framebuffer.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 3e1770d77..a9ad7d660 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -143,6 +143,9 @@ private:
143 GLint scissor_x2; 143 GLint scissor_x2;
144 GLint scissor_y2; 144 GLint scissor_y2;
145 alignas(16) GLvec3 fog_color; 145 alignas(16) GLvec3 fog_color;
146 alignas(8) GLvec2 proctex_noise_f;
147 alignas(8) GLvec2 proctex_noise_a;
148 alignas(8) GLvec2 proctex_noise_p;
146 alignas(16) GLvec3 lighting_global_ambient; 149 alignas(16) GLvec3 lighting_global_ambient;
147 LightSrc light_src[8]; 150 LightSrc light_src[8];
148 alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages 151 alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages
@@ -150,7 +153,7 @@ private:
150 }; 153 };
151 154
152 static_assert( 155 static_assert(
153 sizeof(UniformData) == 0x3C0, 156 sizeof(UniformData) == 0x3E0,
154 "The size of the UniformData structure has changed, update the structure in the shader"); 157 "The size of the UniformData structure has changed, update the structure in the shader");
155 static_assert(sizeof(UniformData) < 16384, 158 static_assert(sizeof(UniformData) < 16384,
156 "UniformData structure must be less than 16kb as per the OpenGL spec"); 159 "UniformData structure must be less than 16kb as per the OpenGL spec");
@@ -180,6 +183,16 @@ private:
180 void SyncFogColor(); 183 void SyncFogColor();
181 void SyncFogLUT(); 184 void SyncFogLUT();
182 185
186 /// Sync the procedural texture noise configuration to match the PICA register
187 void SyncProcTexNoise();
188
189 /// Sync the procedural texture lookup tables
190 void SyncProcTexNoiseLUT();
191 void SyncProcTexColorMap();
192 void SyncProcTexAlphaMap();
193 void SyncProcTexLUT();
194 void SyncProcTexDiffLUT();
195
183 /// Syncs the alpha test states to match the PICA register 196 /// Syncs the alpha test states to match the PICA register
184 void SyncAlphaTest(); 197 void SyncAlphaTest();
185 198
@@ -248,6 +261,11 @@ private:
248 UniformData data; 261 UniformData data;
249 bool lut_dirty[6]; 262 bool lut_dirty[6];
250 bool fog_lut_dirty; 263 bool fog_lut_dirty;
264 bool proctex_noise_lut_dirty;
265 bool proctex_color_map_dirty;
266 bool proctex_alpha_map_dirty;
267 bool proctex_lut_dirty;
268 bool proctex_diff_lut_dirty;
251 bool dirty; 269 bool dirty;
252 } uniform_block_data = {}; 270 } uniform_block_data = {};
253 271
@@ -262,4 +280,19 @@ private:
262 280
263 OGLTexture fog_lut; 281 OGLTexture fog_lut;
264 std::array<GLuint, 128> fog_lut_data{}; 282 std::array<GLuint, 128> fog_lut_data{};
283
284 OGLTexture proctex_noise_lut;
285 std::array<GLvec2, 128> proctex_noise_lut_data{};
286
287 OGLTexture proctex_color_map;
288 std::array<GLvec2, 128> proctex_color_map_data{};
289
290 OGLTexture proctex_alpha_map;
291 std::array<GLvec2, 128> proctex_alpha_map_data{};
292
293 OGLTexture proctex_lut;
294 std::array<GLvec4, 256> proctex_lut_data{};
295
296 OGLTexture proctex_diff_lut;
297 std::array<GLvec4, 256> proctex_diff_lut_data{};
265}; 298};
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 7b44dade8..600119321 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -114,6 +114,22 @@ PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) {
114 state.lighting.bump_renorm = regs.lighting.config0.disable_bump_renorm == 0; 114 state.lighting.bump_renorm = regs.lighting.config0.disable_bump_renorm == 0;
115 state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0; 115 state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0;
116 116
117 state.proctex.enable = regs.texturing.main_config.texture3_enable;
118 if (state.proctex.enable) {
119 state.proctex.coord = regs.texturing.main_config.texture3_coordinates;
120 state.proctex.u_clamp = regs.texturing.proctex.u_clamp;
121 state.proctex.v_clamp = regs.texturing.proctex.v_clamp;
122 state.proctex.color_combiner = regs.texturing.proctex.color_combiner;
123 state.proctex.alpha_combiner = regs.texturing.proctex.alpha_combiner;
124 state.proctex.separate_alpha = regs.texturing.proctex.separate_alpha;
125 state.proctex.noise_enable = regs.texturing.proctex.noise_enable;
126 state.proctex.u_shift = regs.texturing.proctex.u_shift;
127 state.proctex.v_shift = regs.texturing.proctex.v_shift;
128 state.proctex.lut_width = regs.texturing.proctex_lut.width;
129 state.proctex.lut_offset = regs.texturing.proctex_lut_offset;
130 state.proctex.lut_filter = regs.texturing.proctex_lut.filter;
131 }
132
117 return res; 133 return res;
118} 134}
119 135
@@ -132,8 +148,7 @@ static std::string TexCoord(const PicaShaderConfig& config, int texture_unit) {
132 if (texture_unit == 2 && config.state.texture2_use_coord1) { 148 if (texture_unit == 2 && config.state.texture2_use_coord1) {
133 return "texcoord[1]"; 149 return "texcoord[1]";
134 } 150 }
135 // TODO: if texture unit 3 (procedural texture) implementation also uses this function, 151
136 // config.state.texture3_coordinates should be repected here.
137 return "texcoord[" + std::to_string(texture_unit) + "]"; 152 return "texcoord[" + std::to_string(texture_unit) + "]";
138} 153}
139 154
@@ -175,6 +190,14 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
175 case Source::Texture2: 190 case Source::Texture2:
176 out += "texture(tex[2], " + TexCoord(config, 2) + ")"; 191 out += "texture(tex[2], " + TexCoord(config, 2) + ")";
177 break; 192 break;
193 case Source::Texture3:
194 if (config.state.proctex.enable) {
195 out += "ProcTex()";
196 } else {
197 LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it");
198 out += "vec4(0.0)";
199 }
200 break;
178 case Source::PreviousBuffer: 201 case Source::PreviousBuffer:
179 out += "combiner_buffer"; 202 out += "combiner_buffer";
180 break; 203 break;
@@ -483,9 +506,18 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
483 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { 506 if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
484 // Bump mapping is enabled using a normal map, read perturbation vector from the selected 507 // Bump mapping is enabled using a normal map, read perturbation vector from the selected
485 // texture 508 // texture
486 std::string bump_selector = std::to_string(lighting.bump_selector); 509 if (lighting.bump_selector == 3) {
487 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], " + 510 if (config.state.proctex.enable) {
488 TexCoord(config, lighting.bump_selector) + ").rgb - 1.0;\n"; 511 out += "vec3 surface_normal = 2.0 * ProcTex().rgb - 1.0;\n";
512 } else {
513 LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it");
514 out += "vec3 surface_normal = vec3(-1.0);\n";
515 }
516 } else {
517 std::string bump_selector = std::to_string(lighting.bump_selector);
518 out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], " +
519 TexCoord(config, lighting.bump_selector) + ").rgb - 1.0;\n";
520 }
489 521
490 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher 522 // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher
491 // precision result 523 // precision result
@@ -693,6 +725,221 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
693 out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n"; 725 out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n";
694} 726}
695 727
728using ProcTexClamp = TexturingRegs::ProcTexClamp;
729using ProcTexShift = TexturingRegs::ProcTexShift;
730using ProcTexCombiner = TexturingRegs::ProcTexCombiner;
731using ProcTexFilter = TexturingRegs::ProcTexFilter;
732
733void AppendProcTexShiftOffset(std::string& out, const std::string& v, ProcTexShift mode,
734 ProcTexClamp clamp_mode) {
735 std::string offset = (clamp_mode == ProcTexClamp::MirroredRepeat) ? "1.0" : "0.5";
736 switch (mode) {
737 case ProcTexShift::None:
738 out += "0";
739 break;
740 case ProcTexShift::Odd:
741 out += offset + " * ((int(" + v + ") / 2) % 2)";
742 break;
743 case ProcTexShift::Even:
744 out += offset + " * (((int(" + v + ") + 1) / 2) % 2)";
745 break;
746 default:
747 LOG_CRITICAL(HW_GPU, "Unknown shift mode %u", static_cast<u32>(mode));
748 out += "0";
749 break;
750 }
751}
752
753void AppendProcTexClamp(std::string& out, const std::string& var, ProcTexClamp mode) {
754 switch (mode) {
755 case ProcTexClamp::ToZero:
756 out += var + " = " + var + " > 1.0 ? 0 : " + var + ";\n";
757 break;
758 case ProcTexClamp::ToEdge:
759 out += var + " = " + "min(" + var + ", 1.0);\n";
760 break;
761 case ProcTexClamp::SymmetricalRepeat:
762 out += var + " = " + "fract(" + var + ");\n";
763 break;
764 case ProcTexClamp::MirroredRepeat: {
765 out +=
766 var + " = int(" + var + ") % 2 == 0 ? fract(" + var + ") : 1.0 - fract(" + var + ");\n";
767 break;
768 }
769 case ProcTexClamp::Pulse:
770 out += var + " = " + var + " > 0.5 ? 1.0 : 0.0;\n";
771 break;
772 default:
773 LOG_CRITICAL(HW_GPU, "Unknown clamp mode %u", static_cast<u32>(mode));
774 out += var + " = " + "min(" + var + ", 1.0);\n";
775 break;
776 }
777}
778
779void AppendProcTexCombineAndMap(std::string& out, ProcTexCombiner combiner,
780 const std::string& map_lut) {
781 std::string combined;
782 switch (combiner) {
783 case ProcTexCombiner::U:
784 combined = "u";
785 break;
786 case ProcTexCombiner::U2:
787 combined = "(u * u)";
788 break;
789 case TexturingRegs::ProcTexCombiner::V:
790 combined = "v";
791 break;
792 case TexturingRegs::ProcTexCombiner::V2:
793 combined = "(v * v)";
794 break;
795 case TexturingRegs::ProcTexCombiner::Add:
796 combined = "((u + v) * 0.5)";
797 break;
798 case TexturingRegs::ProcTexCombiner::Add2:
799 combined = "((u * u + v * v) * 0.5)";
800 break;
801 case TexturingRegs::ProcTexCombiner::SqrtAdd2:
802 combined = "min(sqrt(u * u + v * v), 1.0)";
803 break;
804 case TexturingRegs::ProcTexCombiner::Min:
805 combined = "min(u, v)";
806 break;
807 case TexturingRegs::ProcTexCombiner::Max:
808 combined = "max(u, v)";
809 break;
810 case TexturingRegs::ProcTexCombiner::RMax:
811 combined = "min(((u + v) * 0.5 + sqrt(u * u + v * v)) * 0.5, 1.0)";
812 break;
813 default:
814 LOG_CRITICAL(HW_GPU, "Unknown combiner %u", static_cast<u32>(combiner));
815 combined = "0.0";
816 break;
817 }
818 out += "ProcTexLookupLUT(" + map_lut + ", " + combined + ")";
819}
820
821void AppendProcTexSampler(std::string& out, const PicaShaderConfig& config) {
822 // LUT sampling uitlity
823 // For NoiseLUT/ColorMap/AlphaMap, coord=0.0 is lut[0], coord=127.0/128.0 is lut[127] and
824 // coord=1.0 is lut[127]+lut_diff[127]. For other indices, the result is interpolated using
825 // value entries and difference entries.
826 out += R"(
827float ProcTexLookupLUT(sampler1D lut, float coord) {
828 coord *= 128;
829 float index_i = clamp(floor(coord), 0.0, 127.0);
830 float index_f = coord - index_i; // fract() cannot be used here because 128.0 needs to be
831 // extracted as index_i = 127.0 and index_f = 1.0
832 vec2 entry = texelFetch(lut, int(index_i), 0).rg;
833 return clamp(entry.r + entry.g * index_f, 0.0, 1.0);
834}
835 )";
836
837 // Noise utility
838 if (config.state.proctex.noise_enable) {
839 // See swrasterizer/proctex.cpp for more information about these functions
840 out += R"(
841int ProcTexNoiseRand1D(int v) {
842 const int table[] = int[](0,4,10,8,4,9,7,12,5,15,13,14,11,15,2,11);
843 return ((v % 9 + 2) * 3 & 0xF) ^ table[(v / 9) & 0xF];
844}
845
846float ProcTexNoiseRand2D(vec2 point) {
847 const int table[] = int[](10,2,15,8,0,7,4,5,5,13,2,6,13,9,3,14);
848 int u2 = ProcTexNoiseRand1D(int(point.x));
849 int v2 = ProcTexNoiseRand1D(int(point.y));
850 v2 += ((u2 & 3) == 1) ? 4 : 0;
851 v2 ^= (u2 & 1) * 6;
852 v2 += 10 + u2;
853 v2 &= 0xF;
854 v2 ^= table[u2];
855 return -1.0 + float(v2) * 2.0/ 15.0;
856}
857
858float ProcTexNoiseCoef(vec2 x) {
859 vec2 grid = 9.0 * proctex_noise_f * abs(x + proctex_noise_p);
860 vec2 point = floor(grid);
861 vec2 frac = grid - point;
862
863 float g0 = ProcTexNoiseRand2D(point) * (frac.x + frac.y);
864 float g1 = ProcTexNoiseRand2D(point + vec2(1.0, 0.0)) * (frac.x + frac.y - 1.0);
865 float g2 = ProcTexNoiseRand2D(point + vec2(0.0, 1.0)) * (frac.x + frac.y - 1.0);
866 float g3 = ProcTexNoiseRand2D(point + vec2(1.0, 1.0)) * (frac.x + frac.y - 2.0);
867
868 float x_noise = ProcTexLookupLUT(proctex_noise_lut, frac.x);
869 float y_noise = ProcTexLookupLUT(proctex_noise_lut, frac.y);
870 float x0 = mix(g0, g1, x_noise);
871 float x1 = mix(g2, g3, x_noise);
872 return mix(x0, x1, y_noise);
873}
874 )";
875 }
876
877 out += "vec4 ProcTex() {\n";
878 out += "vec2 uv = abs(texcoord[" + std::to_string(config.state.proctex.coord) + "]);\n";
879
880 // Get shift offset before noise generation
881 out += "float u_shift = ";
882 AppendProcTexShiftOffset(out, "uv.y", config.state.proctex.u_shift,
883 config.state.proctex.u_clamp);
884 out += ";\n";
885 out += "float v_shift = ";
886 AppendProcTexShiftOffset(out, "uv.x", config.state.proctex.v_shift,
887 config.state.proctex.v_clamp);
888 out += ";\n";
889
890 // Generate noise
891 if (config.state.proctex.noise_enable) {
892 out += "uv += proctex_noise_a * ProcTexNoiseCoef(uv);\n";
893 out += "uv = abs(uv);\n";
894 }
895
896 // Shift
897 out += "float u = uv.x + u_shift;\n";
898 out += "float v = uv.y + v_shift;\n";
899
900 // Clamp
901 AppendProcTexClamp(out, "u", config.state.proctex.u_clamp);
902 AppendProcTexClamp(out, "v", config.state.proctex.v_clamp);
903
904 // Combine and map
905 out += "float lut_coord = ";
906 AppendProcTexCombineAndMap(out, config.state.proctex.color_combiner, "proctex_color_map");
907 out += ";\n";
908
909 // Look up color
910 // For the color lut, coord=0.0 is lut[offset] and coord=1.0 is lut[offset+width-1]
911 out += "lut_coord *= " + std::to_string(config.state.proctex.lut_width - 1) + ";\n";
912 // TODO(wwylele): implement mipmap
913 switch (config.state.proctex.lut_filter) {
914 case ProcTexFilter::Linear:
915 case ProcTexFilter::LinearMipmapLinear:
916 case ProcTexFilter::LinearMipmapNearest:
917 out += "int lut_index_i = int(lut_coord) + " +
918 std::to_string(config.state.proctex.lut_offset) + ";\n";
919 out += "float lut_index_f = fract(lut_coord);\n";
920 out += "vec4 final_color = texelFetch(proctex_lut, lut_index_i, 0) + lut_index_f * "
921 "texelFetch(proctex_diff_lut, lut_index_i, 0);\n";
922 break;
923 case ProcTexFilter::Nearest:
924 case ProcTexFilter::NearestMipmapLinear:
925 case ProcTexFilter::NearestMipmapNearest:
926 out += "lut_coord += " + std::to_string(config.state.proctex.lut_offset) + ";\n";
927 out += "vec4 final_color = texelFetch(proctex_lut, int(round(lut_coord)), 0);\n";
928 break;
929 }
930
931 if (config.state.proctex.separate_alpha) {
932 // Note: in separate alpha mode, the alpha channel skips the color LUT look up stage. It
933 // uses the output of CombineAndMap directly instead.
934 out += "float final_alpha = ";
935 AppendProcTexCombineAndMap(out, config.state.proctex.alpha_combiner, "proctex_alpha_map");
936 out += ";\n";
937 out += "return vec4(final_color.xyz, final_alpha);\n}\n";
938 } else {
939 out += "return final_color;\n}\n";
940 }
941}
942
696std::string GenerateFragmentShader(const PicaShaderConfig& config) { 943std::string GenerateFragmentShader(const PicaShaderConfig& config) {
697 const auto& state = config.state; 944 const auto& state = config.state;
698 945
@@ -735,6 +982,9 @@ layout (std140) uniform shader_data {
735 int scissor_x2; 982 int scissor_x2;
736 int scissor_y2; 983 int scissor_y2;
737 vec3 fog_color; 984 vec3 fog_color;
985 vec2 proctex_noise_f;
986 vec2 proctex_noise_a;
987 vec2 proctex_noise_p;
738 vec3 lighting_global_ambient; 988 vec3 lighting_global_ambient;
739 LightSrc light_src[NUM_LIGHTS]; 989 LightSrc light_src[NUM_LIGHTS];
740 vec4 const_color[NUM_TEV_STAGES]; 990 vec4 const_color[NUM_TEV_STAGES];
@@ -744,12 +994,21 @@ layout (std140) uniform shader_data {
744uniform sampler2D tex[3]; 994uniform sampler2D tex[3];
745uniform sampler1D lut[6]; 995uniform sampler1D lut[6];
746uniform usampler1D fog_lut; 996uniform usampler1D fog_lut;
997uniform sampler1D proctex_noise_lut;
998uniform sampler1D proctex_color_map;
999uniform sampler1D proctex_alpha_map;
1000uniform sampler1D proctex_lut;
1001uniform sampler1D proctex_diff_lut;
747 1002
748// Rotate the vector v by the quaternion q 1003// Rotate the vector v by the quaternion q
749vec3 quaternion_rotate(vec4 q, vec3 v) { 1004vec3 quaternion_rotate(vec4 q, vec3 v) {
750 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); 1005 return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
751} 1006})";
752 1007
1008 if (config.state.proctex.enable)
1009 AppendProcTexSampler(out, config);
1010
1011 out += R"(
753void main() { 1012void main() {
754vec4 primary_fragment_color = vec4(0.0); 1013vec4 primary_fragment_color = vec4(0.0);
755vec4 secondary_fragment_color = vec4(0.0); 1014vec4 secondary_fragment_color = vec4(0.0);
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
index 3fb046b76..ea6d216d1 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.h
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -113,6 +113,19 @@ union PicaShaderConfig {
113 } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb; 113 } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
114 } lighting; 114 } lighting;
115 115
116 struct {
117 bool enable;
118 u32 coord;
119 Pica::TexturingRegs::ProcTexClamp u_clamp, v_clamp;
120 Pica::TexturingRegs::ProcTexCombiner color_combiner, alpha_combiner;
121 bool separate_alpha;
122 bool noise_enable;
123 Pica::TexturingRegs::ProcTexShift u_shift, v_shift;
124 u32 lut_width;
125 u32 lut_offset;
126 Pica::TexturingRegs::ProcTexFilter lut_filter;
127 } proctex;
128
116 } state; 129 } state;
117}; 130};
118#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) 131#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 3c03b424a..bf837a7fb 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -58,6 +58,12 @@ OpenGLState::OpenGLState() {
58 58
59 fog_lut.texture_1d = 0; 59 fog_lut.texture_1d = 0;
60 60
61 proctex_lut.texture_1d = 0;
62 proctex_diff_lut.texture_1d = 0;
63 proctex_color_map.texture_1d = 0;
64 proctex_alpha_map.texture_1d = 0;
65 proctex_noise_lut.texture_1d = 0;
66
61 draw.read_framebuffer = 0; 67 draw.read_framebuffer = 0;
62 draw.draw_framebuffer = 0; 68 draw.draw_framebuffer = 0;
63 draw.vertex_array = 0; 69 draw.vertex_array = 0;
@@ -201,6 +207,36 @@ void OpenGLState::Apply() const {
201 glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d); 207 glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d);
202 } 208 }
203 209
210 // ProcTex Noise LUT
211 if (proctex_noise_lut.texture_1d != cur_state.proctex_noise_lut.texture_1d) {
212 glActiveTexture(GL_TEXTURE10);
213 glBindTexture(GL_TEXTURE_1D, proctex_noise_lut.texture_1d);
214 }
215
216 // ProcTex Color Map
217 if (proctex_color_map.texture_1d != cur_state.proctex_color_map.texture_1d) {
218 glActiveTexture(GL_TEXTURE11);
219 glBindTexture(GL_TEXTURE_1D, proctex_color_map.texture_1d);
220 }
221
222 // ProcTex Alpha Map
223 if (proctex_alpha_map.texture_1d != cur_state.proctex_alpha_map.texture_1d) {
224 glActiveTexture(GL_TEXTURE12);
225 glBindTexture(GL_TEXTURE_1D, proctex_alpha_map.texture_1d);
226 }
227
228 // ProcTex LUT
229 if (proctex_lut.texture_1d != cur_state.proctex_lut.texture_1d) {
230 glActiveTexture(GL_TEXTURE13);
231 glBindTexture(GL_TEXTURE_1D, proctex_lut.texture_1d);
232 }
233
234 // ProcTex Diff LUT
235 if (proctex_diff_lut.texture_1d != cur_state.proctex_diff_lut.texture_1d) {
236 glActiveTexture(GL_TEXTURE14);
237 glBindTexture(GL_TEXTURE_1D, proctex_diff_lut.texture_1d);
238 }
239
204 // Framebuffer 240 // Framebuffer
205 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { 241 if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
206 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); 242 glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index aee3c2946..7dcc03bd5 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -72,6 +72,26 @@ public:
72 } fog_lut; 72 } fog_lut;
73 73
74 struct { 74 struct {
75 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
76 } proctex_noise_lut;
77
78 struct {
79 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
80 } proctex_color_map;
81
82 struct {
83 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
84 } proctex_alpha_map;
85
86 struct {
87 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
88 } proctex_lut;
89
90 struct {
91 GLuint texture_1d; // GL_TEXTURE_BINDING_1D
92 } proctex_diff_lut;
93
94 struct {
75 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING 95 GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
76 GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING 96 GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
77 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING 97 GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING