Add anaglyph settings

This commit is contained in:
Megamouse 2026-04-08 01:12:10 +02:00
parent c102d1ec0d
commit 4312221498
29 changed files with 717 additions and 32 deletions

View File

@ -1,5 +1,6 @@
#pragma once
#include <array>
#include <cmath>
#include <type_traits>
@ -1027,3 +1028,6 @@ using color1u = color1_base<unsigned int>;
using color1i = color1_base<int>;
using color1f = color1_base<float>;
using color1d = color1_base<double>;
using mat3f = color3_base<float>[3];
static_assert(sizeof(mat3f) == sizeof(float) * 3 * 3);

160
Utilities/stereo_config.cpp Normal file
View File

@ -0,0 +1,160 @@
#include "stdafx.h"
#include "stereo_config.h"
#include "Emu/system_config.h"
stereo_config::stereo_matrices::stereo_matrices(const std::array<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r)
{
for (usz i = 0; i < 3; i++)
{
left[i] = l[i];
right[i] = r[i];
}
}
atomic_t<bool> stereo_config::s_live_preview_enabled = false;
stereo_config::stereo_matrices stereo_config::m_live_matrices = {};
const std::unordered_map<stereo_render_mode_options, stereo_config::stereo_matrices> stereo_config::m_matrices =
{
{stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_red_green, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
}
)},
{stereo_render_mode_options::anaglyph_red_blue, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0.5f)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0.5f)
}
)},
{stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices(
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 1.0f / 3.0f),
color3_base<f32>(0, 0, 1.0f / 3.0f),
color3_base<f32>(0, 0, 1.0f / 3.0f)
}
)},
};
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
{
if (m_in_emulation && s_live_preview_enabled)
{
return m_live_matrices;
}
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_left_only_matrices = stereo_matrices(
{
color3_base<float>(1, 0, 0),
color3_base<float>(0, 1, 0),
color3_base<float>(0, 0, 1)
},
{
color3_base<float>(0, 0, 0),
color3_base<float>(0, 0, 0),
color3_base<float>(0, 0, 0)
});
return s_left_only_matrices;
}
std::map<std::string, std::string> stereo_config::get_custom_matrix(bool is_left) const
{
return convert_matrix(is_left ? m_custom_matrices.left : m_custom_matrices.right);
}
std::map<std::string, std::string> stereo_config::convert_matrix(const mat3f& mat)
{
std::map<std::string, std::string> 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;
}

77
Utilities/stereo_config.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
#include "geometry.h"
#include "Emu/system_config_types.h"
#include "Utilities/StrUtil.h"
#include <map>
#include <mutex>
struct stereo_config
{
public:
struct stereo_matrices
{
stereo_matrices() = default;
stereo_matrices(const std::array<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r);
mat3f left {};
mat3f right {};
};
stereo_config(bool in_emulation) : m_in_emulation(in_emulation) {}
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; }
static void set_live_matrices(const stereo_matrices& matrices) { m_live_matrices = matrices; }
std::map<std::string, std::string> get_custom_matrix(bool is_left) const;
template <typename T>
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, -10.0f, 10.0f))
{
mat[i].rgb[j] = static_cast<f32>(val);
}
}
}
}
static std::map<std::string, std::string> convert_matrix(const mat3f& mat);
static atomic_t<bool> s_live_preview_enabled;
private:
stereo_matrices m_custom_matrices {};
stereo_render_mode_options m_stereo_mode = stereo_render_mode_options::disabled;
bool m_in_emulation = false;
static stereo_matrices m_live_matrices;
static const std::unordered_map<stereo_render_mode_options, stereo_matrices> m_matrices;
};

View File

@ -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)

View File

@ -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

View File

@ -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<GLuint>& source, f32 gamma, bool limited_rgb,
bool stereo_enabled, stereo_render_mode_options stereo_mode, gl::filter input_filter)
bool stereo_enabled, gl::filter input_filter)
{
if (m_input_filter != input_filter)
{
@ -519,10 +520,16 @@ namespace gl
m_sampler.set_parameteri(GL_TEXTURE_MAG_FILTER, static_cast<GLenum>(m_input_filter));
}
static stereo_config stereo_cfg = stereo_config(true);
stereo_cfg.update_from_config(stereo_enabled);
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<u8>(stereo_mode) : 0;
program_handle.uniforms["stereo_image_count"] = (source[1] == GL_NONE? 1 : 2);
program_handle.uniforms["stereo_display_mode"] = static_cast<u8>(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]);

