diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/am/am.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.h | 36 | ||||
| -rw-r--r-- | src/core/hle/service/apm/apm_controller.cpp | 10 | ||||
| -rw-r--r-- | src/core/hle/service/apm/apm_controller.h | 15 | ||||
| -rw-r--r-- | src/input_common/drivers/sdl_driver.cpp | 17 | ||||
| -rw-r--r-- | src/input_common/drivers/udp_client.cpp | 14 | ||||
| -rw-r--r-- | src/input_common/drivers/udp_client.h | 2 | ||||
| -rw-r--r-- | src/input_common/input_engine.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/input_engine.h | 18 | ||||
| -rw-r--r-- | src/input_common/input_poller.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/maxwell_to_gl.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/maxwell_to_vk.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_blit_screen.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_texture_cache.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.cpp | 37 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.h | 5 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 10 |
17 files changed, 136 insertions, 56 deletions
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 773dc9f29..2f8e21568 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -618,7 +618,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) { | |||
| 618 | AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { | 618 | AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { |
| 619 | if (messages.empty()) { | 619 | if (messages.empty()) { |
| 620 | on_new_message->GetWritableEvent().Clear(); | 620 | on_new_message->GetWritableEvent().Clear(); |
| 621 | return AppletMessage::NoMessage; | 621 | return AppletMessage::None; |
| 622 | } | 622 | } |
| 623 | auto msg = messages.front(); | 623 | auto msg = messages.front(); |
| 624 | messages.pop(); | 624 | messages.pop(); |
| @@ -633,7 +633,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const { | |||
| 633 | } | 633 | } |
| 634 | 634 | ||
| 635 | void AppletMessageQueue::RequestExit() { | 635 | void AppletMessageQueue::RequestExit() { |
| 636 | PushMessage(AppletMessage::ExitRequested); | 636 | PushMessage(AppletMessage::Exit); |
| 637 | } | 637 | } |
| 638 | 638 | ||
| 639 | void AppletMessageQueue::FocusStateChanged() { | 639 | void AppletMessageQueue::FocusStateChanged() { |
| @@ -732,7 +732,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) { | |||
| 732 | const auto message = msg_queue->PopMessage(); | 732 | const auto message = msg_queue->PopMessage(); |
| 733 | IPC::ResponseBuilder rb{ctx, 3}; | 733 | IPC::ResponseBuilder rb{ctx, 3}; |
| 734 | 734 | ||
| 735 | if (message == AppletMessageQueue::AppletMessage::NoMessage) { | 735 | if (message == AppletMessageQueue::AppletMessage::None) { |
| 736 | LOG_ERROR(Service_AM, "Message queue is empty"); | 736 | LOG_ERROR(Service_AM, "Message queue is empty"); |
| 737 | rb.Push(ERR_NO_MESSAGES); | 737 | rb.Push(ERR_NO_MESSAGES); |
| 738 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); | 738 | rb.PushEnum<AppletMessageQueue::AppletMessage>(message); |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 2a578aea5..fdd937b82 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -22,6 +22,7 @@ class NVFlinger; | |||
| 22 | 22 | ||
| 23 | namespace Service::AM { | 23 | namespace Service::AM { |
| 24 | 24 | ||
| 25 | // This is nn::settings::Language | ||
| 25 | enum SystemLanguage { | 26 | enum SystemLanguage { |
| 26 | Japanese = 0, | 27 | Japanese = 0, |
| 27 | English = 1, // en-US | 28 | English = 1, // en-US |
| @@ -41,16 +42,44 @@ enum SystemLanguage { | |||
| 41 | // 4.0.0+ | 42 | // 4.0.0+ |
| 42 | SimplifiedChinese = 15, | 43 | SimplifiedChinese = 15, |
| 43 | TraditionalChinese = 16, | 44 | TraditionalChinese = 16, |
| 45 | // 10.1.0+ | ||
| 46 | BrazilianPortuguese = 17, | ||
| 44 | }; | 47 | }; |
| 45 | 48 | ||
| 46 | class AppletMessageQueue { | 49 | class AppletMessageQueue { |
| 47 | public: | 50 | public: |
| 51 | // This is nn::am::AppletMessage | ||
| 48 | enum class AppletMessage : u32 { | 52 | enum class AppletMessage : u32 { |
| 49 | NoMessage = 0, | 53 | None = 0, |
| 50 | ExitRequested = 4, | 54 | ChangeIntoForeground = 1, |
| 55 | ChangeIntoBackground = 2, | ||
| 56 | Exit = 4, | ||
| 57 | ApplicationExited = 6, | ||
| 51 | FocusStateChanged = 15, | 58 | FocusStateChanged = 15, |
| 59 | Resume = 16, | ||
| 60 | DetectShortPressingHomeButton = 20, | ||
| 61 | DetectLongPressingHomeButton = 21, | ||
| 62 | DetectShortPressingPowerButton = 22, | ||
| 63 | DetectMiddlePressingPowerButton = 23, | ||
| 64 | DetectLongPressingPowerButton = 24, | ||
| 65 | RequestToPrepareSleep = 25, | ||
| 66 | FinishedSleepSequence = 26, | ||
| 67 | SleepRequiredByHighTemperature = 27, | ||
| 68 | SleepRequiredByLowBattery = 28, | ||
| 69 | AutoPowerDown = 29, | ||
| 52 | OperationModeChanged = 30, | 70 | OperationModeChanged = 30, |
| 53 | PerformanceModeChanged = 31, | 71 | PerformanceModeChanged = 31, |
| 72 | DetectReceivingCecSystemStandby = 32, | ||
| 73 | SdCardRemoved = 33, | ||
| 74 | LaunchApplicationRequested = 50, | ||
| 75 | RequestToDisplay = 51, | ||
| 76 | ShowApplicationLogo = 55, | ||
| 77 | HideApplicationLogo = 56, | ||
| 78 | ForceHideApplicationLogo = 57, | ||
| 79 | FloatingApplicationDetected = 60, | ||
| 80 | DetectShortPressingCaptureButton = 90, | ||
| 81 | AlbumScreenShotTaken = 92, | ||
| 82 | AlbumRecordingSaved = 93, | ||
| 54 | }; | 83 | }; |
| 55 | 84 | ||
| 56 | explicit AppletMessageQueue(Core::System& system); | 85 | explicit AppletMessageQueue(Core::System& system); |
| @@ -179,11 +208,14 @@ public: | |||
| 179 | ~ICommonStateGetter() override; | 208 | ~ICommonStateGetter() override; |
| 180 | 209 | ||
| 181 | private: | 210 | private: |
| 211 | // This is nn::oe::FocusState | ||
| 182 | enum class FocusState : u8 { | 212 | enum class FocusState : u8 { |
| 183 | InFocus = 1, | 213 | InFocus = 1, |
| 184 | NotInFocus = 2, | 214 | NotInFocus = 2, |
| 215 | Background = 3, | ||
| 185 | }; | 216 | }; |
| 186 | 217 | ||
| 218 | // This is nn::oe::OperationMode | ||
| 187 | enum class OperationMode : u8 { | 219 | enum class OperationMode : u8 { |
| 188 | Handheld = 0, | 220 | Handheld = 0, |
| 189 | Docked = 1, | 221 | Docked = 1, |
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp index 98839fe97..187fef2ad 100644 --- a/src/core/hle/service/apm/apm_controller.cpp +++ b/src/core/hle/service/apm/apm_controller.cpp | |||
| @@ -17,8 +17,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con | |||
| 17 | 17 | ||
| 18 | Controller::Controller(Core::Timing::CoreTiming& core_timing_) | 18 | Controller::Controller(Core::Timing::CoreTiming& core_timing_) |
| 19 | : core_timing{core_timing_}, configs{ | 19 | : core_timing{core_timing_}, configs{ |
| 20 | {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, | 20 | {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 21 | {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, | 21 | {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION}, |
| 22 | } {} | 22 | } {} |
| 23 | 23 | ||
| 24 | Controller::~Controller() = default; | 24 | Controller::~Controller() = default; |
| @@ -63,13 +63,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { | |||
| 63 | PerformanceConfiguration::Config15, | 63 | PerformanceConfiguration::Config15, |
| 64 | }}; | 64 | }}; |
| 65 | 65 | ||
| 66 | SetPerformanceConfiguration(PerformanceMode::Docked, | 66 | SetPerformanceConfiguration(PerformanceMode::Boost, |
| 67 | BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); | 67 | BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | PerformanceMode Controller::GetCurrentPerformanceMode() const { | 70 | PerformanceMode Controller::GetCurrentPerformanceMode() const { |
| 71 | return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked | 71 | return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost |
| 72 | : PerformanceMode::Handheld; | 72 | : PerformanceMode::Normal; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { | 75 | PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { |
diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..d6fbd2c0c 100644 --- a/src/core/hle/service/apm/apm_controller.h +++ b/src/core/hle/service/apm/apm_controller.h | |||
| @@ -32,15 +32,18 @@ enum class PerformanceConfiguration : u32 { | |||
| 32 | Config16 = 0x9222000C, | 32 | Config16 = 0x9222000C, |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | // This is nn::oe::CpuBoostMode | ||
| 35 | enum class CpuBoostMode : u32 { | 36 | enum class CpuBoostMode : u32 { |
| 36 | Disabled = 0, | 37 | Normal = 0, // Boost mode disabled |
| 37 | Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 | 38 | FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16 |
| 38 | Partial = 2, // GPU Only -> Config 15 or 16 | 39 | Partial = 2, // GPU Only -> Config 15 or 16 |
| 39 | }; | 40 | }; |
| 40 | 41 | ||
| 41 | enum class PerformanceMode : u8 { | 42 | // This is nn::oe::PerformanceMode |
| 42 | Handheld = 0, | 43 | enum class PerformanceMode : s32 { |
| 43 | Docked = 1, | 44 | Invalid = -1, |
| 45 | Normal = 0, | ||
| 46 | Boost = 1, | ||
| 44 | }; | 47 | }; |
| 45 | 48 | ||
| 46 | // Class to manage the state and change of the emulated system performance. | 49 | // Class to manage the state and change of the emulated system performance. |
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 5cf1987ad..c17ea305e 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp | |||
| @@ -175,22 +175,23 @@ public: | |||
| 175 | return false; | 175 | return false; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | BatteryLevel GetBatteryLevel() { | 178 | Common::Input::BatteryLevel GetBatteryLevel() { |
| 179 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); | 179 | const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get()); |
| 180 | switch (level) { | 180 | switch (level) { |
| 181 | case SDL_JOYSTICK_POWER_EMPTY: | 181 | case SDL_JOYSTICK_POWER_EMPTY: |
| 182 | return BatteryLevel::Empty; | 182 | return Common::Input::BatteryLevel::Empty; |
| 183 | case SDL_JOYSTICK_POWER_LOW: | 183 | case SDL_JOYSTICK_POWER_LOW: |
| 184 | return BatteryLevel::Low; | 184 | return Common::Input::BatteryLevel::Low; |
| 185 | case SDL_JOYSTICK_POWER_MEDIUM: | 185 | case SDL_JOYSTICK_POWER_MEDIUM: |
| 186 | return BatteryLevel::Medium; | 186 | return Common::Input::BatteryLevel::Medium; |
| 187 | case SDL_JOYSTICK_POWER_FULL: | 187 | case SDL_JOYSTICK_POWER_FULL: |
| 188 | case SDL_JOYSTICK_POWER_MAX: | 188 | case SDL_JOYSTICK_POWER_MAX: |
| 189 | return BatteryLevel::Full; | 189 | return Common::Input::BatteryLevel::Full; |
| 190 | case SDL_JOYSTICK_POWER_UNKNOWN: | ||
| 191 | case SDL_JOYSTICK_POWER_WIRED: | 190 | case SDL_JOYSTICK_POWER_WIRED: |
| 191 | return Common::Input::BatteryLevel::Charging; | ||
| 192 | case SDL_JOYSTICK_POWER_UNKNOWN: | ||
| 192 | default: | 193 | default: |
| 193 | return BatteryLevel::Charging; | 194 | return Common::Input::BatteryLevel::None; |
| 194 | } | 195 | } |
| 195 | } | 196 | } |
| 196 | 197 | ||
| @@ -351,6 +352,8 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) { | |||
| 351 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { | 352 | if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) { |
| 352 | const PadIdentifier identifier = joystick->GetPadIdentifier(); | 353 | const PadIdentifier identifier = joystick->GetPadIdentifier(); |
| 353 | SetButton(identifier, event.jbutton.button, true); | 354 | SetButton(identifier, event.jbutton.button, true); |
| 355 | // Battery doesn't trigger an event so just update every button press | ||
| 356 | SetBattery(identifier, joystick->GetBatteryLevel()); | ||
| 354 | } | 357 | } |
| 355 | break; | 358 | break; |
| 356 | } | 359 | } |
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 64162f431..9780ead10 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp | |||
| @@ -192,22 +192,22 @@ std::size_t UDPClient::GetClientNumber(std::string_view host, u16 port) const { | |||
| 192 | return MAX_UDP_CLIENTS; | 192 | return MAX_UDP_CLIENTS; |
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { | 195 | Common::Input::BatteryLevel UDPClient::GetBatteryLevel(Response::Battery battery) const { |
| 196 | switch (battery) { | 196 | switch (battery) { |
| 197 | case Response::Battery::Dying: | 197 | case Response::Battery::Dying: |
| 198 | return BatteryLevel::Empty; | 198 | return Common::Input::BatteryLevel::Empty; |
| 199 | case Response::Battery::Low: | 199 | case Response::Battery::Low: |
| 200 | return BatteryLevel::Critical; | 200 | return Common::Input::BatteryLevel::Critical; |
| 201 | case Response::Battery::Medium: | 201 | case Response::Battery::Medium: |
| 202 | return BatteryLevel::Low; | 202 | return Common::Input::BatteryLevel::Low; |
| 203 | case Response::Battery::High: | 203 | case Response::Battery::High: |
| 204 | return BatteryLevel::Medium; | 204 | return Common::Input::BatteryLevel::Medium; |
| 205 | case Response::Battery::Full: | 205 | case Response::Battery::Full: |
| 206 | case Response::Battery::Charged: | 206 | case Response::Battery::Charged: |
| 207 | return BatteryLevel::Full; | 207 | return Common::Input::BatteryLevel::Full; |
| 208 | case Response::Battery::Charging: | 208 | case Response::Battery::Charging: |
| 209 | default: | 209 | default: |
| 210 | return BatteryLevel::Charging; | 210 | return Common::Input::BatteryLevel::Charging; |
| 211 | } | 211 | } |
| 212 | } | 212 | } |
| 213 | 213 | ||
diff --git a/src/input_common/drivers/udp_client.h b/src/input_common/drivers/udp_client.h index 76e32bd04..c7cc7d846 100644 --- a/src/input_common/drivers/udp_client.h +++ b/src/input_common/drivers/udp_client.h | |||
| @@ -141,7 +141,7 @@ private: | |||
| 141 | std::size_t GetClientNumber(std::string_view host, u16 port) const; | 141 | std::size_t GetClientNumber(std::string_view host, u16 port) const; |
| 142 | 142 | ||
| 143 | // Translates UDP battery level to input engine battery level | 143 | // Translates UDP battery level to input engine battery level |
| 144 | BatteryLevel GetBatteryLevel(Response::Battery battery) const; | 144 | Common::Input::BatteryLevel GetBatteryLevel(Response::Battery battery) const; |
| 145 | 145 | ||
| 146 | void OnVersion(Response::Version); | 146 | void OnVersion(Response::Version); |
| 147 | void OnPortInfo(Response::PortInfo); | 147 | void OnPortInfo(Response::PortInfo); |
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp index 65ae1b848..7adf7e3d7 100644 --- a/src/input_common/input_engine.cpp +++ b/src/input_common/input_engine.cpp | |||
| @@ -70,7 +70,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) | |||
| 70 | TriggerOnAxisChange(identifier, axis, value); | 70 | TriggerOnAxisChange(identifier, axis, value); |
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void InputEngine::SetBattery(const PadIdentifier& identifier, BatteryLevel value) { | 73 | void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) { |
| 74 | { | 74 | { |
| 75 | std::lock_guard lock{mutex}; | 75 | std::lock_guard lock{mutex}; |
| 76 | ControllerData& controller = controller_list.at(identifier); | 76 | ControllerData& controller = controller_list.at(identifier); |
| @@ -143,13 +143,13 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const { | |||
| 143 | return axis_iter->second; | 143 | return axis_iter->second; |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { | 146 | Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const { |
| 147 | std::lock_guard lock{mutex}; | 147 | std::lock_guard lock{mutex}; |
| 148 | const auto controller_iter = controller_list.find(identifier); | 148 | const auto controller_iter = controller_list.find(identifier); |
| 149 | if (controller_iter == controller_list.cend()) { | 149 | if (controller_iter == controller_list.cend()) { |
| 150 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), | 150 | LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(), |
| 151 | identifier.pad, identifier.port); | 151 | identifier.pad, identifier.port); |
| 152 | return BatteryLevel::Charging; | 152 | return Common::Input::BatteryLevel::Charging; |
| 153 | } | 153 | } |
| 154 | const ControllerData& controller = controller_iter->second; | 154 | const ControllerData& controller = controller_iter->second; |
| 155 | return controller.battery; | 155 | return controller.battery; |
| @@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, | |||
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, | 272 | void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier, |
| 273 | [[maybe_unused]] BatteryLevel value) { | 273 | [[maybe_unused]] Common::Input::BatteryLevel value) { |
| 274 | std::lock_guard lock{mutex_callback}; | 274 | std::lock_guard lock{mutex_callback}; |
| 275 | for (const auto& poller_pair : callback_list) { | 275 | for (const auto& poller_pair : callback_list) { |
| 276 | const InputIdentifier& poller = poller_pair.second; | 276 | const InputIdentifier& poller = poller_pair.second; |
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h index c6c027aef..f44e0799b 100644 --- a/src/input_common/input_engine.h +++ b/src/input_common/input_engine.h | |||
| @@ -34,16 +34,6 @@ struct BasicMotion { | |||
| 34 | u64 delta_timestamp{}; | 34 | u64 delta_timestamp{}; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | // Stages of a battery charge | ||
| 38 | enum class BatteryLevel { | ||
| 39 | Empty, | ||
| 40 | Critical, | ||
| 41 | Low, | ||
| 42 | Medium, | ||
| 43 | Full, | ||
| 44 | Charging, | ||
| 45 | }; | ||
| 46 | |||
| 47 | // Types of input that are stored in the engine | 37 | // Types of input that are stored in the engine |
| 48 | enum class EngineInputType { | 38 | enum class EngineInputType { |
| 49 | None, | 39 | None, |
| @@ -178,7 +168,7 @@ public: | |||
| 178 | bool GetButton(const PadIdentifier& identifier, int button) const; | 168 | bool GetButton(const PadIdentifier& identifier, int button) const; |
| 179 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; | 169 | bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; |
| 180 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; | 170 | f32 GetAxis(const PadIdentifier& identifier, int axis) const; |
| 181 | BatteryLevel GetBattery(const PadIdentifier& identifier) const; | 171 | Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; |
| 182 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; | 172 | BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; |
| 183 | 173 | ||
| 184 | int SetCallback(InputIdentifier input_identifier); | 174 | int SetCallback(InputIdentifier input_identifier); |
| @@ -189,7 +179,7 @@ protected: | |||
| 189 | void SetButton(const PadIdentifier& identifier, int button, bool value); | 179 | void SetButton(const PadIdentifier& identifier, int button, bool value); |
| 190 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); | 180 | void SetHatButton(const PadIdentifier& identifier, int button, u8 value); |
| 191 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); | 181 | void SetAxis(const PadIdentifier& identifier, int axis, f32 value); |
| 192 | void SetBattery(const PadIdentifier& identifier, BatteryLevel value); | 182 | void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 193 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); | 183 | void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); |
| 194 | 184 | ||
| 195 | virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { | 185 | virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { |
| @@ -202,13 +192,13 @@ private: | |||
| 202 | std::unordered_map<int, u8> hat_buttons; | 192 | std::unordered_map<int, u8> hat_buttons; |
| 203 | std::unordered_map<int, float> axes; | 193 | std::unordered_map<int, float> axes; |
| 204 | std::unordered_map<int, BasicMotion> motions; | 194 | std::unordered_map<int, BasicMotion> motions; |
| 205 | BatteryLevel battery{}; | 195 | Common::Input::BatteryLevel battery{}; |
| 206 | }; | 196 | }; |
| 207 | 197 | ||
| 208 | void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); | 198 | void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); |
| 209 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); | 199 | void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); |
| 210 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); | 200 | void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); |
| 211 | void TriggerOnBatteryChange(const PadIdentifier& identifier, BatteryLevel value); | 201 | void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); |
| 212 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, | 202 | void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, |
| 213 | const BasicMotion& value); | 203 | const BasicMotion& value); |
| 214 | 204 | ||
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp index 7f3c08597..82b585ff2 100644 --- a/src/input_common/input_poller.cpp +++ b/src/input_common/input_poller.cpp | |||
| @@ -470,7 +470,7 @@ public: | |||
| 470 | } | 470 | } |
| 471 | 471 | ||
| 472 | Common::Input::BatteryStatus GetStatus() const { | 472 | Common::Input::BatteryStatus GetStatus() const { |
| 473 | return static_cast<Common::Input::BatteryLevel>(input_engine->GetBattery(identifier)); | 473 | return input_engine->GetBattery(identifier); |
| 474 | } | 474 | } |
| 475 | 475 | ||
| 476 | void ForceUpdate() override { | 476 | void ForceUpdate() override { |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index daba42ed9..db5bf1d30 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -184,6 +184,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) { | |||
| 184 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 184 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 185 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 185 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 186 | return GL_FLOAT; | 186 | return GL_FLOAT; |
| 187 | case Maxwell::VertexAttribute::Size::Size_11_11_10: | ||
| 188 | return GL_UNSIGNED_INT_10F_11F_11F_REV; | ||
| 187 | default: | 189 | default: |
| 188 | break; | 190 | break; |
| 189 | } | 191 | } |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 751e4792b..1c136c410 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -495,6 +495,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib | |||
| 495 | return VK_FORMAT_R32G32B32_SFLOAT; | 495 | return VK_FORMAT_R32G32B32_SFLOAT; |
| 496 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 496 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 497 | return VK_FORMAT_R32G32B32A32_SFLOAT; | 497 | return VK_FORMAT_R32G32B32A32_SFLOAT; |
| 498 | case Maxwell::VertexAttribute::Size::Size_11_11_10: | ||
| 499 | return VK_FORMAT_B10G11R11_UFLOAT_PACK32; | ||
| 498 | default: | 500 | default: |
| 499 | break; | 501 | break; |
| 500 | } | 502 | } |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index c71a1f44d..621a6a071 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -100,6 +100,8 @@ VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | |||
| 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; |
| 101 | case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: | 101 | case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: |
| 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; | 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; |
| 103 | case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: | ||
| 104 | return VK_FORMAT_B8G8R8A8_UNORM; | ||
| 103 | default: | 105 | default: |
| 104 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | 106 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", |
| 105 | static_cast<u32>(framebuffer.pixel_format)); | 107 | static_cast<u32>(framebuffer.pixel_format)); |
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 0ba56ff1e..0f62779de 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp | |||
| @@ -554,10 +554,12 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im | |||
| 554 | }; | 554 | }; |
| 555 | } | 555 | } |
| 556 | 556 | ||
| 557 | [[nodiscard]] bool IsFormatFlipped(PixelFormat format) { | 557 | [[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { |
| 558 | switch (format) { | 558 | switch (format) { |
| 559 | case PixelFormat::A1B5G5R5_UNORM: | 559 | case PixelFormat::A1B5G5R5_UNORM: |
| 560 | return true; | 560 | return true; |
| 561 | case PixelFormat::B5G6R5_UNORM: | ||
| 562 | return emulate_bgr565; | ||
| 561 | default: | 563 | default: |
| 562 | return false; | 564 | return false; |
| 563 | } | 565 | } |
| @@ -1488,7 +1490,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI | |||
| 1488 | }; | 1490 | }; |
| 1489 | if (!info.IsRenderTarget()) { | 1491 | if (!info.IsRenderTarget()) { |
| 1490 | swizzle = info.Swizzle(); | 1492 | swizzle = info.Swizzle(); |
| 1491 | if (IsFormatFlipped(format)) { | 1493 | if (IsFormatFlipped(format, device->MustEmulateBGR565())) { |
| 1492 | std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); | 1494 | std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); |
| 1493 | } | 1495 | } |
| 1494 | if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { | 1496 | if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { |
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 153702c0b..effde73c9 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -39,6 +39,11 @@ constexpr std::array DEPTH16_UNORM_STENCIL8_UINT{ | |||
| 39 | VK_FORMAT_D32_SFLOAT_S8_UINT, | 39 | VK_FORMAT_D32_SFLOAT_S8_UINT, |
| 40 | VK_FORMAT_UNDEFINED, | 40 | VK_FORMAT_UNDEFINED, |
| 41 | }; | 41 | }; |
| 42 | |||
| 43 | constexpr std::array B5G6R5_UNORM_PACK16{ | ||
| 44 | VK_FORMAT_R5G6B5_UNORM_PACK16, | ||
| 45 | VK_FORMAT_UNDEFINED, | ||
| 46 | }; | ||
| 42 | } // namespace Alternatives | 47 | } // namespace Alternatives |
| 43 | 48 | ||
| 44 | enum class NvidiaArchitecture { | 49 | enum class NvidiaArchitecture { |
| @@ -87,6 +92,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) { | |||
| 87 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); | 92 | return Alternatives::DEPTH24_UNORM_STENCIL8_UINT.data(); |
| 88 | case VK_FORMAT_D16_UNORM_S8_UINT: | 93 | case VK_FORMAT_D16_UNORM_S8_UINT: |
| 89 | return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); | 94 | return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); |
| 95 | case VK_FORMAT_B5G6R5_UNORM_PACK16: | ||
| 96 | return Alternatives::B5G6R5_UNORM_PACK16.data(); | ||
| 90 | default: | 97 | default: |
| 91 | return nullptr; | 98 | return nullptr; |
| 92 | } | 99 | } |
| @@ -224,9 +231,14 @@ std::vector<std::string> GetSupportedExtensions(vk::PhysicalDevice physical) { | |||
| 224 | return supported_extensions; | 231 | return supported_extensions; |
| 225 | } | 232 | } |
| 226 | 233 | ||
| 234 | bool IsExtensionSupported(std::span<const std::string> supported_extensions, | ||
| 235 | std::string_view extension) { | ||
| 236 | return std::ranges::find(supported_extensions, extension) != supported_extensions.end(); | ||
| 237 | } | ||
| 238 | |||
| 227 | NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, | 239 | NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, |
| 228 | std::span<const std::string> exts) { | 240 | std::span<const std::string> exts) { |
| 229 | if (std::ranges::find(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME) != exts.end()) { | 241 | if (IsExtensionSupported(exts, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { |
| 230 | VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; | 242 | VkPhysicalDeviceFragmentShadingRatePropertiesKHR shading_rate_props{}; |
| 231 | shading_rate_props.sType = | 243 | shading_rate_props.sType = |
| 232 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; | 244 | VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; |
| @@ -239,7 +251,7 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical, | |||
| 239 | return NvidiaArchitecture::AmpereOrNewer; | 251 | return NvidiaArchitecture::AmpereOrNewer; |
| 240 | } | 252 | } |
| 241 | } | 253 | } |
| 242 | if (std::ranges::find(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME) != exts.end()) { | 254 | if (IsExtensionSupported(exts, VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME)) { |
| 243 | return NvidiaArchitecture::Turing; | 255 | return NvidiaArchitecture::Turing; |
| 244 | } | 256 | } |
| 245 | return NvidiaArchitecture::VoltaOrOlder; | 257 | return NvidiaArchitecture::VoltaOrOlder; |
| @@ -604,7 +616,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 604 | break; | 616 | break; |
| 605 | } | 617 | } |
| 606 | } | 618 | } |
| 607 | if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_MESA_RADV) { | 619 | const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; |
| 620 | if (ext_extended_dynamic_state && is_radv) { | ||
| 608 | // Mask driver version variant | 621 | // Mask driver version variant |
| 609 | const u32 version = (properties.driverVersion << 3) >> 3; | 622 | const u32 version = (properties.driverVersion << 3) >> 3; |
| 610 | if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { | 623 | if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { |
| @@ -613,6 +626,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 613 | ext_extended_dynamic_state = false; | 626 | ext_extended_dynamic_state = false; |
| 614 | } | 627 | } |
| 615 | } | 628 | } |
| 629 | if (ext_vertex_input_dynamic_state && is_radv) { | ||
| 630 | // TODO(ameerj): Blacklist only offending driver versions | ||
| 631 | // TODO(ameerj): Confirm if RDNA1 is affected | ||
| 632 | const bool is_rdna2 = | ||
| 633 | IsExtensionSupported(supported_extensions, VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); | ||
| 634 | if (is_rdna2) { | ||
| 635 | LOG_WARNING(Render_Vulkan, | ||
| 636 | "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); | ||
| 637 | ext_vertex_input_dynamic_state = false; | ||
| 638 | } | ||
| 639 | } | ||
| 616 | sets_per_pool = 64; | 640 | sets_per_pool = 64; |
| 617 | 641 | ||
| 618 | const bool is_amd = | 642 | const bool is_amd = |
| @@ -628,7 +652,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 628 | has_broken_cube_compatibility = true; | 652 | has_broken_cube_compatibility = true; |
| 629 | } | 653 | } |
| 630 | } | 654 | } |
| 631 | const bool is_amd_or_radv = is_amd || driver_id == VK_DRIVER_ID_MESA_RADV; | 655 | const bool is_amd_or_radv = is_amd || is_radv; |
| 632 | if (ext_sampler_filter_minmax && is_amd_or_radv) { | 656 | if (ext_sampler_filter_minmax && is_amd_or_radv) { |
| 633 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. | 657 | // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. |
| 634 | if (!is_float16_supported) { | 658 | if (!is_float16_supported) { |
| @@ -639,6 +663,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 639 | } | 663 | } |
| 640 | 664 | ||
| 641 | const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; | 665 | const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; |
| 666 | const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; | ||
| 642 | if (ext_vertex_input_dynamic_state && is_intel_windows) { | 667 | if (ext_vertex_input_dynamic_state && is_intel_windows) { |
| 643 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); | 668 | LOG_WARNING(Render_Vulkan, "Blacklisting Intel for VK_EXT_vertex_input_dynamic_state"); |
| 644 | ext_vertex_input_dynamic_state = false; | 669 | ext_vertex_input_dynamic_state = false; |
| @@ -652,6 +677,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 652 | LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); | 677 | LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); |
| 653 | cant_blit_msaa = true; | 678 | cant_blit_msaa = true; |
| 654 | } | 679 | } |
| 680 | if (is_intel_anv) { | ||
| 681 | LOG_WARNING(Render_Vulkan, "ANV driver does not support native BGR format"); | ||
| 682 | must_emulate_bgr565 = true; | ||
| 683 | } | ||
| 655 | 684 | ||
| 656 | supports_d24_depth = | 685 | supports_d24_depth = |
| 657 | IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, | 686 | IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 37d140ebd..34b1add16 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -354,6 +354,10 @@ public: | |||
| 354 | return cant_blit_msaa; | 354 | return cant_blit_msaa; |
| 355 | } | 355 | } |
| 356 | 356 | ||
| 357 | bool MustEmulateBGR565() const { | ||
| 358 | return must_emulate_bgr565; | ||
| 359 | } | ||
| 360 | |||
| 357 | private: | 361 | private: |
| 358 | /// Checks if the physical device is suitable. | 362 | /// Checks if the physical device is suitable. |
| 359 | void CheckSuitability(bool requires_swapchain) const; | 363 | void CheckSuitability(bool requires_swapchain) const; |
| @@ -448,6 +452,7 @@ private: | |||
| 448 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached | 452 | bool has_nsight_graphics{}; ///< Has Nsight Graphics attached |
| 449 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. | 453 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. |
| 450 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. | 454 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. |
| 455 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. | ||
| 451 | 456 | ||
| 452 | // Telemetry parameters | 457 | // Telemetry parameters |
| 453 | std::string vendor_name; ///< Device's driver name. | 458 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e3fd38a02..b3a8da0ea 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -2937,6 +2937,7 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 2937 | if (nfc_state == Service::NFP::DeviceState::TagFound || | 2937 | if (nfc_state == Service::NFP::DeviceState::TagFound || |
| 2938 | nfc_state == Service::NFP::DeviceState::TagMounted) { | 2938 | nfc_state == Service::NFP::DeviceState::TagMounted) { |
| 2939 | nfc->CloseAmiibo(); | 2939 | nfc->CloseAmiibo(); |
| 2940 | QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); | ||
| 2940 | return; | 2941 | return; |
| 2941 | } | 2942 | } |
| 2942 | 2943 | ||
| @@ -2965,6 +2966,15 @@ void GMainWindow::LoadAmiibo(const QString& filename) { | |||
| 2965 | return; | 2966 | return; |
| 2966 | } | 2967 | } |
| 2967 | 2968 | ||
| 2969 | // Remove amiibo if one is connected | ||
| 2970 | const auto nfc_state = nfc->GetCurrentState(); | ||
| 2971 | if (nfc_state == Service::NFP::DeviceState::TagFound || | ||
| 2972 | nfc_state == Service::NFP::DeviceState::TagMounted) { | ||
| 2973 | nfc->CloseAmiibo(); | ||
| 2974 | QMessageBox::warning(this, tr("Amiibo"), tr("The current amiibo has been removed")); | ||
| 2975 | return; | ||
| 2976 | } | ||
| 2977 | |||
| 2968 | QFile nfc_file{filename}; | 2978 | QFile nfc_file{filename}; |
| 2969 | if (!nfc_file.open(QIODevice::ReadOnly)) { | 2979 | if (!nfc_file.open(QIODevice::ReadOnly)) { |
| 2970 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), | 2980 | QMessageBox::warning(this, tr("Error opening Amiibo data file"), |