Compare commits

...

17 Commits

Author SHA1 Message Date
vperus 11f4bf8a9a [input_common] Move variable declaration closer to usage
MSVC supplied with VS2022 generates "warning C4189: 'CALIBRATION_THRESHOLD':
local variable is initialized but not referenced" which is treated as an
error.

Circumvent it by moving constexpr variable directly into body of lambda function.
2021-12-17 20:51:47 +02:00
vperus 660c6bec22 Revert of b01aa72
Caused worker_thread to be stuck in Stage1Completed state until job's destruction.
2021-11-29 16:37:11 +02:00
vperus 04fa990b0c [input_common] Add completion test for CalibrationConfigurationJob 2021-11-29 16:33:12 +02:00
Fernando S 3031223153 Merge pull request #7396 from FernandoS27/blit-this-mf
TextureCache: Eliminate format deduction as full depth conversion has been supported.
2021-11-28 12:16:31 +01:00
Fernando Sahmkow 5a3463bc2b Texture Cache: Secure insertions against deletions. 2021-11-28 10:53:27 +01:00
bunnei 11a9bff36d Merge pull request #7438 from german77/homebrew2
Core: Stub services and functions needed for checkpoint
2021-11-28 00:54:48 -08:00
bunnei be56587ad7 Merge pull request #7451 from german77/debug_controller
settings: Add debug setting to enable all controllers
2021-11-28 00:54:29 -08:00
german77 51df96b7c0 settings: Add debug setting to enable all controllers 2021-11-27 20:24:37 -06:00
Fernando Sahmkow ecefc932e6 Texture Cache: Redesigning the blitting system (again). 2021-11-27 11:22:16 +01:00
Fernando Sahmkow 1624f307d0 Texture Cache: Further fix regressions. 2021-11-26 17:03:48 +01:00
Fernando Sahmkow 08674aee87 Texture Cache: Fix issue with blitting 3D textures. 2021-11-22 06:07:21 +01:00
Fernando Sahmkow 1e474fb9d1 Texture Cache: Correct conversion shaders. 2021-11-22 00:21:42 +01:00
Fernando Sahmkow 0902119302 Texture Cache: Always copy on NVIDIA. 2021-11-22 00:06:56 +01:00
Fernando Sahmkow 8532849439 TextureCache: Simplify blitting of D24S8 formats and fix bugs. 2021-11-22 00:00:01 +01:00
Fernando Sahmkow d7f4434bd5 VulkanTexturECache: Use reinterpret on D32_S8 formats. 2021-11-21 21:09:49 +01:00
Fernando Sahmkow b96caf200d HostShaders: Fix D24S8 convertion shaders. 2021-11-21 21:04:04 +01:00
Fernando Sahmkow 779f4ac72d TextureCache: Eliminate format deduction as full depth conversion has been supported. 2021-11-21 05:37:01 +01:00
26 changed files with 431 additions and 265 deletions
+1
View File
@@ -604,6 +604,7 @@ struct Values {
BasicSetting<bool> extended_logging{false, "extended_logging"};
BasicSetting<bool> use_debug_asserts{false, "use_debug_asserts"};
BasicSetting<bool> use_auto_stub{false, "use_auto_stub"};
BasicSetting<bool> enable_all_controllers{false, "enable_all_controllers"};
// Miscellaneous
BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
+5
View File
@@ -370,6 +370,11 @@ enum class ControllerType {
RightJoycon,
Handheld,
GameCube,
Pokeball,
NES,
SNES,
N64,
SegaGenesis,
};
struct PlayerInput {
+20
View File
@@ -27,6 +27,16 @@ NpadStyleIndex EmulatedController::MapSettingsTypeToNPad(Settings::ControllerTyp
return NpadStyleIndex::Handheld;
case Settings::ControllerType::GameCube:
return NpadStyleIndex::GameCube;
case Settings::ControllerType::Pokeball:
return NpadStyleIndex::Pokeball;
case Settings::ControllerType::NES:
return NpadStyleIndex::NES;
case Settings::ControllerType::SNES:
return NpadStyleIndex::SNES;
case Settings::ControllerType::N64:
return NpadStyleIndex::N64;
case Settings::ControllerType::SegaGenesis:
return NpadStyleIndex::SegaGenesis;
default:
return NpadStyleIndex::ProController;
}
@@ -46,6 +56,16 @@ Settings::ControllerType EmulatedController::MapNPadToSettingsType(NpadStyleInde
return Settings::ControllerType::Handheld;
case NpadStyleIndex::GameCube:
return Settings::ControllerType::GameCube;
case NpadStyleIndex::Pokeball:
return Settings::ControllerType::Pokeball;
case NpadStyleIndex::NES:
return Settings::ControllerType::NES;
case NpadStyleIndex::SNES:
return Settings::ControllerType::SNES;
case NpadStyleIndex::N64:
return Settings::ControllerType::N64;
case NpadStyleIndex::SegaGenesis:
return Settings::ControllerType::SegaGenesis;
default:
return Settings::ControllerType::ProController;
}
@@ -263,6 +263,10 @@ void Controller_NPad::OnInit() {
style.fullkey.Assign(1);
style.gamecube.Assign(1);
style.palma.Assign(1);
style.lark.Assign(1);
style.lucia.Assign(1);
style.lagoon.Assign(1);
style.lager.Assign(1);
hid_core.SetSupportedStyleTag(style);
}
+37 -33
View File
@@ -536,42 +536,46 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
std::function<void(u16, u16, u16, u16)> data_callback) {
std::thread([=, this] {
u16 min_x{UINT16_MAX};
u16 min_y{UINT16_MAX};
u16 max_x{};
u16 max_y{};
Status current_status{Status::Initialized};
SocketCallback callback{
[](Response::Version) {}, [](Response::PortInfo) {},
[&](Response::PadData data) {
static constexpr u16 CALIBRATION_THRESHOLD = 100;
static constexpr u16 MAX_VALUE = UINT16_MAX;
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
[&](Response::PadData data) {
constexpr u16 CALIBRATION_THRESHOLD = 100;
if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now
current_status = Status::Ready;
status_callback(current_status);
}
const auto& touchpad_0 = data.touch[0];
if (touchpad_0.is_active == 0) {
return;
}
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y)
current_status = Status::Stage1Completed;
status_callback(current_status);
}
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
// Set the current position as max value and finishes configuration
const u16 max_x = touchpad_0.x;
const u16 max_y = touchpad_0.y;
current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status);
if (current_status == Status::Initialized) {
// Receiving data means the communication is ready now
current_status = Status::Ready;
status_callback(current_status);
}
if (data.touch[0].is_active == 0) {
return;
}
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
data.touch[0].y);
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
if (current_status == Status::Ready) {
// First touch - min data (min_x/min_y)
current_status = Status::Stage1Completed;
status_callback(current_status);
}
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
// Set the current position as max value and finishes
// configuration
max_x = data.touch[0].x;
max_y = data.touch[0].y;
current_status = Status::Completed;
data_callback(min_x, min_y, max_x, max_y);
status_callback(current_status);
complete_event.Set();
}
}};
complete_event.Set();
}
}};
Socket socket{host, port, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
complete_event.Wait();
+13 -8
View File
@@ -54,6 +54,18 @@ struct Message {
template <typename T>
constexpr Type GetMessageType();
template <typename T>
Message<T> CreateMessage(const u32 magic, const T data, const u32 sender_id) {
boost::crc_32_type crc;
Header header{
magic, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, sender_id, GetMessageType<T>(),
};
Message<T> message{header, data};
crc.process_bytes(&message, sizeof(Message<T>));
message.header.crc = crc.checksum();
return message;
}
namespace Request {
enum RegisterFlags : u8 {
@@ -101,14 +113,7 @@ static_assert(std::is_trivially_copyable_v<PadData>,
*/
template <typename T>
Message<T> Create(const T data, const u32 client_id = 0) {
boost::crc_32_type crc;
Header header{
CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(),
};
Message<T> message{header, data};
crc.process_bytes(&message, sizeof(Message<T>));
message.header.crc = crc.checksum();
return message;
return CreateMessage(CLIENT_MAGIC, data, client_id);
}
} // namespace Request
+2 -1
View File
@@ -10,11 +10,12 @@ add_executable(tests
core/network/network.cpp
tests.cpp
video_core/buffer_base.cpp
input_common/calibration_configuration_job.cpp
)
create_target_directory_groups(tests)
target_link_libraries(tests PRIVATE common core)
target_link_libraries(tests PRIVATE common core input_common)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
add_test(NAME tests COMMAND tests)
@@ -0,0 +1,136 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/crc.hpp>
#include <catch2/catch.hpp>
#include "input_common/drivers/udp_client.h"
#include "input_common/helpers/udp_protocol.h"
class FakeCemuhookServer {
public:
FakeCemuhookServer()
: socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {}
~FakeCemuhookServer() {
is_running = false;
boost::system::error_code error_code;
socket.shutdown(boost::asio::socket_base::shutdown_both, error_code);
socket.close();
if (handler.joinable()) {
handler.join();
}
}
u16 GetPort() {
return socket.local_endpoint().port();
}
std::string GetHost() {
return socket.local_endpoint().address().to_string();
}
void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) {
constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header);
constexpr size_t PadDataSize =
sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>);
REQUIRE(touch_movement_path.size() > 0);
is_running = true;
handler = std::thread([touch_movement_path, this]() {
auto current_touch_position = touch_movement_path.begin();
while (is_running) {
boost::asio::ip::udp::endpoint sender_endpoint;
boost::system::error_code error_code;
auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer),
sender_endpoint, 0, error_code);
if (received_size < HeaderSize) {
continue;
}
InputCommon::CemuhookUDP::Header header{};
std::memcpy(&header, receive_buffer.data(), HeaderSize);
switch (header.type) {
case InputCommon::CemuhookUDP::Type::PadData: {
InputCommon::CemuhookUDP::Response::PadData pad_data{};
pad_data.touch[0] = *current_touch_position;
const auto pad_message = InputCommon::CemuhookUDP::CreateMessage(
InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0);
std::memcpy(send_buffer.data(), &pad_message, PadDataSize);
socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint,
0, error_code);
bool can_advance =
std::next(current_touch_position) != touch_movement_path.end();
if (can_advance) {
std::advance(current_touch_position, 1);
}
break;
}
case InputCommon::CemuhookUDP::Type::PortInfo:
case InputCommon::CemuhookUDP::Type::Version:
default:
break;
}
}
});
}
private:
boost::asio::io_service io_service;
boost::asio::ip::udp::socket socket;
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer;
std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer;
bool is_running = false;
std::thread handler;
};
TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
Common::Event complete_event;
FakeCemuhookServer server;
server.Run({{
.is_active = 1,
.x = 0,
.y = 0,
},
{
.is_active = 1,
.x = 200,
.y = 200,
}});
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{};
u16 min_x{};
u16 min_y{};
u16 max_x{};
u16 max_y{};
InputCommon::CemuhookUDP::CalibrationConfigurationJob job(
server.GetHost(), server.GetPort(),
[&status,
&complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) {
status = status_;
if (status ==
InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) {
complete_event.Set();
}
},
[&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
min_x = min_x_;
min_y = min_y_;
max_x = max_x_;
max_y = max_y_;
});
complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10));
REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed);
REQUIRE(min_x == 0);
REQUIRE(min_y == 0);
REQUIRE(max_x == 200);
REQUIRE(max_y == 200);
}
@@ -11,13 +11,9 @@ set(SHADER_FILES
block_linear_unswizzle_2d.comp
block_linear_unswizzle_3d.comp
convert_abgr8_to_d24s8.frag
convert_b10g11r11_to_d24s8.frag
convert_d24s8_to_abgr8.frag
convert_d24s8_to_b10g11r11.frag
convert_d24s8_to_r16g16.frag
convert_depth_to_float.frag
convert_float_to_depth.frag
convert_r16g16_to_d24s8.frag
full_screen_triangle.vert
fxaa.frag
fxaa.vert
@@ -9,9 +9,10 @@ layout(binding = 0) uniform sampler2D color_texture;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
uvec4 color = uvec4(texelFetch(color_texture, coord, 0).rgba * (exp2(8) - 1.0f));
uint depth_unorm = (color.r << 16) | (color.g << 8) | color.b;
uvec4 color = uvec4(texelFetch(color_texture, coord, 0).abgr * (exp2(8) - 1.0f));
uvec4 bytes = color << uvec4(24, 16, 8, 0);
uint depth_stencil_unorm = bytes.x | bytes.y | bytes.z | bytes.w;
gl_FragDepth = float(depth_unorm) / (exp2(24.0) - 1.0f);
gl_FragStencilRefARB = int(color.a);
gl_FragDepth = float(depth_stencil_unorm & 0x00FFFFFFu) / (exp2(24.0) - 1.0f);
gl_FragStencilRefARB = int(depth_stencil_unorm >> 24);
}
@@ -1,19 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D color_texture;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
vec4 color = texelFetch(color_texture, coord, 0).rgba;
uint depth_stencil_unorm = (uint(color.b * (exp2(10) - 1.0f)) << 22)
| (uint(color.g * (exp2(11) - 1.0f)) << 11)
| (uint(color.r * (exp2(11) - 1.0f)));
gl_FragDepth = float(depth_stencil_unorm >> 8) / (exp2(24.0) - 1.0f);
gl_FragStencilRefARB = int(depth_stencil_unorm & 0x00FF);
}
@@ -14,8 +14,10 @@ void main() {
uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
color.r = float(depth >> 16) / (exp2(8) - 1.0);
color.g = float((depth >> 8) & 0x00FF) / (exp2(8) - 1.0);
color.b = float(depth & 0x00FF) / (exp2(8) - 1.0);
color.a = float(stencil) / (exp2(8) - 1.0);
highp uint depth_val =
uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
highp uvec4 components =
uvec4(stencil_val, (uvec3(depth_val) >> uvec3(24u, 16u, 8u)) & 0x000000FFu);
color.abgr = vec4(components) / (exp2(8.0) - 1.0);
}
@@ -1,21 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450
layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform isampler2D stencil_tex;
layout(location = 0) out vec4 color;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
color.b = float(depth >> 22) / (exp2(10) - 1.0);
color.g = float((depth >> 11) & 0x00FF) / (exp2(11) - 1.0);
color.r = float(depth & 0x00FF) / (exp2(11) - 1.0);
color.a = 1.0f;
}
@@ -1,21 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450
layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform isampler2D stencil_tex;
layout(location = 0) out vec4 color;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
color.r = float(depth >> 16) / (exp2(16) - 1.0);
color.g = float((depth >> 16) & 0x00FF) / (exp2(16) - 1.0);
color.b = 0.0f;
color.a = 1.0f;
}
@@ -1,18 +0,0 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#version 450
#extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D color_texture;
void main() {
ivec2 coord = ivec2(gl_FragCoord.xy);
vec4 color = texelFetch(color_texture, coord, 0).rgba;
uint depth_stencil_unorm = (uint(color.r * (exp2(16) - 1.0f)) << 16)
| (uint(color.g * (exp2(16) - 1.0f)) << 16);
gl_FragDepth = float(depth_stencil_unorm >> 8) / (exp2(24.0) - 1.0f);
gl_FragStencilRefARB = int(depth_stencil_unorm & 0x00FF);
}
+50 -48
View File
@@ -5,13 +5,9 @@
#include <algorithm>
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_b10g11r11_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_b10g11r11_frag_spv.h"
#include "video_core/host_shaders/convert_d24s8_to_r16g16_frag_spv.h"
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
#include "video_core/host_shaders/convert_r16g16_to_d24s8_frag_spv.h"
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
#include "video_core/host_shaders/vulkan_blit_color_float_frag_spv.h"
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
@@ -361,11 +357,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, VKScheduler& scheduler_,
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
convert_b10g11r11_to_d24s8_frag(BuildShader(device, CONVERT_B10G11R11_TO_D24S8_FRAG_SPV)),
convert_r16g16_to_d24s8_frag(BuildShader(device, CONVERT_R16G16_TO_D24S8_FRAG_SPV)),
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
convert_d24s8_to_b10g11r11_frag(BuildShader(device, CONVERT_D24S8_TO_B10G11R11_FRAG_SPV)),
convert_d24s8_to_r16g16_frag(BuildShader(device, CONVERT_D24S8_TO_R16G16_FRAG_SPV)),
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {
if (device.IsExtShaderStencilExportSupported()) {
@@ -461,30 +453,11 @@ void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
}
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
convert_abgr8_to_d24s8_frag, true);
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
}
void BlitImageHelper::ConvertB10G11R11ToD24S8(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
ConvertPipelineDepthTargetEx(convert_b10g11r11_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
convert_b10g11r11_to_d24s8_frag, true);
Convert(*convert_b10g11r11_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
}
void BlitImageHelper::ConvertR16G16ToD24S8(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
ConvertPipelineDepthTargetEx(convert_r16g16_to_d24s8_pipeline, dst_framebuffer->RenderPass(),
convert_r16g16_to_d24s8_frag, true);
Convert(*convert_r16g16_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
ConvertColor(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view, up_scale,
down_shift);
}
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
@@ -495,24 +468,6 @@ void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
down_shift);
}
void BlitImageHelper::ConvertD24S8ToB10G11R11(const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
ConvertPipelineColorTargetEx(convert_d24s8_to_b10g11r11_pipeline, dst_framebuffer->RenderPass(),
convert_d24s8_to_b10g11r11_frag, false);
ConvertDepthStencil(*convert_d24s8_to_b10g11r11_pipeline, dst_framebuffer, src_image_view,
up_scale, down_shift);
}
void BlitImageHelper::ConvertD24S8ToR16G16(const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale,
u32 down_shift) {
ConvertPipelineColorTargetEx(convert_d24s8_to_r16g16_pipeline, dst_framebuffer->RenderPass(),
convert_d24s8_to_r16g16_frag, false);
ConvertDepthStencil(*convert_d24s8_to_r16g16_pipeline, dst_framebuffer, src_image_view,
up_scale, down_shift);
}
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
@@ -560,6 +515,53 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
scheduler.InvalidateState();
}
void BlitImageHelper::ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const VkPipelineLayout layout = *one_texture_pipeline_layout;
const VkImageView src_view = src_image_view.ColorView();
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent{
.width = std::max((src_image_view.size.width * up_scale) >> down_shift, 1U),
.height = std::max((src_image_view.size.height * up_scale) >> down_shift, 1U),
};
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_view, extent, up_scale, down_shift,
this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
.x = 0,
.y = 0,
};
const VkViewport viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(extent.width),
.height = static_cast<float>(extent.height),
.minDepth = 0.0f,
.maxDepth = 0.0f,
};
const VkRect2D scissor{
.offset = offset,
.extent = extent,
};
const PushConstants push_constants{
.tex_scale = {viewport.width, viewport.height},
.tex_offset = {0.0f, 0.0f},
};
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
// TODO: Barriers
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
nullptr);
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT, push_constants);
cmdbuf.Draw(3, 1, 0, 0);
});
scheduler.InvalidateState();
}
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift) {
const VkPipelineLayout layout = *two_textures_pipeline_layout;
+4 -21
View File
@@ -56,28 +56,19 @@ public:
void ConvertR16ToD16(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertB10G11R11ToD24S8(const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale, u32 down_shift);
void ConvertR16G16ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD24S8ToB10G11R11(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
void ConvertD24S8ToR16G16(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
u32 up_scale, u32 down_shift);
private:
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
const ImageView& src_image_view, u32 up_scale, u32 down_shift);
void ConvertColor(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift);
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
ImageView& src_image_view, u32 up_scale, u32 down_shift);
@@ -114,11 +105,7 @@ private:
vk::ShaderModule convert_depth_to_float_frag;
vk::ShaderModule convert_float_to_depth_frag;
vk::ShaderModule convert_abgr8_to_d24s8_frag;
vk::ShaderModule convert_b10g11r11_to_d24s8_frag;
vk::ShaderModule convert_r16g16_to_d24s8_frag;
vk::ShaderModule convert_d24s8_to_abgr8_frag;
vk::ShaderModule convert_d24s8_to_b10g11r11_frag;
vk::ShaderModule convert_d24s8_to_r16g16_frag;
vk::Sampler linear_sampler;
vk::Sampler nearest_sampler;
@@ -131,11 +118,7 @@ private:
vk::Pipeline convert_d16_to_r16_pipeline;
vk::Pipeline convert_r16_to_d16_pipeline;
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
vk::Pipeline convert_b10g11r11_to_d24s8_pipeline;
vk::Pipeline convert_r16g16_to_d24s8_pipeline;
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
vk::Pipeline convert_d24s8_to_b10g11r11_pipeline;
vk::Pipeline convert_d24s8_to_r16g16_pipeline;
};
} // namespace Vulkan
@@ -775,8 +775,18 @@ StagingBufferRef TextureCacheRuntime::DownloadStagingBuffer(size_t size) {
bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) {
if (VideoCore::Surface::GetFormatType(dst.info.format) ==
VideoCore::Surface::SurfaceType::DepthStencil) {
return !device.IsExtShaderStencilExportSupported();
VideoCore::Surface::SurfaceType::DepthStencil &&
!device.IsExtShaderStencilExportSupported()) {
return true;
}
if (VideoCore::Surface::GetFormatType(src.info.format) ==
VideoCore::Surface::SurfaceType::DepthStencil &&
!device.IsExtShaderStencilExportSupported()) {
return true;
}
if (dst.info.format == PixelFormat::D32_FLOAT_S8_UINT ||
src.info.format == PixelFormat::D32_FLOAT_S8_UINT) {
return true;
}
return false;
}
@@ -1058,21 +1068,10 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
}
break;
case PixelFormat::A8B8G8R8_UNORM:
case PixelFormat::B8G8R8A8_UNORM:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view, up_scale, down_shift);
}
break;
case PixelFormat::B10G11R11_FLOAT:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToB10G11R11(dst, src_view, up_scale, down_shift);
}
break;
case PixelFormat::R16G16_UNORM:
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
return blit_image_helper.ConvertD24S8ToR16G16(dst, src_view, up_scale, down_shift);
}
break;
case PixelFormat::R32_FLOAT:
if (src_view.format == PixelFormat::D32_FLOAT) {
return blit_image_helper.ConvertD32ToR32(dst, src_view, up_scale, down_shift);
@@ -1084,16 +1083,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
}
break;
case PixelFormat::S8_UINT_D24_UNORM:
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
src_view.format == PixelFormat::B8G8R8A8_UNORM) {
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
}
if (src_view.format == PixelFormat::B10G11R11_FLOAT) {
return blit_image_helper.ConvertB10G11R11ToD24S8(dst, src_view, up_scale, down_shift);
}
if (src_view.format == PixelFormat::R16G16_UNORM) {
return blit_image_helper.ConvertR16G16ToD24S8(dst, src_view, up_scale, down_shift);
}
return blit_image_helper.ConvertABGR8ToD24S8(dst, src_view, up_scale, down_shift);
break;
case PixelFormat::D32_FLOAT:
if (src_view.format == PixelFormat::R32_FLOAT) {
@@ -1590,6 +1580,14 @@ VkImageView ImageView::StencilView() {
return *stencil_view;
}
VkImageView ImageView::ColorView() {
if (color_view) {
return *color_view;
}
color_view = MakeView(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
return *color_view;
}
VkImageView ImageView::StorageView(Shader::TextureType texture_type,
Shader::ImageFormat image_format) {
if (image_format == Shader::ImageFormat::Typeless) {
@@ -184,6 +184,8 @@ public:
[[nodiscard]] VkImageView StencilView();
[[nodiscard]] VkImageView ColorView();
[[nodiscard]] VkImageView StorageView(Shader::TextureType texture_type,
Shader::ImageFormat image_format);
@@ -224,6 +226,7 @@ private:
std::unique_ptr<StorageViews> storage_views;
vk::ImageView depth_view;
vk::ImageView stencil_view;
vk::ImageView color_view;
VkImage image_handle = VK_NULL_HANDLE;
VkImageView render_target = VK_NULL_HANDLE;
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
+74 -22
View File
@@ -472,7 +472,7 @@ template <class P>
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) {
const BlitImages images = GetBlitImages(dst, src);
const BlitImages images = GetBlitImages(dst, src, copy);
const ImageId dst_id = images.dst_id;
const ImageId src_id = images.src_id;
@@ -762,12 +762,15 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
const bool broken_views =
runtime.HasBrokenTextureViewFormats() || True(options & RelaxedOptions::ForceBrokenViews);
const bool native_bgr = runtime.HasNativeBgr();
ImageId image_id;
const bool flexible_formats = True(options & RelaxedOptions::Format);
ImageId image_id{};
boost::container::small_vector<ImageId, 1> image_ids;
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
if (True(existing_image.flags & ImageFlagBits::Remapped)) {
return false;
}
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear)
[[unlikely]] {
const bool strict_size = False(options & RelaxedOptions::Size) &&
True(existing_image.flags & ImageFlagBits::Strong);
const ImageInfo& existing = existing_image.info;
@@ -776,17 +779,27 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
IsPitchLinearSameSize(existing, info, strict_size) &&
IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
image_id = existing_image_id;
return true;
image_ids.push_back(existing_image_id);
return !flexible_formats && existing.format == info.format;
}
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
native_bgr)) {
image_id = existing_image_id;
return true;
image_ids.push_back(existing_image_id);
return !flexible_formats && existing_image.info.format == info.format;
}
return false;
};
ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda);
return image_id;
if (image_ids.size() <= 1) [[likely]] {
return image_id;
}
auto image_ids_compare = [this](ImageId a, ImageId b) {
auto& image_a = slot_images[a];
auto& image_b = slot_images[b];
return image_a.modification_tick < image_b.modification_tick;
};
return *std::ranges::max_element(image_ids, image_ids_compare);
}
template <class P>
@@ -1078,32 +1091,58 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
template <class P>
typename TextureCache<P>::BlitImages TextureCache<P>::GetBlitImages(
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src) {
static constexpr auto FIND_OPTIONS = RelaxedOptions::Format | RelaxedOptions::Samples;
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy) {
static constexpr auto FIND_OPTIONS = RelaxedOptions::Samples;
const GPUVAddr dst_addr = dst.Address();
const GPUVAddr src_addr = src.Address();
ImageInfo dst_info(dst);
ImageInfo src_info(src);
const bool can_be_depth_blit =
dst_info.format == src_info.format && copy.filter == Tegra::Engines::Fermi2D::Filter::Point;
ImageId dst_id;
ImageId src_id;
RelaxedOptions try_options = FIND_OPTIONS;
if (can_be_depth_blit) {
try_options |= RelaxedOptions::Format;
}
do {
has_deleted_images = false;
dst_id = FindImage(dst_info, dst_addr, FIND_OPTIONS);
src_id = FindImage(src_info, src_addr, FIND_OPTIONS);
const ImageBase* const dst_image = dst_id ? &slot_images[dst_id] : nullptr;
src_id = FindImage(src_info, src_addr, try_options);
dst_id = FindImage(dst_info, dst_addr, try_options);
const ImageBase* const src_image = src_id ? &slot_images[src_id] : nullptr;
DeduceBlitImages(dst_info, src_info, dst_image, src_image);
if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
continue;
if (src_image && src_image->info.num_samples > 1) {
RelaxedOptions find_options{FIND_OPTIONS | RelaxedOptions::ForceBrokenViews};
src_id = FindOrInsertImage(src_info, src_addr, find_options);
dst_id = FindOrInsertImage(dst_info, dst_addr, find_options);
if (has_deleted_images) {
continue;
}
break;
}
RelaxedOptions find_options{};
if (src_info.num_samples > 1) {
// it's a resolve, we must enforce the same format.
find_options = RelaxedOptions::ForceBrokenViews;
if (can_be_depth_blit) {
const ImageBase* const dst_image = src_id ? &slot_images[src_id] : nullptr;
DeduceBlitImages(dst_info, src_info, dst_image, src_image);
if (GetFormatType(dst_info.format) != GetFormatType(src_info.format)) {
continue;
}
}
if (!src_id) {
src_id = InsertImage(src_info, src_addr, RelaxedOptions{});
}
if (!dst_id) {
dst_id = InsertImage(dst_info, dst_addr, RelaxedOptions{});
}
src_id = FindOrInsertImage(src_info, src_addr, find_options);
dst_id = FindOrInsertImage(dst_info, dst_addr, find_options);
} while (has_deleted_images);
if (GetFormatType(dst_info.format) != SurfaceType::ColorTexture) {
// Make sure the images are depth and/or stencil textures.
do {
has_deleted_images = false;
src_id = FindOrInsertImage(src_info, src_addr, RelaxedOptions{});
dst_id = FindOrInsertImage(dst_info, dst_addr, RelaxedOptions{});
} while (has_deleted_images);
}
return BlitImages{
.dst_id = dst_id,
.src_id = src_id,
@@ -1160,7 +1199,14 @@ template <class P>
ImageViewId TextureCache<P>::FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
bool is_clear) {
const auto options = is_clear ? RelaxedOptions::Samples : RelaxedOptions{};
const ImageId image_id = FindOrInsertImage(info, gpu_addr, options);
ImageId image_id{};
bool delete_state = has_deleted_images;
do {
has_deleted_images = false;
image_id = FindOrInsertImage(info, gpu_addr, options);
delete_state |= has_deleted_images;
} while (has_deleted_images);
has_deleted_images = delete_state;
if (!image_id) {
return NULL_IMAGE_VIEW_ID;
}
@@ -1783,7 +1829,13 @@ void TextureCache<P>::CopyImage(ImageId dst_id, ImageId src_id, std::vector<Imag
const SubresourceExtent src_extent{.levels = 1, .layers = 1};
const SubresourceRange dst_range{.base = dst_base, .extent = dst_extent};
const SubresourceRange src_range{.base = src_base, .extent = src_extent};
const ImageViewInfo dst_view_info(ImageViewType::e2D, dst.info.format, dst_range);
PixelFormat dst_format = dst.info.format;
if (GetFormatType(src.info.format) == SurfaceType::DepthStencil &&
GetFormatType(dst_format) == SurfaceType::ColorTexture &&
BytesPerBlock(dst_format) == 4) {
dst_format = PixelFormat::A8B8G8R8_UNORM;
}
const ImageViewInfo dst_view_info(ImageViewType::e2D, dst_format, dst_range);
const ImageViewInfo src_view_info(ImageViewType::e2D, src.info.format, src_range);
const auto [dst_framebuffer_id, dst_view_id] = RenderTargetFromImage(dst_id, dst_view_info);
Framebuffer* const dst_framebuffer = &slot_framebuffers[dst_framebuffer_id];
@@ -252,7 +252,8 @@ private:
/// Return a blit image pair from the given guest blit parameters
[[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src);
const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Config& copy);
/// Find or create a sampler from a guest descriptor sampler
[[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
+3 -17
View File
@@ -1151,28 +1151,15 @@ bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
const ImageBase* src) {
bool is_resolve = false;
const auto original_src_format = src_info.format;
const auto original_dst_format = dst_info.format;
if (src) {
if (GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
src_info.format = src->info.format;
}
is_resolve = src->info.num_samples > 1;
src_info.num_samples = src->info.num_samples;
src_info.size = src->info.size;
if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
src_info.format = src->info.format;
}
if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
dst_info.format = dst->info.format;
}
if (src && GetFormatType(src->info.format) != SurfaceType::ColorTexture) {
if (dst) {
if (GetFormatType(dst->info.format) == SurfaceType::ColorTexture) {
src_info.format = original_src_format;
}
} else {
dst_info.format = src->info.format;
}
dst_info.format = src->info.format;
}
if (dst && GetFormatType(dst->info.format) != SurfaceType::ColorTexture) {
if (src) {
@@ -1183,7 +1170,6 @@ void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase*
src_info.format = dst->info.format;
}
}
ASSERT(!is_resolve || dst_info.format == src_info.format);
}
u32 MapSizeBytes(const ImageBase& image) {
+2
View File
@@ -508,6 +508,7 @@ void Config::ReadDebuggingValues() {
ReadBasicSetting(Settings::values.extended_logging);
ReadBasicSetting(Settings::values.use_debug_asserts);
ReadBasicSetting(Settings::values.use_auto_stub);
ReadBasicSetting(Settings::values.enable_all_controllers);
qt_config->endGroup();
}
@@ -1051,6 +1052,7 @@ void Config::SaveDebuggingValues() {
WriteBasicSetting(Settings::values.quest_flag);
WriteBasicSetting(Settings::values.use_debug_asserts);
WriteBasicSetting(Settings::values.disable_macro_jit);
WriteBasicSetting(Settings::values.enable_all_controllers);
qt_config->endGroup();
}
@@ -42,6 +42,7 @@ void ConfigureDebug::SetConfiguration() {
ui->quest_flag->setChecked(Settings::values.quest_flag.GetValue());
ui->use_debug_asserts->setChecked(Settings::values.use_debug_asserts.GetValue());
ui->use_auto_stub->setChecked(Settings::values.use_auto_stub.GetValue());
ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue());
ui->enable_graphics_debugging->setEnabled(runtime_lock);
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue());
ui->enable_shader_feedback->setEnabled(runtime_lock);
@@ -67,6 +68,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.quest_flag = ui->quest_flag->isChecked();
Settings::values.use_debug_asserts = ui->use_debug_asserts->isChecked();
Settings::values.use_auto_stub = ui->use_auto_stub->isChecked();
Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked();
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked();
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
@@ -198,6 +198,13 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enable_all_controllers">
<property name="text">
<string>Enable all Controller Types</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@@ -947,6 +947,40 @@ void ConfigureInputPlayer::SetConnectableControllers() {
Core::HID::NpadStyleIndex::GameCube);
ui->comboControllerType->addItem(tr("GameCube Controller"));
}
// Disable all unsupported controllers
if (!Settings::values.enable_all_controllers) {
return;
}
if (enable_all || npad_style_set.palma == 1) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::Pokeball);
ui->comboControllerType->addItem(tr("Poke Ball Plus"));
}
if (enable_all || npad_style_set.lark == 1) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::NES);
ui->comboControllerType->addItem(tr("NES Controller"));
}
if (enable_all || npad_style_set.lucia == 1) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::SNES);
ui->comboControllerType->addItem(tr("SNES Controller"));
}
if (enable_all || npad_style_set.lagoon == 1) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::N64);
ui->comboControllerType->addItem(tr("N64 Controller"));
}
if (enable_all || npad_style_set.lager == 1) {
index_controller_type_pairs.emplace_back(ui->comboControllerType->count(),
Core::HID::NpadStyleIndex::SegaGenesis);
ui->comboControllerType->addItem(tr("Sega Genesis"));
}
};
if (!is_powered_on) {