View File

@ -98,7 +98,7 @@ namespace gl
video_out_calibration_pass();
void run(gl::command_context& cmd, const areau& viewport, const rsx::simple_array<GLuint>& source, f32 gamma, bool limited_rgb,
bool stereo_enabled, stereo_render_mode_options stereo_mode, gl::filter input_filter);
bool stereo_enabled, gl::filter input_filter);
};
struct rp_ssbo_to_generic_texture final : public overlay_pass

View File

@ -436,7 +436,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
}
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, avconfig.stereo_enabled, filter);
}
}

View File

@ -104,6 +104,7 @@ namespace gl
void operator = (const color4f& rhs) const { glProgramUniform4f(m_program.id(), location(), rhs.r, rhs.g, rhs.b, rhs.a); }
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 mat3f& rhs) const { glProgramUniformMatrix3fv(m_program.id(), location(), 1, GL_FALSE, &rhs[0].rgb[0]); }
void operator = (const std::span<const int>& rhs) const { glProgramUniform1iv(m_program.id(), location(), ::size32(rhs), rhs.data()); }
void operator = (const std::span<const handle64_t>& rhs) const { glProgramUniformHandleui64vARB(m_program.id(), location(), ::size32(rhs), rhs.data()); }
};

View File

@ -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)

View File

@ -14,6 +14,7 @@
#include "../Program/RSXOverlay.h"
#include "util/fnv_hash.hpp"
#include "Utilities/stereo_config.h"
#include "Emu/Cell/timers.hpp"
@ -911,18 +912,28 @@ 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<vk::viewable_image*>& src, f32 gamma, bool limited_rgb,
bool stereo_enabled, stereo_render_mode_options stereo_mode, VkRenderPass render_pass)
bool stereo_enabled, VkRenderPass render_pass)
{
static stereo_config stereo_cfg = stereo_config(true);
stereo_cfg.update_from_config(stereo_enabled);
const auto& matrices = stereo_cfg.matrices();
config.gamma = gamma;
config.limit_range = limited_rgb? 1 : 0;
config.stereo_display_mode = stereo_enabled ? static_cast<u8>(stereo_mode) : 0;
config.limit_range = limited_rgb ? 1 : 0;
config.stereo_display_mode = static_cast<u8>(stereo_cfg.stereo_mode());
config.stereo_image_count = std::min(::size32(src), 2u);
for (u32 i = 0; i < 3; i++)
{
std::memcpy(config.left_anaglyph_matrix[i].rgba, matrices.left[i].rgb, sizeof(matrices.left[i].rgb));
std::memcpy(config.right_anaglyph_matrix[i].rgba, matrices.right[i].rgb, sizeof(matrices.right[i].rgb));
}
std::vector<vk::image_view*> views;
views.reserve(2);

View File

@ -209,9 +209,18 @@ namespace vk
int limit_range;
int stereo_display_mode;
int stereo_image_count;
color4_base<float> left_anaglyph_matrix[3];
color4_base<float> right_anaglyph_matrix[3];
};
float data[4];
float data[(
sizeof(gamma) +
sizeof(limit_range) +
sizeof(stereo_display_mode) +
sizeof(stereo_image_count) +
sizeof(left_anaglyph_matrix) +
sizeof(right_anaglyph_matrix)
) / sizeof(float)];
}
config = {};
@ -223,7 +232,7 @@ namespace vk
void run(vk::command_buffer& cmd, const areau& viewport, vk::framebuffer* target,
const rsx::simple_array<vk::viewable_image*>& src, f32 gamma, bool limited_rgb,
bool stereo_enabled, stereo_render_mode_options stereo_mode, VkRenderPass render_pass);
bool stereo_enabled, VkRenderPass render_pass);
};
// TODO: Replace with a proper manager

View File

@ -839,7 +839,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
vk::get_overlay_pass<vk::video_out_calibration_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, avconfig.stereo_enabled, single_target_pass);
direct_fbo->release();
}

View File

@ -234,6 +234,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

View File

@ -681,6 +681,7 @@ void fmt_class_string<stereo_render_mode_options>::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;

View File

