diff options
| author | 2016-06-15 17:55:36 -0400 | |
|---|---|---|
| committer | 2016-06-15 17:55:36 -0400 | |
| commit | 7d2d6e5f7864973c9d945d76887ff7e1209be205 (patch) | |
| tree | ab6addc2f62eadc637c6aad42625d99d925bca69 /src | |
| parent | Merge pull request #1842 from Subv/ports (diff) | |
| parent | OpenGL: Implement fog (diff) | |
| download | yuzu-7d2d6e5f7864973c9d945d76887ff7e1209be205.tar.gz yuzu-7d2d6e5f7864973c9d945d76887ff7e1209be205.tar.xz yuzu-7d2d6e5f7864973c9d945d76887ff7e1209be205.zip | |
Merge pull request #1875 from JayFoxRox/fog
Implement standard fog (fog mode 5)
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/command_processor.cpp | 14 | ||||
| -rw-r--r-- | src/video_core/pica.h | 53 | ||||
| -rw-r--r-- | src/video_core/pica_state.h | 16 | ||||
| -rw-r--r-- | src/video_core/rasterizer.cpp | 73 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 68 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 27 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 38 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 8 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 4 |
9 files changed, 253 insertions, 48 deletions
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 19e03adf4..689859049 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -423,6 +423,20 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { | |||
| 423 | break; | 423 | break; |
| 424 | } | 424 | } |
| 425 | 425 | ||
| 426 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8): | ||
| 427 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9): | ||
| 428 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea): | ||
| 429 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb): | ||
| 430 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec): | ||
| 431 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed): | ||
| 432 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee): | ||
| 433 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef): | ||
| 434 | { | ||
| 435 | g_state.fog.lut[regs.fog_lut_offset % 128].raw = value; | ||
| 436 | regs.fog_lut_offset.Assign(regs.fog_lut_offset + 1); | ||
| 437 | break; | ||
| 438 | } | ||
| 439 | |||
| 426 | default: | 440 | default: |
| 427 | break; | 441 | break; |
| 428 | } | 442 | } |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index 544ea037f..09702d46a 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -401,22 +401,47 @@ struct Regs { | |||
| 401 | TevStageConfig tev_stage3; | 401 | TevStageConfig tev_stage3; |
| 402 | INSERT_PADDING_WORDS(0x3); | 402 | INSERT_PADDING_WORDS(0x3); |
| 403 | 403 | ||
| 404 | enum class FogMode : u32 { | ||
| 405 | None = 0, | ||
| 406 | Fog = 5, | ||
| 407 | Gas = 7, | ||
| 408 | }; | ||
| 409 | |||
| 404 | union { | 410 | union { |
| 405 | // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in | 411 | BitField<0, 3, FogMode> fog_mode; |
| 406 | // these masks are set | 412 | BitField<16, 1, u32> fog_flip; |
| 407 | BitField< 8, 4, u32> update_mask_rgb; | ||
| 408 | BitField<12, 4, u32> update_mask_a; | ||
| 409 | 413 | ||
| 410 | bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { | 414 | union { |
| 411 | return (stage_index < 4) && (update_mask_rgb & (1 << stage_index)); | 415 | // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in |
| 412 | } | 416 | // these masks are set |
| 417 | BitField< 8, 4, u32> update_mask_rgb; | ||
| 418 | BitField<12, 4, u32> update_mask_a; | ||
| 413 | 419 | ||
| 414 | bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { | 420 | bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { |
| 415 | return (stage_index < 4) && (update_mask_a & (1 << stage_index)); | 421 | return (stage_index < 4) && (update_mask_rgb & (1 << stage_index)); |
| 416 | } | 422 | } |
| 417 | } tev_combiner_buffer_input; | 423 | |
| 424 | bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { | ||
| 425 | return (stage_index < 4) && (update_mask_a & (1 << stage_index)); | ||
| 426 | } | ||
| 427 | } tev_combiner_buffer_input; | ||
| 428 | }; | ||
| 429 | |||
| 430 | union { | ||
| 431 | u32 raw; | ||
| 432 | BitField< 0, 8, u32> r; | ||
| 433 | BitField< 8, 8, u32> g; | ||
| 434 | BitField<16, 8, u32> b; | ||
| 435 | } fog_color; | ||
| 436 | |||
| 437 | INSERT_PADDING_WORDS(0x4); | ||
| 438 | |||
| 439 | BitField<0, 16, u32> fog_lut_offset; | ||
| 440 | |||
| 441 | INSERT_PADDING_WORDS(0x1); | ||
| 442 | |||
| 443 | u32 fog_lut_data[8]; | ||
| 418 | 444 | ||
| 419 | INSERT_PADDING_WORDS(0xf); | ||
| 420 | TevStageConfig tev_stage4; | 445 | TevStageConfig tev_stage4; |
| 421 | INSERT_PADDING_WORDS(0x3); | 446 | INSERT_PADDING_WORDS(0x3); |
| 422 | TevStageConfig tev_stage5; | 447 | TevStageConfig tev_stage5; |
| @@ -1318,6 +1343,10 @@ ASSERT_REG_POSITION(tev_stage1, 0xc8); | |||
| 1318 | ASSERT_REG_POSITION(tev_stage2, 0xd0); | 1343 | ASSERT_REG_POSITION(tev_stage2, 0xd0); |
| 1319 | ASSERT_REG_POSITION(tev_stage3, 0xd8); | 1344 | ASSERT_REG_POSITION(tev_stage3, 0xd8); |
| 1320 | ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0); | 1345 | ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0); |
| 1346 | ASSERT_REG_POSITION(fog_mode, 0xe0); | ||
| 1347 | ASSERT_REG_POSITION(fog_color, 0xe1); | ||
| 1348 | ASSERT_REG_POSITION(fog_lut_offset, 0xe6); | ||
| 1349 | ASSERT_REG_POSITION(fog_lut_data, 0xe8); | ||
| 1321 | ASSERT_REG_POSITION(tev_stage4, 0xf0); | 1350 | ASSERT_REG_POSITION(tev_stage4, 0xf0); |
| 1322 | ASSERT_REG_POSITION(tev_stage5, 0xf8); | 1351 | ASSERT_REG_POSITION(tev_stage5, 0xf8); |
| 1323 | ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd); | 1352 | ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd); |
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h index 495174c25..01f4285a8 100644 --- a/src/video_core/pica_state.h +++ b/src/video_core/pica_state.h | |||
| @@ -33,10 +33,10 @@ struct State { | |||
| 33 | u32 raw; | 33 | u32 raw; |
| 34 | 34 | ||
| 35 | // LUT value, encoded as 12-bit fixed point, with 12 fraction bits | 35 | // LUT value, encoded as 12-bit fixed point, with 12 fraction bits |
| 36 | BitField< 0, 12, u32> value; | 36 | BitField< 0, 12, u32> value; // 0.0.12 fixed point |
| 37 | 37 | ||
| 38 | // Used by HW for efficient interpolation, Citra does not use these | 38 | // Used by HW for efficient interpolation, Citra does not use these |
| 39 | BitField<12, 12, u32> difference; | 39 | BitField<12, 12, s32> difference; // 1.0.11 fixed point |
| 40 | 40 | ||
| 41 | float ToFloat() { | 41 | float ToFloat() { |
| 42 | return static_cast<float>(value) / 4095.f; | 42 | return static_cast<float>(value) / 4095.f; |
| @@ -46,6 +46,18 @@ struct State { | |||
| 46 | std::array<std::array<LutEntry, 256>, 24> luts; | 46 | std::array<std::array<LutEntry, 256>, 24> luts; |
| 47 | } lighting; | 47 | } lighting; |
| 48 | 48 | ||
| 49 | struct { | ||
| 50 | union LutEntry { | ||
| 51 | // Used for raw access | ||
| 52 | u32 raw; | ||
| 53 | |||
| 54 | BitField< 0, 13, s32> difference; // 1.1.11 fixed point | ||
| 55 | BitField<13, 11, u32> value; // 0.0.11 fixed point | ||
| 56 | }; | ||
| 57 | |||
| 58 | std::array<LutEntry, 128> lut; | ||
| 59 | } fog; | ||
| 60 | |||
| 49 | /// Current Pica command list | 61 | /// Current Pica command list |
| 50 | struct { | 62 | struct { |
| 51 | const u32* head_ptr; | 63 | const u32* head_ptr; |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 65168f05a..a84170094 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -398,6 +398,26 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 398 | float24::FromFloat32(static_cast<float>(w2))); | 398 | float24::FromFloat32(static_cast<float>(w2))); |
| 399 | float24 interpolated_w_inverse = float24::FromFloat32(1.0f) / Math::Dot(w_inverse, baricentric_coordinates); | 399 | float24 interpolated_w_inverse = float24::FromFloat32(1.0f) / Math::Dot(w_inverse, baricentric_coordinates); |
| 400 | 400 | ||
| 401 | // interpolated_z = z / w | ||
| 402 | float interpolated_z_over_w = (v0.screenpos[2].ToFloat32() * w0 + | ||
| 403 | v1.screenpos[2].ToFloat32() * w1 + | ||
| 404 | v2.screenpos[2].ToFloat32() * w2) / wsum; | ||
| 405 | |||
| 406 | // Not fully accurate. About 3 bits in precision are missing. | ||
| 407 | // Z-Buffer (z / w * scale + offset) | ||
| 408 | float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32(); | ||
| 409 | float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32(); | ||
| 410 | float depth = interpolated_z_over_w * depth_scale + depth_offset; | ||
| 411 | |||
| 412 | // Potentially switch to W-Buffer | ||
| 413 | if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { | ||
| 414 | // W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w) | ||
| 415 | depth *= interpolated_w_inverse.ToFloat32() * wsum; | ||
| 416 | } | ||
| 417 | |||
| 418 | // Clamp the result | ||
| 419 | depth = MathUtil::Clamp(depth, 0.0f, 1.0f); | ||
| 420 | |||
| 401 | // Perspective correct attribute interpolation: | 421 | // Perspective correct attribute interpolation: |
| 402 | // Attribute values cannot be calculated by simple linear interpolation since | 422 | // Attribute values cannot be calculated by simple linear interpolation since |
| 403 | // they are not linear in screen space. For example, when interpolating a | 423 | // they are not linear in screen space. For example, when interpolating a |
| @@ -833,6 +853,38 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 833 | continue; | 853 | continue; |
| 834 | } | 854 | } |
| 835 | 855 | ||
| 856 | // Apply fog combiner | ||
| 857 | // Not fully accurate. We'd have to know what data type is used to | ||
| 858 | // store the depth etc. Using float for now until we know more | ||
| 859 | // about Pica datatypes | ||
| 860 | if (regs.fog_mode == Regs::FogMode::Fog) { | ||
| 861 | const Math::Vec3<u8> fog_color = { | ||
| 862 | static_cast<u8>(regs.fog_color.r.Value()), | ||
| 863 | static_cast<u8>(regs.fog_color.g.Value()), | ||
| 864 | static_cast<u8>(regs.fog_color.b.Value()), | ||
| 865 | }; | ||
| 866 | |||
| 867 | // Get index into fog LUT | ||
| 868 | float fog_index; | ||
| 869 | if (g_state.regs.fog_flip) { | ||
| 870 | fog_index = (1.0f - depth) * 128.0f; | ||
| 871 | } else { | ||
| 872 | fog_index = depth * 128.0f; | ||
| 873 | } | ||
| 874 | |||
| 875 | // Generate clamped fog factor from LUT for given fog index | ||
| 876 | float fog_i = MathUtil::Clamp(floorf(fog_index), 0.0f, 127.0f); | ||
| 877 | float fog_f = fog_index - fog_i; | ||
| 878 | const auto& fog_lut_entry = g_state.fog.lut[static_cast<unsigned int>(fog_i)]; | ||
| 879 | float fog_factor = (fog_lut_entry.value + fog_lut_entry.difference * fog_f) / 2047.0f; // This is signed fixed point 1.11 | ||
| 880 | fog_factor = MathUtil::Clamp(fog_factor, 0.0f, 1.0f); | ||
| 881 | |||
| 882 | // Blend the fog | ||
| 883 | for (unsigned i = 0; i < 3; i++) { | ||
| 884 | combiner_output[i] = fog_factor * combiner_output[i] + (1.0f - fog_factor) * fog_color[i]; | ||
| 885 | } | ||
| 886 | } | ||
| 887 | |||
| 836 | u8 old_stencil = 0; | 888 | u8 old_stencil = 0; |
| 837 | 889 | ||
| 838 | auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { | 890 | auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) { |
| @@ -887,27 +939,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, | |||
| 887 | } | 939 | } |
| 888 | } | 940 | } |
| 889 | 941 | ||
| 890 | // interpolated_z = z / w | ||
| 891 | float interpolated_z_over_w = (v0.screenpos[2].ToFloat32() * w0 + | ||
| 892 | v1.screenpos[2].ToFloat32() * w1 + | ||
| 893 | v2.screenpos[2].ToFloat32() * w2) / wsum; | ||
| 894 | |||
| 895 | // Not fully accurate. About 3 bits in precision are missing. | ||
| 896 | // Z-Buffer (z / w * scale + offset) | ||
| 897 | float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32(); | ||
| 898 | float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32(); | ||
| 899 | float depth = interpolated_z_over_w * depth_scale + depth_offset; | ||
| 900 | |||
| 901 | // Potentially switch to W-Buffer | ||
| 902 | if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { | ||
| 903 | |||
| 904 | // W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w) | ||
| 905 | depth *= interpolated_w_inverse.ToFloat32() * wsum; | ||
| 906 | } | ||
| 907 | |||
| 908 | // Clamp the result | ||
| 909 | depth = MathUtil::Clamp(depth, 0.0f, 1.0f); | ||
| 910 | |||
| 911 | // Convert float to integer | 942 | // Convert float to integer |
| 912 | unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); | 943 | unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format); |
| 913 | u32 z = (u32)(depth * ((1 << num_bits) - 1)); | 944 | u32 z = (u32)(depth * ((1 << num_bits) - 1)); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 931c34a37..328a4f66b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -62,6 +62,8 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 62 | uniform_block_data.lut_dirty[index] = true; | 62 | uniform_block_data.lut_dirty[index] = true; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | uniform_block_data.fog_lut_dirty = true; | ||
| 66 | |||
| 65 | // Set vertex attributes | 67 | // Set vertex attributes |
| 66 | glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); | 68 | glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); |
| 67 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); | 69 | glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); |
| @@ -102,6 +104,18 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { | |||
| 102 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 104 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 107 | // Setup the LUT for the fog | ||
| 108 | { | ||
| 109 | fog_lut.Create(); | ||
| 110 | state.fog_lut.texture_1d = fog_lut.handle; | ||
| 111 | } | ||
| 112 | state.Apply(); | ||
| 113 | |||
| 114 | glActiveTexture(GL_TEXTURE9); | ||
| 115 | glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr); | ||
| 116 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
| 117 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
| 118 | |||
| 105 | // Sync fixed function OpenGL state | 119 | // Sync fixed function OpenGL state |
| 106 | SyncCullMode(); | 120 | SyncCullMode(); |
| 107 | SyncBlendEnabled(); | 121 | SyncBlendEnabled(); |
| @@ -215,6 +229,12 @@ void RasterizerOpenGL::DrawTriangles() { | |||
| 215 | } | 229 | } |
| 216 | } | 230 | } |
| 217 | 231 | ||
| 232 | // Sync the fog lut | ||
| 233 | if (uniform_block_data.fog_lut_dirty) { | ||
| 234 | SyncFogLUT(); | ||
| 235 | uniform_block_data.fog_lut_dirty = false; | ||
| 236 | } | ||
| 237 | |||
| 218 | // Sync the uniform data | 238 | // Sync the uniform data |
| 219 | if (uniform_block_data.dirty) { | 239 | if (uniform_block_data.dirty) { |
| 220 | glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); | 240 | glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW); |
| @@ -280,6 +300,21 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 280 | SyncBlendColor(); | 300 | SyncBlendColor(); |
| 281 | break; | 301 | break; |
| 282 | 302 | ||
| 303 | // Fog state | ||
| 304 | case PICA_REG_INDEX(fog_color): | ||
| 305 | SyncFogColor(); | ||
| 306 | break; | ||
| 307 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8): | ||
| 308 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9): | ||
| 309 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea): | ||
| 310 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb): | ||
| 311 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec): | ||
| 312 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed): | ||
| 313 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee): | ||
| 314 | case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef): | ||
| 315 | uniform_block_data.fog_lut_dirty = true; | ||
| 316 | break; | ||
| 317 | |||
| 283 | // Alpha test | 318 | // Alpha test |
| 284 | case PICA_REG_INDEX(output_merger.alpha_test): | 319 | case PICA_REG_INDEX(output_merger.alpha_test): |
| 285 | SyncAlphaTest(); | 320 | SyncAlphaTest(); |
| @@ -329,6 +364,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { | |||
| 329 | break; | 364 | break; |
| 330 | 365 | ||
| 331 | // TEV stages | 366 | // TEV stages |
| 367 | // (This also syncs fog_mode and fog_flip which are part of tev_combiner_buffer_input) | ||
| 332 | case PICA_REG_INDEX(tev_stage0.color_source1): | 368 | case PICA_REG_INDEX(tev_stage0.color_source1): |
| 333 | case PICA_REG_INDEX(tev_stage0.color_modifier1): | 369 | case PICA_REG_INDEX(tev_stage0.color_modifier1): |
| 334 | case PICA_REG_INDEX(tev_stage0.color_op): | 370 | case PICA_REG_INDEX(tev_stage0.color_op): |
| @@ -950,9 +986,15 @@ void RasterizerOpenGL::SetShader() { | |||
| 950 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); | 986 | uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]"); |
| 951 | if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); } | 987 | if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); } |
| 952 | 988 | ||
| 989 | GLuint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); | ||
| 990 | if (uniform_fog_lut != -1) { glUniform1i(uniform_fog_lut, 9); } | ||
| 991 | |||
| 953 | current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); | 992 | current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); |
| 954 | 993 | ||
| 955 | unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); | 994 | unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data"); |
| 995 | GLint block_size; | ||
| 996 | glGetActiveUniformBlockiv(current_shader->shader.handle, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &block_size); | ||
| 997 | ASSERT_MSG(block_size == sizeof(UniformData), "Uniform block size did not match!"); | ||
| 956 | glUniformBlockBinding(current_shader->shader.handle, block_index, 0); | 998 | glUniformBlockBinding(current_shader->shader.handle, block_index, 0); |
| 957 | 999 | ||
| 958 | // Update uniforms | 1000 | // Update uniforms |
| @@ -974,6 +1016,8 @@ void RasterizerOpenGL::SetShader() { | |||
| 974 | SyncLightDistanceAttenuationBias(light_index); | 1016 | SyncLightDistanceAttenuationBias(light_index); |
| 975 | SyncLightDistanceAttenuationScale(light_index); | 1017 | SyncLightDistanceAttenuationScale(light_index); |
| 976 | } | 1018 | } |
| 1019 | |||
| 1020 | SyncFogColor(); | ||
| 977 | } | 1021 | } |
| 978 | } | 1022 | } |
| 979 | 1023 | ||
| @@ -1040,6 +1084,30 @@ void RasterizerOpenGL::SyncBlendColor() { | |||
| 1040 | state.blend.color.alpha = blend_color[3]; | 1084 | state.blend.color.alpha = blend_color[3]; |
| 1041 | } | 1085 | } |
| 1042 | 1086 | ||
| 1087 | void RasterizerOpenGL::SyncFogColor() { | ||
| 1088 | const auto& regs = Pica::g_state.regs; | ||
| 1089 | uniform_block_data.data.fog_color = { | ||
| 1090 | regs.fog_color.r.Value() / 255.0f, | ||
| 1091 | regs.fog_color.g.Value() / 255.0f, | ||
| 1092 | regs.fog_color.b.Value() / 255.0f | ||
| 1093 | }; | ||
| 1094 | uniform_block_data.dirty = true; | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | void RasterizerOpenGL::SyncFogLUT() { | ||
| 1098 | std::array<GLuint, 128> new_data; | ||
| 1099 | |||
| 1100 | std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), [](const auto& entry) { | ||
| 1101 | return entry.raw; | ||
| 1102 | }); | ||
| 1103 | |||
| 1104 | if (new_data != fog_lut_data) { | ||
| 1105 | fog_lut_data = new_data; | ||
| 1106 | glActiveTexture(GL_TEXTURE9); | ||
| 1107 | glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 128, GL_RED_INTEGER, GL_UNSIGNED_INT, fog_lut_data.data()); | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | |||
| 1043 | void RasterizerOpenGL::SyncAlphaTest() { | 1111 | void RasterizerOpenGL::SyncAlphaTest() { |
| 1044 | const auto& regs = Pica::g_state.regs; | 1112 | const auto& regs = Pica::g_state.regs; |
| 1045 | if (regs.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) { | 1113 | if (regs.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 bb7f20161..42482df4b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -76,6 +76,9 @@ union PicaShaderConfig { | |||
| 76 | state.tev_stages[i].scales_raw = tev_stage.scales_raw; | 76 | state.tev_stages[i].scales_raw = tev_stage.scales_raw; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | state.fog_mode = regs.fog_mode; | ||
| 80 | state.fog_flip = regs.fog_flip; | ||
| 81 | |||
| 79 | state.combiner_buffer_input = | 82 | state.combiner_buffer_input = |
| 80 | regs.tev_combiner_buffer_input.update_mask_rgb.Value() | | 83 | regs.tev_combiner_buffer_input.update_mask_rgb.Value() | |
| 81 | regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; | 84 | regs.tev_combiner_buffer_input.update_mask_a.Value() << 4; |
| @@ -168,13 +171,14 @@ union PicaShaderConfig { | |||
| 168 | }; | 171 | }; |
| 169 | 172 | ||
| 170 | struct State { | 173 | struct State { |
| 171 | |||
| 172 | Pica::Regs::CompareFunc alpha_test_func; | 174 | Pica::Regs::CompareFunc alpha_test_func; |
| 173 | Pica::Regs::TextureConfig::TextureType texture0_type; | 175 | Pica::Regs::TextureConfig::TextureType texture0_type; |
| 174 | std::array<TevStageConfigRaw, 6> tev_stages; | 176 | std::array<TevStageConfigRaw, 6> tev_stages; |
| 175 | u8 combiner_buffer_input; | 177 | u8 combiner_buffer_input; |
| 176 | 178 | ||
| 177 | Pica::Regs::DepthBuffering depthmap_enable; | 179 | Pica::Regs::DepthBuffering depthmap_enable; |
| 180 | Pica::Regs::FogMode fog_mode; | ||
| 181 | bool fog_flip; | ||
| 178 | 182 | ||
| 179 | struct { | 183 | struct { |
| 180 | struct { | 184 | struct { |
| @@ -316,19 +320,22 @@ private: | |||
| 316 | GLfloat dist_atten_scale; | 320 | GLfloat dist_atten_scale; |
| 317 | }; | 321 | }; |
| 318 | 322 | ||
| 319 | /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned | 323 | /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned |
| 324 | // NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at | ||
| 325 | // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. | ||
| 326 | // Not following that rule will cause problems on some AMD drivers. | ||
| 320 | struct UniformData { | 327 | struct UniformData { |
| 321 | // A vec4 color for each of the six tev stages | ||
| 322 | GLvec4 const_color[6]; | ||
| 323 | GLvec4 tev_combiner_buffer_color; | ||
| 324 | GLint alphatest_ref; | 328 | GLint alphatest_ref; |
| 325 | GLfloat depth_scale; | 329 | GLfloat depth_scale; |
| 326 | GLfloat depth_offset; | 330 | GLfloat depth_offset; |
| 331 | alignas(16) GLvec3 fog_color; | ||
| 327 | alignas(16) GLvec3 lighting_global_ambient; | 332 | alignas(16) GLvec3 lighting_global_ambient; |
| 328 | LightSrc light_src[8]; | 333 | LightSrc light_src[8]; |
| 334 | alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages | ||
| 335 | alignas(16) GLvec4 tev_combiner_buffer_color; | ||
| 329 | }; | 336 | }; |
| 330 | 337 | ||
| 331 | static_assert(sizeof(UniformData) == 0x390, "The size of the UniformData structure has changed, update the structure in the shader"); | 338 | static_assert(sizeof(UniformData) == 0x3A0, "The size of the UniformData structure has changed, update the structure in the shader"); |
| 332 | static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); | 339 | static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec"); |
| 333 | 340 | ||
| 334 | /// Sets the OpenGL shader in accordance with the current PICA register state | 341 | /// Sets the OpenGL shader in accordance with the current PICA register state |
| @@ -352,6 +359,10 @@ private: | |||
| 352 | /// Syncs the blend color to match the PICA register | 359 | /// Syncs the blend color to match the PICA register |
| 353 | void SyncBlendColor(); | 360 | void SyncBlendColor(); |
| 354 | 361 | ||
| 362 | /// Syncs the fog states to match the PICA register | ||
| 363 | void SyncFogColor(); | ||
| 364 | void SyncFogLUT(); | ||
| 365 | |||
| 355 | /// Syncs the alpha test states to match the PICA register | 366 | /// Syncs the alpha test states to match the PICA register |
| 356 | void SyncAlphaTest(); | 367 | void SyncAlphaTest(); |
| 357 | 368 | ||
| @@ -419,6 +430,7 @@ private: | |||
| 419 | struct { | 430 | struct { |
| 420 | UniformData data; | 431 | UniformData data; |
| 421 | bool lut_dirty[6]; | 432 | bool lut_dirty[6]; |
| 433 | bool fog_lut_dirty; | ||
| 422 | bool dirty; | 434 | bool dirty; |
| 423 | } uniform_block_data = {}; | 435 | } uniform_block_data = {}; |
| 424 | 436 | ||
| @@ -430,4 +442,7 @@ private: | |||
| 430 | 442 | ||
| 431 | std::array<OGLTexture, 6> lighting_luts; | 443 | std::array<OGLTexture, 6> lighting_luts; |
| 432 | std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; | 444 | std::array<std::array<GLvec4, 256>, 6> lighting_lut_data{}; |
| 445 | |||
| 446 | OGLTexture fog_lut; | ||
| 447 | std::array<GLuint, 128> fog_lut_data{}; | ||
| 433 | }; | 448 | }; |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8332e722d..3bace7f01 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -552,17 +552,19 @@ struct LightSrc { | |||
| 552 | }; | 552 | }; |
| 553 | 553 | ||
| 554 | layout (std140) uniform shader_data { | 554 | layout (std140) uniform shader_data { |
| 555 | vec4 const_color[NUM_TEV_STAGES]; | ||
| 556 | vec4 tev_combiner_buffer_color; | ||
| 557 | int alphatest_ref; | 555 | int alphatest_ref; |
| 558 | float depth_scale; | 556 | float depth_scale; |
| 559 | float depth_offset; | 557 | float depth_offset; |
| 558 | vec3 fog_color; | ||
| 560 | vec3 lighting_global_ambient; | 559 | vec3 lighting_global_ambient; |
| 561 | LightSrc light_src[NUM_LIGHTS]; | 560 | LightSrc light_src[NUM_LIGHTS]; |
| 561 | vec4 const_color[NUM_TEV_STAGES]; | ||
| 562 | vec4 tev_combiner_buffer_color; | ||
| 562 | }; | 563 | }; |
| 563 | 564 | ||
| 564 | uniform sampler2D tex[3]; | 565 | uniform sampler2D tex[3]; |
| 565 | uniform sampler1D lut[6]; | 566 | uniform sampler1D lut[6]; |
| 567 | uniform usampler1D fog_lut; | ||
| 566 | 568 | ||
| 567 | // Rotate the vector v by the quaternion q | 569 | // Rotate the vector v by the quaternion q |
| 568 | vec3 quaternion_rotate(vec4 q, vec3 v) { | 570 | vec3 quaternion_rotate(vec4 q, vec3 v) { |
| @@ -580,6 +582,12 @@ vec4 secondary_fragment_color = vec4(0.0); | |||
| 580 | return out; | 582 | return out; |
| 581 | } | 583 | } |
| 582 | 584 | ||
| 585 | out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; | ||
| 586 | out += "float depth = z_over_w * depth_scale + depth_offset;\n"; | ||
| 587 | if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { | ||
| 588 | out += "depth /= gl_FragCoord.w;\n"; | ||
| 589 | } | ||
| 590 | |||
| 583 | if (state.lighting.enable) | 591 | if (state.lighting.enable) |
| 584 | WriteLighting(out, config); | 592 | WriteLighting(out, config); |
| 585 | 593 | ||
| @@ -596,14 +604,30 @@ vec4 secondary_fragment_color = vec4(0.0); | |||
| 596 | out += ") discard;\n"; | 604 | out += ") discard;\n"; |
| 597 | } | 605 | } |
| 598 | 606 | ||
| 599 | out += "color = last_tex_env_out;\n"; | 607 | // Append fog combiner |
| 608 | if (state.fog_mode == Regs::FogMode::Fog) { | ||
| 609 | // Get index into fog LUT | ||
| 610 | if (state.fog_flip) { | ||
| 611 | out += "float fog_index = (1.0 - depth) * 128.0;\n"; | ||
| 612 | } else { | ||
| 613 | out += "float fog_index = depth * 128.0;\n"; | ||
| 614 | } | ||
| 600 | 615 | ||
| 601 | out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n"; | 616 | // Generate clamped fog factor from LUT for given fog index |
| 602 | out += "float depth = z_over_w * depth_scale + depth_offset;\n"; | 617 | out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; |
| 603 | if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) { | 618 | out += "float fog_f = fog_index - fog_i;\n"; |
| 604 | out += "depth /= gl_FragCoord.w;\n"; | 619 | out += "uint fog_lut_entry = texelFetch(fog_lut, int(fog_i), 0).r;\n"; |
| 620 | out += "float fog_lut_entry_difference = float(int((fog_lut_entry & 0x1FFFU) << 19U) >> 19);\n"; // Extract signed difference | ||
| 621 | out += "float fog_lut_entry_value = float((fog_lut_entry >> 13U) & 0x7FFU);\n"; | ||
| 622 | out += "float fog_factor = (fog_lut_entry_value + fog_lut_entry_difference * fog_f) / 2047.0;\n"; | ||
| 623 | out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; | ||
| 624 | |||
| 625 | // Blend the fog | ||
| 626 | out += "last_tex_env_out.rgb = mix(fog_color.rgb, last_tex_env_out.rgb, fog_factor);\n"; | ||
| 605 | } | 627 | } |
| 628 | |||
| 606 | out += "gl_FragDepth = depth;\n"; | 629 | out += "gl_FragDepth = depth;\n"; |
| 630 | out += "color = last_tex_env_out;\n"; | ||
| 607 | 631 | ||
| 608 | out += "}"; | 632 | out += "}"; |
| 609 | 633 | ||
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index fa141fc9a..13ee986b9 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -58,6 +58,8 @@ OpenGLState::OpenGLState() { | |||
| 58 | lut.texture_1d = 0; | 58 | lut.texture_1d = 0; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | fog_lut.texture_1d = 0; | ||
| 62 | |||
| 61 | draw.read_framebuffer = 0; | 63 | draw.read_framebuffer = 0; |
| 62 | draw.draw_framebuffer = 0; | 64 | draw.draw_framebuffer = 0; |
| 63 | draw.vertex_array = 0; | 65 | draw.vertex_array = 0; |
| @@ -195,6 +197,12 @@ void OpenGLState::Apply() const { | |||
| 195 | } | 197 | } |
| 196 | } | 198 | } |
| 197 | 199 | ||
| 200 | // Fog LUT | ||
| 201 | if (fog_lut.texture_1d != cur_state.fog_lut.texture_1d) { | ||
| 202 | glActiveTexture(GL_TEXTURE9); | ||
| 203 | glBindTexture(GL_TEXTURE_1D, fog_lut.texture_1d); | ||
| 204 | } | ||
| 205 | |||
| 198 | // Framebuffer | 206 | // Framebuffer |
| 199 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { | 207 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { |
| 200 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); | 208 | 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 228727054..13c71b0a6 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -68,6 +68,10 @@ public: | |||
| 68 | } lighting_luts[6]; | 68 | } lighting_luts[6]; |
| 69 | 69 | ||
| 70 | struct { | 70 | struct { |
| 71 | GLuint texture_1d; // GL_TEXTURE_BINDING_1D | ||
| 72 | } fog_lut; | ||
| 73 | |||
| 74 | struct { | ||
| 71 | GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING | 75 | GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING |
| 72 | GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING | 76 | GLuint draw_framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING |
| 73 | GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING | 77 | GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING |