diff --git a/Utilities/geometry.h b/Utilities/geometry.h index 3ffbc04dd3..a5881d1cd5 100644 --- a/Utilities/geometry.h +++ b/Utilities/geometry.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -1027,3 +1028,6 @@ using color1u = color1_base; using color1i = color1_base; using color1f = color1_base; using color1d = color1_base; + +using mat3f = color3_base[3]; +static_assert(sizeof(mat3f) == sizeof(float) * 3 * 3); diff --git a/Utilities/stereo_config.cpp b/Utilities/stereo_config.cpp new file mode 100644 index 0000000000..cba9a542f2 --- /dev/null +++ b/Utilities/stereo_config.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "stereo_config.h" +#include "Emu/system_config.h" + +stereo_config::stereo_matrices::stereo_matrices(const std::array, 3>& l, const std::array, 3>& r) +{ + for (usz i = 0; i < 3; i++) + { + left[i] = l[i]; + right[i] = r[i]; + } +} + +const std::unordered_map stereo_config::m_matrices = +{ + {stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices( + { color3_base(1, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 0) }, + { color3_base(0, 0, 0), color3_base(0, 1, 0), color3_base(0, 0, 1) } + )}, + {stereo_render_mode_options::anaglyph_red_green, stereo_matrices( + { color3_base(1, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 0) }, + { color3_base(0, 0, 0), color3_base(0, 1, 0), color3_base(0, 0, 0) } + )}, + {stereo_render_mode_options::anaglyph_red_blue, stereo_matrices( + { color3_base(1, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 0) }, + { color3_base(0, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 1) } + )}, + {stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices( + { color3_base(1, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 0.5f) }, + { color3_base(0, 0, 0), color3_base(0, 1, 0), color3_base(0, 0, 0.5f) } + )}, + {stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices( + { color3_base(0, 0, 0), color3_base(0, 1, 0), color3_base(0, 0, 0) }, + { color3_base(1, 0, 0), color3_base(0, 0, 0), color3_base(0, 0, 1) } + )}, + {stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices( + { color3_base(1, 0, 0), color3_base(0, 1, 0), color3_base(0, 0, 0) }, + { color3_base(0, 0, 0), color3_base(0, 0, 0), color3_base(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f) } + )}, +}; + +//const std::unordered_map stereo_config::m_improved_matrices = +//{ +// {stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices( +// {color3_base(0.456f, 0.500f, 0.176f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f)}, +// {color3_base(-0.043f, -0.088f, -0.002f), color3_base(0.378f, 0.734f, -0.018f), color3_base(-0.072f, -0.113f, 1.226f)} +// )}, +// {stereo_render_mode_options::anaglyph_red_green, stereo_matrices( +// {color3_base(0.299f, 0.587f, 0.114f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f)}, +// {color3_base(0.0f, 0.0f, 0.0f), color3_base(0.299f, 0.587f, 0.114f), color3_base(0.0f, 0.0f, 0.0f)} +// )}, +// {stereo_render_mode_options::anaglyph_red_blue, stereo_matrices( +// {color3_base(0.299f, 0.587f, 0.114f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f)}, +// {color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.299f, 0.587f, 0.114f)} +// )}, +// {stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices( +// {color3_base(0.437f, 0.449f, 0.164f), color3_base(0.449f, 0.869f, 0.081f), color3_base(0.0f, 0.0f, 1.0f)}, +// {color3_base(-0.062f, -0.062f, 0.0f), color3_base(-0.062f, 1.0f, 0.062f), color3_base(0.0f, 0.0f, 1.0f)} +// )}, +// {stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices( +// {color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 1.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f)}, +// {color3_base(1.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 1.0f)} +// )}, +// {stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices( +// {color3_base(0.299f, 0.587f, 0.0f), color3_base(0.299f, 0.587f, 0.0f), color3_base(0.0f, 0.0f, 0.0f)}, +// {color3_base(0.0f, 0.0f, 0.0f), color3_base(0.0f, 0.0f, 0.0f), color3_base(0.3333f, 0.3333f, 0.3333f)} +// )}, +//}; + +void stereo_config::update_from_config(bool stereo_enabled) +{ + m_stereo_mode = stereo_enabled ? g_cfg.video.stereo_render_mode.get() : stereo_render_mode_options::disabled; + + if (m_stereo_mode == stereo_render_mode_options::anaglyph_custom) + { + stereo_config::stereo_matrices custom_matrices {}; + stereo_config::convert_matrix(m_custom_matrices.left, g_cfg.video.custom_anaglyph_matrices.left.get_map()); + stereo_config::convert_matrix(m_custom_matrices.right, g_cfg.video.custom_anaglyph_matrices.right.get_map()); + } +} + +const stereo_config::stereo_matrices& stereo_config::matrices() const +{ + switch (m_stereo_mode) + { + case stereo_render_mode_options::disabled: + case stereo_render_mode_options::side_by_side: + case stereo_render_mode_options::over_under: + case stereo_render_mode_options::interlaced: + break; + case stereo_render_mode_options::anaglyph_red_green: + case stereo_render_mode_options::anaglyph_red_blue: + case stereo_render_mode_options::anaglyph_red_cyan: + case stereo_render_mode_options::anaglyph_magenta_cyan: + case stereo_render_mode_options::anaglyph_trioscopic: + case stereo_render_mode_options::anaglyph_amber_blue: + return ::at32(m_matrices, m_stereo_mode); + case stereo_render_mode_options::anaglyph_custom: + return m_custom_matrices; + } + + static const stereo_matrices s_empty_matrices = {}; + return s_empty_matrices; +} + +std::map stereo_config::get_custom_matrix(bool is_left) const +{ + return convert_matrix(is_left ? m_custom_matrices.left : m_custom_matrices.right); +} + +std::map stereo_config::convert_matrix(const mat3f& mat) +{ + std::map values {}; + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + values[fmt::format("%d%d", i, j)] = fmt::format("%f", mat[i].rgb[j]); + } + } + return values; +} diff --git a/Utilities/stereo_config.h b/Utilities/stereo_config.h new file mode 100644 index 0000000000..01b5d9d6cf --- /dev/null +++ b/Utilities/stereo_config.h @@ -0,0 +1,69 @@ +#pragma once + +#include "geometry.h" +#include "Emu/system_config_types.h" +#include "Utilities/StrUtil.h" +#include + +struct stereo_config +{ +public: + struct stereo_matrices + { + stereo_matrices() = default; + stereo_matrices(const std::array, 3>& l, const std::array, 3>& r); + + mat3f left {}; + mat3f right {}; + }; + + void set_stereo_mode(stereo_render_mode_options mode) { m_stereo_mode = mode; } + stereo_render_mode_options stereo_mode() const { return m_stereo_mode; } + + const stereo_matrices& matrices() const; + + void update_from_config(bool stereo_enabled); + + void set_custom_matrices(const stereo_matrices& matrices) { m_custom_matrices = matrices; } + + std::map get_custom_matrix(bool is_left) const; + + template + static void convert_matrix(mat3f& mat, const T& values) + { + mat[0] = {}; + mat[1] = {}; + mat[2] = {}; + + if (values.empty()) + { + return; + } + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + const std::string key = fmt::format("%d%d", i, j); + const auto it = values.find(key); + if (it == values.cend()) continue; + + const std::string& val_s = it->second; + if (val_s.empty()) continue; + + if (f64 val = 0.0f; try_to_float(&val, val_s, 0.0f, 1.0f)) + { + mat[i].rgb[j] = static_cast(val); + } + } + } + } + + static std::map convert_matrix(const mat3f& mat); + +private: + stereo_matrices m_custom_matrices {}; + stereo_render_mode_options m_stereo_mode = stereo_render_mode_options::disabled; + + static const std::unordered_map m_matrices; +}; diff --git a/rpcs3/Crypto/unpkg.h b/rpcs3/Crypto/unpkg.h index 0b7971c07e..621d811207 100644 --- a/rpcs3/Crypto/unpkg.h +++ b/rpcs3/Crypto/unpkg.h @@ -126,7 +126,7 @@ struct PKGEntry struct PKGMetaData { private: - static std::string to_hex_string(u8 buf[], usz size) + static std::string to_hex_string(const u8* buf, usz size) { std::stringstream sstream; for (usz i = 0; i < size; i++) @@ -135,7 +135,7 @@ private: } return sstream.str(); } - static std::string to_hex_string(u8 buf[], usz size, usz dotpos) + static std::string to_hex_string(const u8* buf, usz size, usz dotpos) { std::string result = to_hex_string(buf, size); if (result.size() > dotpos) diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 8591399ce8..66e8848684 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -77,6 +77,7 @@ target_sources(rpcs3_emu PRIVATE ../../Utilities/sema.cpp ../../Utilities/simple_ringbuf.cpp ../../Utilities/stack_trace.cpp + ../../Utilities/stereo_config.cpp ../../Utilities/StrFmt.cpp ../../Utilities/Thread.cpp ../../Utilities/version.cpp diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.cpp b/rpcs3/Emu/RSX/GL/GLOverlays.cpp index a758804e4f..22235b6b82 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.cpp +++ b/rpcs3/Emu/RSX/GL/GLOverlays.cpp @@ -1,6 +1,7 @@ #include "GLOverlays.h" #include "Utilities/StrUtil.h" +#include "Utilities/stereo_config.h" #include "../Program/RSXOverlay.h" #include "Emu/Cell/timers.hpp" @@ -510,7 +511,7 @@ namespace gl } void video_out_calibration_pass::run(gl::command_context& cmd, const areau& viewport, const rsx::simple_array& source, f32 gamma, bool limited_rgb, - bool stereo_enabled, stereo_render_mode_options stereo_mode, gl::filter input_filter) + const stereo_config& stereo_cfg, gl::filter input_filter) { if (m_input_filter != input_filter) { @@ -519,10 +520,14 @@ namespace gl m_sampler.set_parameteri(GL_TEXTURE_MAG_FILTER, static_cast(m_input_filter)); } + const auto& matrices = stereo_cfg.matrices(); + program_handle.uniforms["gamma"] = gamma; program_handle.uniforms["limit_range"] = limited_rgb + 0; - program_handle.uniforms["stereo_display_mode"] = stereo_enabled ? static_cast(stereo_mode) : 0; - program_handle.uniforms["stereo_image_count"] = (source[1] == GL_NONE? 1 : 2); + program_handle.uniforms["stereo_display_mode"] = static_cast(stereo_cfg.stereo_mode()); + program_handle.uniforms["stereo_image_count"] = (source[1] == GL_NONE ? 1 : 2); + program_handle.uniforms["left_anaglyph_matrix"] = matrices.left; + program_handle.uniforms["right_anaglyph_matrix"] = matrices.right; saved_sampler_state saved(GL_TEMP_IMAGE_SLOT(0), m_sampler); cmd->bind_texture(GL_TEMP_IMAGE_SLOT(0), GL_TEXTURE_2D, source[0]); diff --git a/rpcs3/Emu/RSX/GL/GLOverlays.h b/rpcs3/Emu/RSX/GL/GLOverlays.h index 8ccfd67305..e32852e7ad 100644 --- a/rpcs3/Emu/RSX/GL/GLOverlays.h +++ b/rpcs3/Emu/RSX/GL/GLOverlays.h @@ -14,6 +14,8 @@ #include #include +struct stereo_config; + namespace gl { struct overlay_pass @@ -98,7 +100,7 @@ namespace gl video_out_calibration_pass(); void run(gl::command_context& cmd, const areau& viewport, const rsx::simple_array& source, f32 gamma, bool limited_rgb, - bool stereo_enabled, stereo_render_mode_options stereo_mode, gl::filter input_filter); + const stereo_config& stereo_cfg, gl::filter input_filter); }; struct rp_ssbo_to_generic_texture final : public overlay_pass diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 2aa11868ee..97f2bcaaa5 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -10,6 +10,7 @@ #include "Emu/RSX/Overlays/overlay_debug_overlay.h" #include "util/video_provider.h" +#include "Utilities/stereo_config.h" LOG_CHANNEL(screenshot_log, "SCREENSHOT"); @@ -435,8 +436,11 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) } } + static stereo_config stereo_cfg = {}; + stereo_cfg.update_from_config(avconfig.stereo_enabled); + gl::screen.bind(); - m_video_output_pass.run(cmd, areau(aspect_ratio), images.map(FN(x ? x->id() : GL_NONE)), gamma, limited_range, avconfig.stereo_enabled, g_cfg.video.stereo_render_mode, filter); + m_video_output_pass.run(cmd, areau(aspect_ratio), images.map(FN(x ? x->id() : GL_NONE)), gamma, limited_range, stereo_cfg, filter); } } diff --git a/rpcs3/Emu/RSX/GL/glutils/program.h b/rpcs3/Emu/RSX/GL/glutils/program.h index 5caca0ed98..23238faea4 100644 --- a/rpcs3/Emu/RSX/GL/glutils/program.h +++ b/rpcs3/Emu/RSX/GL/glutils/program.h @@ -102,6 +102,7 @@ namespace gl void operator = (const areaf& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); } void operator = (const areai& rhs) const { glProgramUniform4i(m_program.id(), location(), rhs.x1, rhs.y1, rhs.x2, rhs.y2); } void operator = (const std::vector& rhs) const { glProgramUniform1iv(m_program.id(), location(), ::size32(rhs), rhs.data()); } + void operator = (const mat3f& rhs) const { glProgramUniformMatrix3fv(m_program.id(), location(), 1, GL_TRUE, &rhs[0].rgb[0]); } }; class uniforms_t diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/VideoOutCalibrationPass.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/VideoOutCalibrationPass.glsl index f33dbd6aa5..8ebc8ae787 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/VideoOutCalibrationPass.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/VideoOutCalibrationPass.glsl @@ -19,6 +19,7 @@ layout(location=0) out vec4 ocol; #define STEREO_MODE_ANAGLYPH_MAGENTA_CYAN 7 #define STEREO_MODE_ANAGLYPH_TRIOSCOPIC 8 #define STEREO_MODE_ANAGLYPH_AMBER_BLUE 9 +#define STEREO_MODE_ANAGLYPH_CUSTOM 10 #define TRUE 1 #define FALSE 0 @@ -37,26 +38,28 @@ layout(push_constant) uniform static_data int limit_range; int stereo_display_mode; int stereo_image_count; + mat3 left_anaglyph_matrix; + mat3 right_anaglyph_matrix; }; #else uniform float gamma; uniform int limit_range; uniform int stereo_display_mode; uniform int stereo_image_count; +uniform mat3 left_anaglyph_matrix; +uniform mat3 right_anaglyph_matrix; #endif +vec3 applyMatrix(vec3 left, vec3 right) +{ + vec3 outColor = left_anaglyph_matrix * left + right_anaglyph_matrix * right; + return clamp(outColor, 0.0, 1.0); +} + vec4 anaglyph(const in vec4 left, const in vec4 right) { - switch (stereo_display_mode) - { - case STEREO_MODE_ANAGLYPH_RED_GREEN: return vec4(left.r, right.g, 0.f, 1.f); - case STEREO_MODE_ANAGLYPH_RED_BLUE: return vec4(left.r, 0.f, right.b, 1.f); - case STEREO_MODE_ANAGLYPH_RED_CYAN: return vec4(left.r, right.g, right.b, 1.f); - case STEREO_MODE_ANAGLYPH_MAGENTA_CYAN: return vec4(left.r, right.g, (left.b + right.b) / 2.f, 1.f); - case STEREO_MODE_ANAGLYPH_TRIOSCOPIC: return vec4(right.r, left.g, right.b, 1.f); - case STEREO_MODE_ANAGLYPH_AMBER_BLUE: return vec4(left.r, left.g, (right.r + right.g + right.b) / 3.f, 1.f); - default: return texture(fs0, tc0); - } + vec3 color = applyMatrix(left.rgb, right.rgb); + return vec4(color, 1.0); } vec4 anaglyph_single_image() @@ -92,6 +95,7 @@ vec4 read_source() case STEREO_MODE_ANAGLYPH_MAGENTA_CYAN: case STEREO_MODE_ANAGLYPH_TRIOSCOPIC: case STEREO_MODE_ANAGLYPH_AMBER_BLUE: + case STEREO_MODE_ANAGLYPH_CUSTOM: return anaglyph_single_image(); case STEREO_MODE_SIDE_BY_SIDE: return (tc0.x < 0.5) @@ -120,6 +124,7 @@ vec4 read_source() case STEREO_MODE_ANAGLYPH_MAGENTA_CYAN: case STEREO_MODE_ANAGLYPH_TRIOSCOPIC: case STEREO_MODE_ANAGLYPH_AMBER_BLUE: + case STEREO_MODE_ANAGLYPH_CUSTOM: return anaglyph_stereo_image(); case STEREO_MODE_SIDE_BY_SIDE: return (tc0.x < 0.5) diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index 684cf8fedb..414a5e78c3 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -14,6 +14,7 @@ #include "../Program/RSXOverlay.h" #include "util/fnv_hash.hpp" +#include "Utilities/stereo_config.h" #include "Emu/Cell/timers.hpp" @@ -911,17 +912,21 @@ namespace vk void video_out_calibration_pass::update_uniforms(vk::command_buffer& cmd, vk::glsl::program* program) { - vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, 16, config.data); + vkCmdPushConstants(cmd, program->layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(config.data), config.data); } void video_out_calibration_pass::run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, const rsx::simple_array& src, f32 gamma, bool limited_rgb, - bool stereo_enabled, stereo_render_mode_options stereo_mode, VkRenderPass render_pass) + const stereo_config& stereo_cfg, VkRenderPass render_pass) { + const auto& matrices = stereo_cfg.matrices(); + config.gamma = gamma; - config.limit_range = limited_rgb? 1 : 0; - config.stereo_display_mode = stereo_enabled ? static_cast(stereo_mode) : 0; + config.limit_range = limited_rgb ? 1 : 0; + config.stereo_display_mode = static_cast(stereo_cfg.stereo_mode()); config.stereo_image_count = std::min(::size32(src), 2u); + std::memcpy(config.left_anaglyph_matrix, matrices.left, sizeof(matrices.left)); + std::memcpy(config.right_anaglyph_matrix, matrices.right, sizeof(matrices.right)); std::vector views; views.reserve(2); diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index de2c218ebe..3c2a8c25ed 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -14,6 +14,8 @@ #include +struct stereo_config; + namespace rsx { namespace overlays @@ -209,9 +211,11 @@ namespace vk int limit_range; int stereo_display_mode; int stereo_image_count; + mat3f left_anaglyph_matrix; + mat3f right_anaglyph_matrix; }; - float data[4]; + float data[22]; } config = {}; @@ -223,7 +227,7 @@ namespace vk void run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target, const rsx::simple_array& src, f32 gamma, bool limited_rgb, - bool stereo_enabled, stereo_render_mode_options stereo_mode, VkRenderPass render_pass); + const stereo_config& stereo_cfg, VkRenderPass render_pass); }; // TODO: Replace with a proper manager diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 432e8c7e87..5cbdf69974 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -11,6 +11,7 @@ #include "upscalers/nearest_pass.hpp" #include "util/asm.hpp" #include "util/video_provider.h" +#include "Utilities/stereo_config.h" extern atomic_t g_user_asked_for_screenshot; extern atomic_t g_recording_mode; @@ -837,9 +838,12 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) direct_fbo = vk::get_framebuffer(*m_device, m_swapchain_dims.width, m_swapchain_dims.height, VK_FALSE, single_target_pass, m_swapchain->get_surface_format(), target_image); direct_fbo->add_ref(); + static stereo_config stereo_cfg = {}; + stereo_cfg.update_from_config(avconfig.stereo_enabled); + vk::get_overlay_pass()->run( *m_current_command_buffer, areau(aspect_ratio), direct_fbo, calibration_src, - avconfig.gamma, !use_full_rgb_range_output, avconfig.stereo_enabled, g_cfg.video.stereo_render_mode, single_target_pass); + avconfig.gamma, !use_full_rgb_range_output, stereo_cfg, single_target_pass); direct_fbo->release(); } diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index f9f6f2c8d3..cf7b6ef21b 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -233,6 +233,15 @@ struct cfg_root : cfg::node } shader_preloading_dialog{ this }; + struct anaglyph_matrices : cfg::node + { + anaglyph_matrices(cfg::node* _this) : cfg::node(_this, "Custom Anaglyph Matrices") {} + + cfg::node_map_entry left{ this, "Matrix Left" }; + cfg::node_map_entry right{ this, "Matrix Right" }; + + } custom_anaglyph_matrices{ this }; + } video{ this }; struct node_audio : cfg::node diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index daefd49a42..fa9dcc94f4 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -665,6 +665,7 @@ void fmt_class_string::format(std::string& out, u64 case stereo_render_mode_options::anaglyph_magenta_cyan: return "Anaglyph Magenta-Cyan"; case stereo_render_mode_options::anaglyph_trioscopic: return "Anaglyph Trioscopic"; case stereo_render_mode_options::anaglyph_amber_blue: return "Anaglyph Amber-Blue"; + case stereo_render_mode_options::anaglyph_custom: return "Anaglyph Custom"; } return unknown; diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index f11e9caa3f..0b44794497 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -354,6 +354,7 @@ enum class stereo_render_mode_options anaglyph_magenta_cyan, anaglyph_trioscopic, anaglyph_amber_blue, + anaglyph_custom, }; enum class xfloat_accuracy diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index c89a066075..30fdc03dba 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -59,6 +59,7 @@ + @@ -564,6 +565,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 6b9c82c959..9b2f970ee9 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1429,6 +1429,9 @@ Emu\GPU\RSX\Overlays + + Utilities + @@ -2875,6 +2878,9 @@ Emu\GPU\RSX\Common + + Utilities + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 2ab3e592bc..a4fc0bd16b 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -832,6 +832,7 @@ + @@ -1190,6 +1191,7 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" + $(QTDIR)\bin\moc.exe;%(FullPath) diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 8c5b6b2905..40985b71a8 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1302,6 +1302,9 @@ Gui\settings + + Gui\settings + @@ -1553,6 +1556,9 @@ Gui\utils + + Gui\settings + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 0a63343e96..154b9da84b 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(rpcs3_ui STATIC about_dialog.cpp + anaglyph_settings_dialog.cpp auto_pause_settings_dialog.cpp basic_mouse_settings_dialog.cpp breakpoint_handler.cpp diff --git a/rpcs3/rpcs3qt/anaglyph_settings_dialog.cpp b/rpcs3/rpcs3qt/anaglyph_settings_dialog.cpp new file mode 100644 index 0000000000..27faa107b2 --- /dev/null +++ b/rpcs3/rpcs3qt/anaglyph_settings_dialog.cpp @@ -0,0 +1,272 @@ +#include "stdafx.h" +#include "anaglyph_settings_dialog.h" + +#include +#include +#include +#include + +color_wedge_widget::color_wedge_widget(QWidget* parent) + : QWidget(parent) +{ + setObjectName("color_wedge_widget"); + setMinimumSize(300, 300); +} + +void color_wedge_widget::set_option(stereo_render_mode_options option) +{ + m_stereo_config.set_stereo_mode(option); + update(); +} + +void color_wedge_widget::set_custom_matrices(const stereo_config::stereo_matrices& matrices) +{ + m_stereo_config.set_custom_matrices(matrices); + update(); +} + +QVector3D color_wedge_widget::apply_matrix(const QVector3D& left, const QVector3D& right, const stereo_config::stereo_matrices& m) +{ + const mat3f& ml = m.left; + const mat3f& mr = m.right; + + QVector3D out; + out.setX(ml[0].x * left.x() + ml[0].y * left.y() + ml[0].z * left.z() + + mr[0].x * right.x() + mr[0].y * right.y() + mr[0].z * right.z()); + + out.setY(ml[1].x * left.x() + ml[1].y * left.y() + ml[1].z * left.z() + + mr[1].x * right.x() + mr[1].y * right.y() + mr[1].z * right.z()); + + out.setZ(ml[2].x * left.x() + ml[2].y * left.y() + ml[2].z * left.z() + + mr[2].x * right.x() + mr[2].y * right.y() + mr[2].z * right.z()); + + out.setX(std::clamp(out.x(), 0.0f, 1.0f)); + out.setY(std::clamp(out.y(), 0.0f, 1.0f)); + out.setZ(std::clamp(out.z(), 0.0f, 1.0f)); + + return out; +} + +QVector3D color_wedge_widget::apply_anaglyph_matrix(const QVector3D& left, const QVector3D& right) +{ + if (m_stereo_config.stereo_mode() == stereo_render_mode_options::disabled) + { + return left; + } + + return apply_matrix(left, right, m_stereo_config.matrices()); +} + +void color_wedge_widget::paintEvent(QPaintEvent* /*event*/) +{ + const int w = width(); + const int h = height(); + + if (m_img.width() != w || m_img.height() != h) + { + m_img = QImage(w, h, QImage::Format_RGB32); + } + + QVector3D out; + + // Loop over pixels + for (int y = 0; y < h; ++y) + { + const float v = float(y) / (h - 1); + + for (int x = 0; x < w; ++x) + { + const float u = float(x) / (w - 1); + + const QVector3D left(u, v, 1.0f - u); + const QVector3D right = left; + + const QVector3D out = apply_anaglyph_matrix(left, right); + + m_img.setPixel(x, y, qRgb(int(out.x() * 255), int(out.y() * 255), int(out.z() * 255))); + } + } + + // Paint the image + QPainter p(this); + p.drawImage(0, 0, m_img); +} + +anaglyph_settings_dialog::anaglyph_settings_dialog(QWidget* parent, std::shared_ptr emu_settings) + : QDialog(parent) + , m_emu_settings(std::move(emu_settings)) +{ + setWindowTitle(tr("Anaglyph Settings")); + setObjectName("anaglyph_settings_dialog"); + setAttribute(Qt::WA_DeleteOnClose); + + stereo_render_mode_options current_mode = stereo_render_mode_options::disabled; + if (u64 result {}; cfg::try_to_enum_value(&result, &fmt_class_string::format, m_emu_settings->GetSetting(emu_settings_type::StereoRenderMode))) + { + const auto mode = static_cast(static_cast>(result)); + switch (mode) + { + case stereo_render_mode_options::anaglyph_red_green: + case stereo_render_mode_options::anaglyph_red_blue: + case stereo_render_mode_options::anaglyph_red_cyan: + case stereo_render_mode_options::anaglyph_magenta_cyan: + case stereo_render_mode_options::anaglyph_trioscopic: + case stereo_render_mode_options::anaglyph_amber_blue: + case stereo_render_mode_options::anaglyph_custom: + current_mode = mode; + break; + default: + break; + } + } + + stereo_config::stereo_matrices custom_matrices {}; + stereo_config::convert_matrix(custom_matrices.left, m_emu_settings->GetMapSetting(emu_settings_type::CustomAnaglyphMatrixLeft)); + stereo_config::convert_matrix(custom_matrices.right, m_emu_settings->GetMapSetting(emu_settings_type::CustomAnaglyphMatrixRight)); + + m_widget_reference = new color_wedge_widget(this); + m_widget_reference->set_option(stereo_render_mode_options::disabled); + + m_widget = new color_wedge_widget(this); + m_widget->set_option(current_mode); + m_widget->set_custom_matrices(custom_matrices); + + QPushButton* button = new QPushButton(tr("Apply Custom Matrices")); + connect(button, &QAbstractButton::clicked, [this]() + { + m_emu_settings->SetMapSetting(emu_settings_type::CustomAnaglyphMatrixLeft, m_widget->get_stereo_config().get_custom_matrix(true)); + m_emu_settings->SetMapSetting(emu_settings_type::CustomAnaglyphMatrixRight, m_widget->get_stereo_config().get_custom_matrix(false)); + }); + button->setEnabled(current_mode == stereo_render_mode_options::anaglyph_custom); + + QComboBox* combo = new QComboBox(this); + combo->addItem(tr("Disabled"), static_cast(stereo_render_mode_options::disabled)); + combo->addItem(tr("Red-Green"), static_cast(stereo_render_mode_options::anaglyph_red_green)); + combo->addItem(tr("Red-Blue"), static_cast(stereo_render_mode_options::anaglyph_red_blue)); + combo->addItem(tr("Red-Cyan"), static_cast(stereo_render_mode_options::anaglyph_red_cyan)); + combo->addItem(tr("Magenta-Cyan"), static_cast(stereo_render_mode_options::anaglyph_magenta_cyan)); + combo->addItem(tr("Trioscopic"), static_cast(stereo_render_mode_options::anaglyph_trioscopic)); + combo->addItem(tr("Amber-Blue"), static_cast(stereo_render_mode_options::anaglyph_amber_blue)); + combo->addItem(tr("Custom"), static_cast(stereo_render_mode_options::anaglyph_custom)); + combo->setCurrentIndex(combo->findData(static_cast(current_mode))); + + connect(combo, &QComboBox::currentIndexChanged, this, [this, combo, button](int /*index*/) + { + button->setEnabled(static_cast(combo->currentData().toInt()) == stereo_render_mode_options::anaglyph_custom); + m_widget->set_option(static_cast(combo->currentData().toInt())); + update_matrices(); + }); + + m_matrix_left = create_matrix_table(); + m_matrix_right = create_matrix_table(); + + update_matrices(); + + const auto group_box = [](const QString& title, QWidget* widget) + { + QHBoxLayout* layout = new QHBoxLayout(); + layout->addWidget(widget); + + QGroupBox* gb = new QGroupBox(title); + gb->setLayout(layout); + return gb; + }; + + QHBoxLayout* lay_h_1 = new QHBoxLayout(); + lay_h_1->addWidget(combo); + lay_h_1->addWidget(button); + lay_h_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); + + QHBoxLayout* lay_h_2 = new QHBoxLayout(); + lay_h_2->addWidget(group_box(tr("Left matrix"), m_matrix_left)); + lay_h_2->addWidget(group_box(tr("Right matrix"), m_matrix_right)); + + QHBoxLayout* lay_h_3 = new QHBoxLayout(); + lay_h_3->addWidget(group_box(tr("Reference"), m_widget_reference)); + lay_h_3->addWidget(group_box(tr("Anaglyph"), m_widget)); + + QVBoxLayout* lay = new QVBoxLayout(); + lay->addLayout(lay_h_1); + lay->addLayout(lay_h_2); + lay->addLayout(lay_h_3); + + setLayout(lay); + adjustSize(); +} + +QTableWidget* anaglyph_settings_dialog::create_matrix_table() +{ + QTableWidget* table = new QTableWidget(3, 3, this); + table->setVerticalHeaderLabels({tr("R out"), tr("G out"), tr("B out")}); + table->setHorizontalHeaderLabels({tr("R in"), tr("G in"), tr("B in")}); + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + table->setItem(r, c, new QTableWidgetItem("0.0")); + } + } + connect(table, &QTableWidget::cellChanged, this, [this]() + { + apply_custom_matrix(); + }); + + table->resizeColumnsToContents(); + table->resizeRowsToContents(); + return table; +} + +void anaglyph_settings_dialog::read_matrix(mat3f& mat, QTableWidget* table) const +{ + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + bool ok = false; + const float val = table->item(r, c)->text().toFloat(&ok); + mat[r].rgb[c] = ok ? val : 0.0f; + } + } +} + +void anaglyph_settings_dialog::update_matrix(QTableWidget* table, const mat3f& matrix, bool is_custom) +{ + if (!table) return; + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + if (QTableWidgetItem* item = table->item(r, c)) + { + Qt::ItemFlags flags = item->flags(); + flags.setFlag(Qt::ItemIsEditable, is_custom); + + item->setFlags(flags); + item->setText(QString::number(matrix[r].rgb[c])); + } + } + } +} + +void anaglyph_settings_dialog::update_matrices() +{ + if (!m_widget) return; + const stereo_config& stereo_cfg = m_widget->get_stereo_config(); + const stereo_config::stereo_matrices& matrices = stereo_cfg.matrices(); + + const bool is_custom = stereo_cfg.stereo_mode() == stereo_render_mode_options::anaglyph_custom; + + m_updating = true; + update_matrix(m_matrix_left, matrices.left, is_custom); + update_matrix(m_matrix_right, matrices.right, is_custom); + m_updating = false; +} + +void anaglyph_settings_dialog::apply_custom_matrix() +{ + if (!m_widget || m_updating) return; + stereo_config::stereo_matrices m {}; + read_matrix(m.left, m_matrix_left); + read_matrix(m.right, m_matrix_right); + m_widget->set_custom_matrices(m); +} diff --git a/rpcs3/rpcs3qt/anaglyph_settings_dialog.h b/rpcs3/rpcs3qt/anaglyph_settings_dialog.h new file mode 100644 index 0000000000..628a5f1017 --- /dev/null +++ b/rpcs3/rpcs3qt/anaglyph_settings_dialog.h @@ -0,0 +1,53 @@ +#pragma once + +#include "emu_settings.h" +#include "Utilities/stereo_config.h" + +#include +#include +#include + +class color_wedge_widget : public QWidget +{ +public: + color_wedge_widget(QWidget* parent = nullptr); + + void set_option(stereo_render_mode_options option); + void set_custom_matrices(const stereo_config::stereo_matrices& matrices); + + const stereo_config& get_stereo_config() const { return m_stereo_config; } + +protected: + QVector3D apply_matrix(const QVector3D& left, const QVector3D& right, const stereo_config::stereo_matrices& m); + + QVector3D apply_anaglyph_matrix(const QVector3D& left, const QVector3D& right); + + void paintEvent(QPaintEvent* event) override; + + stereo_config m_stereo_config {}; + QImage m_img; +}; + +class anaglyph_settings_dialog : public QDialog +{ +public: + anaglyph_settings_dialog(QWidget* parent, std::shared_ptr emu_settings); + +private: + std::shared_ptr m_emu_settings; + color_wedge_widget* m_widget_reference = nullptr; + color_wedge_widget* m_widget = nullptr; + QTableWidget* m_matrix_left = nullptr; + QTableWidget* m_matrix_right = nullptr; + bool m_updating = false; + + QTableWidget* create_matrix_table(); + + void read_matrix(mat3f& mat, QTableWidget* table) const; + + void update_matrix(QTableWidget* table, const mat3f& matrix, bool is_custom); + + void update_matrices(); + + void apply_custom_matrix(); +}; diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 38fd5d2a94..f97ec058b0 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -254,8 +254,10 @@ bool emu_settings::ValidateSettings(bool cleanup) if (cfg_node) { - // Ignore every node in Log subsection - if (level == 0 && cfg_node->get_name() == "Log") + // Ignore every node in map subsections + if (cfg_node->get_type() == cfg::type::log || + cfg_node->get_type() == cfg::type::map || + cfg_node->get_type() == cfg::type::node_map) { continue; } @@ -1495,6 +1497,7 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case stereo_render_mode_options::anaglyph_magenta_cyan: return tr("Anaglyph Magenta-Cyan", "3D Display Mode"); case stereo_render_mode_options::anaglyph_trioscopic: return tr("Anaglyph Green-Magenta (Trioscopic)", "3D Display Mode"); case stereo_render_mode_options::anaglyph_amber_blue: return tr("Anaglyph Amber-Blue (ColorCode 3D)", "3D Display Mode"); + case stereo_render_mode_options::anaglyph_custom: return tr("Anaglyph Custom", "3D Display Mode"); } break; case emu_settings_type::MidiDevices: diff --git a/rpcs3/rpcs3qt/emu_settings_type.cpp b/rpcs3/rpcs3qt/emu_settings_type.cpp index 4b032c78c5..c59ebd8eb1 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.cpp +++ b/rpcs3/rpcs3qt/emu_settings_type.cpp @@ -143,6 +143,10 @@ const std::map settings_location = { emu_settings_type::ShaderLoadBgDarkening, get_cfg_location(local_cfg.video.shader_preloading_dialog.darkening_strength) }, { emu_settings_type::ShaderLoadBgBlur, get_cfg_location(local_cfg.video.shader_preloading_dialog.blur_strength) }, + // Anaglyph matrix + { emu_settings_type::CustomAnaglyphMatrixLeft, get_cfg_location(local_cfg.video.custom_anaglyph_matrices.left) }, + { emu_settings_type::CustomAnaglyphMatrixRight, get_cfg_location(local_cfg.video.custom_anaglyph_matrices.right) }, + // Audio { emu_settings_type::AudioRenderer, get_cfg_location(local_cfg.audio.renderer) }, { emu_settings_type::DumpToFile, get_cfg_location(local_cfg.audio.dump_to_file) }, diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index de04b81e85..5dca5efd0b 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -111,6 +111,10 @@ enum class emu_settings_type RecordWithOverlays, DisableHWTexelRemapping, + // Anaglyph Matrix + CustomAnaglyphMatrixLeft, + CustomAnaglyphMatrixRight, + // Performance Overlay PerfOverlayEnabled, PerfOverlayFramerateGraphEnabled, diff --git a/rpcs3/rpcs3qt/gl_gs_frame.h b/rpcs3/rpcs3qt/gl_gs_frame.h index d1129f8e3a..223ac3cfd1 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.h +++ b/rpcs3/rpcs3qt/gl_gs_frame.h @@ -6,8 +6,8 @@ struct GLContext { - QSurface *surface = nullptr; - QOpenGLContext *handle = nullptr; + QSurface* surface = nullptr; + QOpenGLContext* handle = nullptr; bool owner = false; }; @@ -15,7 +15,7 @@ class gl_gs_frame : public gs_frame { private: QSurfaceFormat m_format; - GLContext *m_primary_context = nullptr; + GLContext* m_primary_context = nullptr; public: explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 26e062b67b..8d6e00cc88 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -24,6 +24,7 @@ #include "render_creator.h" #include "microphone_creator.h" #include "log_level_dialog.h" +#include "anaglyph_settings_dialog.h" #include "Emu/NP/rpcn_countries.h" #include "Emu/GameInfo.h" @@ -45,7 +46,7 @@ LOG_CHANNEL(cfg_log, "CFG"); -std::pair get_data(const QComboBox* box, int index) +static std::pair get_data(const QComboBox* box, int index) { if (!box) return {}; @@ -57,7 +58,7 @@ std::pair get_data(const QComboBox* box, int index) return { var_list[0].toString(), var_list[1].toInt() }; } -int find_item(const QComboBox* box, int value) +static int find_item(const QComboBox* box, int value) { for (int i = 0; box && i < box->count(); i++) { @@ -70,7 +71,7 @@ int find_item(const QComboBox* box, int value) return -1; } -void remove_item(QComboBox* box, int data_value, int def_value) +static void remove_item(QComboBox* box, int data_value, int def_value) { if (!box) return; @@ -593,9 +594,15 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->stereoRenderMode->setEnabled(stereo_allowed && stereo_enabled); ui->stereoRenderEnabled->setEnabled(stereo_allowed); ui->gb_screen_size->setEnabled(stereo_allowed && stereo_enabled); + ui->gb_anaglyph_settings->setEnabled(stereo_allowed && stereo_enabled); }; connect(ui->resBox, &QComboBox::currentIndexChanged, this, [enable_3D_modes](int){ enable_3D_modes(); }); connect(ui->stereoRenderEnabled, &QCheckBox::checkStateChanged, this, [enable_3D_modes](Qt::CheckState){ enable_3D_modes(); }); + connect(ui->pb_anaglyph_settings, &QAbstractButton::clicked, [this]() + { + anaglyph_settings_dialog* dlg = new anaglyph_settings_dialog(this, m_emu_settings); + dlg->open(); + }); enable_3D_modes(); } else @@ -603,6 +610,7 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std ui->stereoRenderMode->setCurrentIndex(find_item(ui->stereoRenderMode, static_cast(g_cfg.video.stereo_render_mode.def))); ui->stereoRenderEnabled->setChecked(false); ui->gb_stereo->setEnabled(false); + ui->gb_anaglyph_settings->setEnabled(false); } // Checkboxes: main options diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 77b3f26593..95ed15313f 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -557,6 +557,22 @@ + + + + Anaglyph Settings + + + + + + Configure + + + + + +