@ -361,6 +361,7 @@ enum class stereo_render_mode_options
anaglyph_magenta_cyan,
anaglyph_trioscopic,
anaglyph_amber_blue,
anaglyph_custom,
};
enum class xfloat_accuracy

View File

@ -59,6 +59,7 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\Utilities\stereo_config.cpp" />
<ClCompile Include="..\Utilities\cheat_info.cpp" />
<ClCompile Include="..\Utilities\date_time.cpp" />
<ClCompile Include="..\Utilities\stack_trace.cpp" />
@ -574,6 +575,7 @@
<ItemGroup>
<ClInclude Include="..\3rdparty\stblib\stb\stb_image.h" />
<ClInclude Include="..\Utilities\address_range.h" />
<ClInclude Include="..\Utilities\stereo_config.h" />
<ClInclude Include="..\Utilities\cheat_info.h" />
<ClInclude Include="..\Utilities\deferred_op.hpp" />
<ClInclude Include="..\Utilities\simple_ringbuf.h" />

View File

@ -1435,6 +1435,9 @@
<ClCompile Include="Loader\iso_validation.cpp">
<Filter>Loader</Filter>
</ClCompile>
<ClCompile Include="..\Utilities\stereo_config.cpp">
<Filter>Utilities</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@ -2881,6 +2884,9 @@
<ClInclude Include="Loader\iso_validation.h">
<Filter>Loader</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\stereo_config.h">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">

View File

@ -838,6 +838,7 @@
</ClCompile>
<ClCompile Include="rpcs3.cpp" />
<ClCompile Include="rpcs3qt\about_dialog.cpp" />
<ClCompile Include="rpcs3qt\anaglyph_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\basic_mouse_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\breakpoint_handler.cpp" />
<ClCompile Include="rpcs3qt\breakpoint_list.cpp" />
@ -1197,6 +1198,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(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"</Command>
</CustomBuild>
<ClInclude Include="rpcs3.h" />
<ClInclude Include="rpcs3qt\anaglyph_settings_dialog.h" />
<ClInclude Include="rpcs3qt\breakpoint_handler.h" />
<CustomBuild Include="rpcs3qt\breakpoint_list.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -1311,6 +1311,9 @@
<ClCompile Include="rpcs3qt\iso_integrity.cpp">
<Filter>Gui\game list</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\anaglyph_settings_dialog.cpp">
<Filter>Gui\settings</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1562,6 +1565,9 @@
<ClInclude Include="rpcs3qt\steam_utils.h">
<Filter>Gui\utils</Filter>
</ClInclude>
<ClInclude Include="rpcs3qt\anaglyph_settings_dialog.h">
<Filter>Gui\settings</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">

View File

@ -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

View File

@ -0,0 +1,293 @@
#include "stdafx.h"
#include "anaglyph_settings_dialog.h"
#include <QGroupBox>
#include <QHBoxLayout>
#include <QPainter>
#include <QPushButton>
color_wedge_widget::color_wedge_widget(QWidget* parent)
: QWidget(parent)
{
setObjectName("color_wedge_widget");
setMinimumSize(300, 300);
}
color_wedge_widget::~color_wedge_widget()
{
stereo_config::s_live_preview_enabled = false;
}
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[1].x * left.y() + ml[2].x * left.z() +
mr[0].x * right.x() + mr[1].x * right.y() + mr[2].x * right.z());
out.setY(ml[0].y * left.x() + ml[1].y * left.y() + ml[2].y * left.z() +
mr[0].y * right.x() + mr[1].y * right.y() + mr[2].y * right.z());
out.setZ(ml[0].z * left.x() + ml[1].z * left.y() + ml[2].z * left.z() +
mr[0].z * right.x() + mr[1].z * 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> 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<stereo_render_mode_options>::format, m_emu_settings->GetSetting(emu_settings_type::StereoRenderMode)))
{
const auto mode = static_cast<stereo_render_mode_options>(static_cast<std::underlying_type_t<stereo_render_mode_options>>(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* apply_button = new QPushButton(tr("Apply Custom Matrices"));
connect(apply_button, &QAbstractButton::clicked, this, [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));
});
apply_button->setEnabled(current_mode == stereo_render_mode_options::anaglyph_custom);
QCheckBox* live_preview_cb = new QCheckBox(tr("Live Preview"));
connect(live_preview_cb, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state)
{
stereo_config::s_live_preview_enabled = state == Qt::CheckState::Checked;
});
QComboBox* combo = new QComboBox(this);
combo->addItem(tr("Disabled"), static_cast<int>(stereo_render_mode_options::disabled));
combo->addItem(tr("Red-Green"), static_cast<int>(stereo_render_mode_options::anaglyph_red_green));
combo->addItem(tr("Red-Blue"), static_cast<int>(stereo_render_mode_options::anaglyph_red_blue));
combo->addItem(tr("Red-Cyan"), static_cast<int>(stereo_render_mode_options::anaglyph_red_cyan));
combo->addItem(tr("Magenta-Cyan"), static_cast<int>(stereo_render_mode_options::anaglyph_magenta_cyan));
combo->addItem(tr("Trioscopic"), static_cast<int>(stereo_render_mode_options::anaglyph_trioscopic));
combo->addItem(tr("Amber-Blue"), static_cast<int>(stereo_render_mode_options::anaglyph_amber_blue));
combo->addItem(tr("Custom"), static_cast<int>(stereo_render_mode_options::anaglyph_custom));
combo->setCurrentIndex(combo->findData(static_cast<int>(current_mode)));
connect(combo, &QComboBox::currentIndexChanged, this, [this, combo, apply_button](int /*index*/)
{
apply_button->setEnabled(static_cast<stereo_render_mode_options>(combo->currentData().toInt()) == stereo_render_mode_options::anaglyph_custom);
m_widget->set_option(static_cast<stereo_render_mode_options>(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(apply_button);
lay_h_1->addWidget(live_preview_cb);
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 in"), tr("G in"), tr("B in")});
table->setHorizontalHeaderLabels({tr("R out"), tr("G out"), tr("B out")});
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& matrix, QTableWidget* table) const
{
if (!table) return;
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);
matrix[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();
stereo_config::set_live_matrices(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);
stereo_config::set_live_matrices(m);
}

View File

@ -0,0 +1,54 @@
#pragma once
#include "emu_settings.h"
#include "Utilities/stereo_config.h"
#include <QDialog>
#include <QTableWidget>
#include <QVector3D>
class color_wedge_widget : public QWidget
{
public:
color_wedge_widget(QWidget* parent = nullptr);
virtual ~color_wedge_widget();
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 = stereo_config(false);
QImage m_img;
};
class anaglyph_settings_dialog : public QDialog
{
public:
anaglyph_settings_dialog(QWidget* parent, std::shared_ptr<emu_settings> emu_settings);
private:
std::shared_ptr<emu_settings> 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& matrix, QTableWidget* table) const;
void update_matrix(QTableWidget* table, const mat3f& matrix, bool is_custom);
void update_matrices();
void apply_custom_matrix();
};

View File

@ -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;
}
@ -1503,6 +1505,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:

View File

@ -144,6 +144,10 @@ const std::map<emu_settings_type, cfg_location> 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) },

View File

@ -112,6 +112,10 @@ enum class emu_settings_type
RecordWithOverlays,
DisableHWTexelRemapping,
// Anaglyph Matrix
CustomAnaglyphMatrixLeft,
CustomAnaglyphMatrixRight,
// Performance Overlay
PerfOverlayEnabled,
PerfOverlayFramerateGraphEnabled,

View File

@ -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> gui_settings, bool force_fullscreen);

View File

@ -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<QString, int> get_data(const QComboBox* box, int index)
static std::pair<QString, int> get_data(const QComboBox* box, int index)
{
if (!box) return {};
@ -57,7 +58,7 @@ std::pair<QString, int> 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> 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> gui_settings, std
ui->stereoRenderMode->setCurrentIndex(find_item(ui->stereoRenderMode, static_cast<int>(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

View File

@ -557,6 +557,22 @@
<item>
<widget class="QComboBox" name="stereoRenderMode"/>
</item>
<item>
<widget class="QGroupBox" name="gb_anaglyph_settings">
<property name="title">
<string>Anaglyph Settings</string>
</property>
<layout class="QHBoxLayout" name="layout_gb_anaglyph_settings">
<item>
<widget class="QPushButton" name="pb_anaglyph_settings">
<property name="text">
<string>Configure</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_screen_size">
<property name="title">