Compare commits

...
Author SHA1 Message Date
yuzubot 130542c074 "Merge Tagged PR 6598" 2022-03-26 19:52:51 +00:00
yuzubot 6dc2058c64 "Merge Tagged PR 7346" 2022-03-26 19:52:50 +00:00
yuzubot 887358c096 "Merge Tagged PR 7832" 2022-03-26 19:52:50 +00:00
yuzubot 8f0d603da9 "Merge Tagged PR 8012" 2022-03-26 19:52:49 +00:00
yuzubot a5539e4b73 "Merge Tagged PR 8088" 2022-03-26 19:52:48 +00:00
bunnei e043cc0bc3 Merge pull request #8092 from yuzu-emu/revert-8080-yo-momma-so-fat-that
Revert "Memory GPU <-> CPU: reduce infighting in the texture cache by adding CPU Cached memory."
2022-03-26 12:39:31 -07:00
bunnei af04f8b8e9 Revert "Memory GPU <-> CPU: reduce infighting in the texture cache by adding CPU Cached memory." 2022-03-26 12:38:30 -07:00
bunnei 664d8c8732 Merge pull request #8041 from Morph1984/inline-swkbd
applets/swkbd: Add support for an updated inline software keyboard
2022-03-25 23:19:11 -07:00
Morph e6f200b960 applets/swkbd: Split software keyboard initialization
Since the CalcArg struct has been updated with a new size and fields, we have to split the initialization of the keyboard into multiple functions.
This also adds support for parsing the new CalcArg struct used by updated versions of Monster Hunter Rise.
2022-03-21 23:58:50 -04:00
Morph e7c1e6fc27 applets/swkbd: Add new inline software keyboard types
These were added in newer firmware versions.
2022-03-21 23:58:50 -04:00
23 changed files with 618 additions and 770 deletions

No files matched your search

+9
View File
@@ -87,6 +87,14 @@ static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
}
void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
auto now = std::chrono::steady_clock::now();
auto duration = now.time_since_epoch();
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
if (nanoseconds > expected_cb_time) {
ns_late = nanoseconds - expected_cb_time;
}
if (!IsPlaying()) {
// Ensure we are in playing state before playing the next buffer
sink_stream.Flush();
@@ -121,6 +129,7 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
ns_late = {};
}
expected_cb_time = nanoseconds + (buffer_release_ns - ns_late);
core_timing.ScheduleEvent(buffer_release_ns - ns_late, release_event, {});
}
+8 -7
View File
@@ -117,13 +117,14 @@ private:
ReleaseCallback release_callback; ///< Buffer release callback for the stream
State state{State::Stopped}; ///< Playback state of the stream
std::shared_ptr<Core::Timing::EventType>
release_event; ///< Core timing release event for the stream
BufferPtr active_buffer; ///< Actively playing buffer in the stream
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
std::string name; ///< Name of the stream, must be unique
release_event; ///< Core timing release event for the stream
BufferPtr active_buffer; ///< Actively playing buffer in the stream
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
SinkStream& sink_stream; ///< Output sink for the stream
Core::Timing::CoreTiming& core_timing; ///< Core timing instance.
std::string name; ///< Name of the stream, must be unique
std::chrono::nanoseconds expected_cb_time = {}; ///< Estimated time of next callback
};
using StreamPtr = std::shared_ptr<Stream>;
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
ASSERT(work_buffer_storage != nullptr);
if (swkbd_config_common.initial_string_length == 0) {
InitializeFrontendKeyboard();
InitializeFrontendNormalKeyboard();
return;
}
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
InitializeFrontendKeyboard();
InitializeFrontendNormalKeyboard();
}
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
ReplyDefault();
}
void SoftwareKeyboard::InitializeFrontendKeyboard() {
if (is_background) {
const auto& appear_arg = swkbd_calc_arg.appear_arg;
void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
const s32 initial_cursor_position =
current_cursor_position > 0 ? current_cursor_position : 0;
const u32 max_text_length =
swkbd_config_common.max_text_length > 0 &&
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? swkbd_config_common.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
? swkbd_config_common.min_text_length
: 0;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
const s32 initial_cursor_position = [this] {
switch (swkbd_config_common.initial_cursor_position) {
case SwkbdInitialCursorPosition::Start:
default:
return 0;
case SwkbdInitialCursorPosition::End:
return static_cast<s32>(initial_text.size());
}
}();
frontend.InitializeKeyboard(
true, std::move(initialize_parameters), {},
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
SubmitTextInline(reply_type, submitted_text, cursor_position);
});
} else {
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
const auto text_draw_type = [this, max_text_length] {
switch (swkbd_config_common.text_draw_type) {
case SwkbdTextDrawType::Line:
default:
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
case SwkbdTextDrawType::Box:
case SwkbdTextDrawType::DownloadCode:
return swkbd_config_common.text_draw_type;
}
}();
std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
const auto enable_return_button =
text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
? swkbd_config_new.disable_cancel_button
: false;
std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{std::move(header_text)},
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{swkbd_config_common.type},
.password_mode{swkbd_config_common.password_mode},
.text_draw_type{text_draw_type},
.key_disable_flags{swkbd_config_common.key_disable_flags},
.use_blur_background{swkbd_config_common.use_blur_background},
.enable_backspace_button{true},
.enable_return_button{enable_return_button},
.disable_cancel_button{disable_cancel_button},
};
const u32 max_text_length =
swkbd_config_common.max_text_length > 0 &&
swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? swkbd_config_common.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
frontend.InitializeKeyboard(
false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
SubmitTextNormal(result, submitted_text, confirmed);
},
{});
}
const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
? swkbd_config_common.min_text_length
: 0;
void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
frontend.InitializeKeyboard(
true, std::move(initialize_parameters), {},
[this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
SubmitTextInline(reply_type, submitted_text, cursor_position);
});
}
const s32 initial_cursor_position = [this] {
switch (swkbd_config_common.initial_cursor_position) {
case SwkbdInitialCursorPosition::Start:
default:
return 0;
case SwkbdInitialCursorPosition::End:
return static_cast<s32>(initial_text.size());
}
}();
void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const auto text_draw_type = [this, max_text_length] {
switch (swkbd_config_common.text_draw_type) {
case SwkbdTextDrawType::Line:
default:
return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
case SwkbdTextDrawType::Box:
case SwkbdTextDrawType::DownloadCode:
return swkbd_config_common.text_draw_type;
}
}();
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
? swkbd_config_common.enable_return_button
: false;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
? swkbd_config_new.disable_cancel_button
: false;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{std::move(header_text)},
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{swkbd_config_common.type},
.password_mode{swkbd_config_common.password_mode},
.text_draw_type{text_draw_type},
.key_disable_flags{swkbd_config_common.key_disable_flags},
.use_blur_background{swkbd_config_common.use_blur_background},
.enable_backspace_button{true},
.enable_return_button{enable_return_button},
.disable_cancel_button{disable_cancel_button},
};
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
frontend.InitializeKeyboard(
false, std::move(initialize_parameters),
[this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
SubmitTextNormal(result, submitted_text, confirmed);
},
{});
}
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
appear_arg.ok_text.data(), appear_arg.ok_text.size());
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
const auto text_draw_type =
max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
Core::Frontend::KeyboardInitializeParameters initialize_parameters{
.ok_text{std::move(ok_text)},
.header_text{},
.sub_text{},
.guide_text{},
.initial_text{current_text},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
.type{appear_arg.type},
.password_mode{SwkbdPasswordMode::Disabled},
.text_draw_type{text_draw_type},
.key_disable_flags{appear_arg.key_disable_flags},
.use_blur_background{false},
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
}
void SoftwareKeyboard::ShowInlineKeyboard() {
void SoftwareKeyboard::ShowInlineKeyboard(
Core::Frontend::InlineAppearParameters appear_parameters) {
frontend.ShowInlineKeyboard(std::move(appear_parameters));
ChangeState(SwkbdState::InitializedIsShown);
}
void SoftwareKeyboard::ShowInlineKeyboardOld() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
const auto& appear_arg = swkbd_calc_arg.appear_arg;
const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
.key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
.key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
.key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
frontend.ShowInlineKeyboard(std::move(appear_parameters));
ShowInlineKeyboard(std::move(appear_parameters));
}
ChangeState(SwkbdState::InitializedIsShown);
void SoftwareKeyboard::ShowInlineKeyboardNew() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
? appear_arg.max_text_length
: DEFAULT_MAX_TEXT_LENGTH;
const u32 min_text_length =
appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
.key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
.key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
.key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
.key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
.enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
ShowInlineKeyboard(std::move(appear_parameters));
}
void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
ReplyReleasedUserWordInfo();
}
void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
LOG_DEBUG(Service_AM, "Processing Request: Calc");
ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
sizeof(SwkbdCalcArg));
std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
sizeof(SwkbdCalcArgCommon));
if (swkbd_calc_arg.flags.set_input_text) {
switch (swkbd_calc_arg_common.calc_arg_size) {
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
ASSERT(request_data.size() ==
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
std::memcpy(&swkbd_calc_arg_old,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgOld));
RequestCalcOld();
break;
case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
ASSERT(request_data.size() ==
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
std::memcpy(&swkbd_calc_arg_new,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgNew));
RequestCalcNew();
break;
default:
UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
ASSERT(request_data.size() >=
sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
std::memcpy(&swkbd_calc_arg_new,
request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
sizeof(SwkbdCalcArgNew));
RequestCalcNew();
break;
}
}
void SoftwareKeyboard::RequestCalcOld() {
if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
}
if (swkbd_calc_arg.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg.cursor_position;
if (swkbd_calc_arg_common.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg_old.cursor_position;
}
if (swkbd_calc_arg.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg.utf8_mode;
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg.flags.unset_customize_dic) {
swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg.flags.unset_user_word_info) {
swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
InitializeFrontendKeyboard();
if (swkbd_state == SwkbdState::NotInitialized &&
swkbd_calc_arg_common.flags.set_initialize_arg) {
InitializeFrontendInlineKeyboardOld();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
if (!swkbd_calc_arg.flags.set_initialize_arg &&
(swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
(swkbd_calc_arg_common.flags.set_input_text ||
swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
ShowInlineKeyboard();
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
ShowInlineKeyboardOld();
return;
}
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}
}
void SoftwareKeyboard::RequestCalcNew() {
if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
}
if (swkbd_calc_arg_common.flags.set_cursor_position) {
current_cursor_position = swkbd_calc_arg_new.cursor_position;
}
if (swkbd_calc_arg_common.flags.set_utf8_mode) {
inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
if (swkbd_state == SwkbdState::NotInitialized &&
swkbd_calc_arg_common.flags.set_initialize_arg) {
InitializeFrontendInlineKeyboardNew();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
(swkbd_calc_arg_common.flags.set_input_text ||
swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
ShowInlineKeyboardNew();
return;
}
if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}
@@ -13,6 +13,11 @@ namespace Core {
class System;
}
namespace Core::Frontend {
struct KeyboardInitializeParameters;
struct InlineAppearParameters;
} // namespace Core::Frontend
namespace Service::AM::Applets {
class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
void ChangeState(SwkbdState state);
/**
* Signals the frontend to initialize the software keyboard with common parameters.
* This initializes either the normal software keyboard or the inline software keyboard
* depending on the state of is_background.
* Signals the frontend to initialize the normal software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
* Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
*/
void InitializeFrontendKeyboard();
void InitializeFrontendNormalKeyboard();
/**
* Signals the frontend to initialize the inline software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the ShowInlineKeyboard() to cause the keyboard to appear.
*/
void InitializeFrontendInlineKeyboard(
Core::Frontend::KeyboardInitializeParameters initialize_parameters);
void InitializeFrontendInlineKeyboardOld();
void InitializeFrontendInlineKeyboardNew();
/// Signals the frontend to show the normal software keyboard.
void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
std::u16string text_check_message);
/// Signals the frontend to show the inline software keyboard.
void ShowInlineKeyboard();
void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
void ShowInlineKeyboardOld();
void ShowInlineKeyboardNew();
/// Signals the frontend to hide the inline software keyboard.
void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
void RequestCalc(const std::vector<u8>& request_data);
void RequestCalcOld();
void RequestCalcNew();
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
SwkbdState swkbd_state{SwkbdState::NotInitialized};
SwkbdInitializeArg swkbd_initialize_arg;
SwkbdCalcArg swkbd_calc_arg;
SwkbdCalcArgCommon swkbd_calc_arg_common;
SwkbdCalcArgOld swkbd_calc_arg_old;
SwkbdCalcArgNew swkbd_calc_arg_new;
bool use_changed_string_v2{false};
bool use_moved_cursor_v2{false};
bool inline_use_utf8{false};
@@ -10,6 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
namespace Service::AM::Applets {
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
};
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
struct SwkbdAppearArg {
struct SwkbdAppearArgOld {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
@@ -229,19 +230,46 @@ struct SwkbdAppearArg {
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
INSERT_PADDING_WORDS(6);
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
};
static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
struct SwkbdCalcArg {
struct SwkbdAppearArgNew {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
char16_t right_optional_symbol_key{};
bool use_prediction{};
bool disable_cancel_button{};
SwkbdKeyDisableFlags key_disable_flags{};
u32 max_text_length{};
u32 min_text_length{};
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
u64 start_sampling_number{};
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
struct SwkbdCalcArgCommon {
u32 unknown{};
u16 calc_arg_size{};
INSERT_PADDING_BYTES(2);
SwkbdCalcArgFlags flags{};
SwkbdInitializeArg initialize_arg{};
};
static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
struct SwkbdCalcArgOld {
f32 volume{};
s32 cursor_position{};
SwkbdAppearArg appear_arg{};
SwkbdAppearArgOld appear_arg{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
@@ -265,7 +293,39 @@ struct SwkbdCalcArg {
u8 se_group{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgOld has incorrect size.");
struct SwkbdCalcArgNew {
SwkbdAppearArgNew appear_arg{};
f32 volume{};
s32 cursor_position{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
bool enable_backspace_button{};
INSERT_PADDING_BYTES(3);
bool key_top_as_floating{};
bool footer_scalable{};
bool alpha_enabled_in_input_mode{};
u8 input_mode_fade_type{};
bool disable_touch{};
bool disable_hardware_keyboard{};
INSERT_PADDING_BYTES(8);
f32 key_top_scale_x{};
f32 key_top_scale_y{};
f32 key_top_translate_x{};
f32 key_top_translate_y{};
f32 key_top_bg_alpha{};
f32 footer_bg_alpha{};
f32 balloon_scale{};
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgNew has incorrect size.");
struct SwkbdChangedStringArg {
u32 text_length{};
@@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
return Status::BadValue;
}
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
if (status != Status::NoBufferAvailable) {
@@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
}
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
status != Status::NoError) {
@@ -1,206 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service::NVFlinger {
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
KernelHelpers::ServiceContext& service_context_)
: id(id_), layer_id(layer_id_), service_context{service_context_} {
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
}
BufferQueue::~BufferQueue() {
service_context.CloseEvent(buffer_wait_event);
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
ASSERT(slot < buffer_slots);
LOG_WARNING(Service, "Adding graphics buffer {}", slot);
{
std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
free_buffers_condition.notify_one();
buffers[slot] = {
.slot = slot,
.status = Buffer::Status::Free,
.igbp_buffer = igbp_buffer,
.transform = {},
.crop_rect = {},
.swap_interval = 0,
.multi_fence = {},
};
buffer_wait_event->GetWritableEvent().Signal();
}
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
u32 height) {
// Wait for first request before trying to dequeue
{
std::unique_lock lock{free_buffers_mutex};
free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
}
if (!is_connect) {
// Buffer was disconnected while the thread was blocked, this is most likely due to
// emulation being stopped
return std::nullopt;
}
std::unique_lock lock{free_buffers_mutex};
auto f_itr = free_buffers.begin();
auto slot = buffers.size();
while (f_itr != free_buffers.end()) {
const Buffer& buffer = buffers[*f_itr];
if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
buffer.igbp_buffer.height == height) {
slot = *f_itr;
free_buffers.erase(f_itr);
break;
}
++f_itr;
}
if (slot == buffers.size()) {
return std::nullopt;
}
buffers[slot].status = Buffer::Status::Dequeued;
return {{buffers[slot].slot, &buffers[slot].multi_fence}};
}
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
ASSERT(slot < buffers.size());
ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
ASSERT(buffers[slot].slot == slot);
return buffers[slot].igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
Service::Nvidia::MultiFence& multi_fence) {
ASSERT(slot < buffers.size());
ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
ASSERT(buffers[slot].slot == slot);
buffers[slot].status = Buffer::Status::Queued;
buffers[slot].transform = transform;
buffers[slot].crop_rect = crop_rect;
buffers[slot].swap_interval = swap_interval;
buffers[slot].multi_fence = multi_fence;
std::unique_lock lock{queue_sequence_mutex};
queue_sequence.push_back(slot);
}
void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
ASSERT(slot < buffers.size());
ASSERT(buffers[slot].status != Buffer::Status::Free);
ASSERT(buffers[slot].slot == slot);
buffers[slot].status = Buffer::Status::Free;
buffers[slot].multi_fence = multi_fence;
buffers[slot].swap_interval = 0;
{
std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
free_buffers_condition.notify_one();
buffer_wait_event->GetWritableEvent().Signal();
}
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
std::unique_lock lock{queue_sequence_mutex};
std::size_t buffer_slot = buffers.size();
// Iterate to find a queued buffer matching the requested slot.
while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
const auto slot = static_cast<std::size_t>(queue_sequence.front());
ASSERT(slot < buffers.size());
if (buffers[slot].status == Buffer::Status::Queued) {
ASSERT(buffers[slot].slot == slot);
buffer_slot = slot;
}
queue_sequence.pop_front();
}
if (buffer_slot == buffers.size()) {
return std::nullopt;
}
buffers[buffer_slot].status = Buffer::Status::Acquired;
return {{buffers[buffer_slot]}};
}
void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(slot < buffers.size());
ASSERT(buffers[slot].status == Buffer::Status::Acquired);
ASSERT(buffers[slot].slot == slot);
buffers[slot].status = Buffer::Status::Free;
{
std::unique_lock lock{free_buffers_mutex};
free_buffers.push_back(slot);
}
free_buffers_condition.notify_one();
buffer_wait_event->GetWritableEvent().Signal();
}
void BufferQueue::Connect() {
std::unique_lock lock{queue_sequence_mutex};
queue_sequence.clear();
is_connect = true;
}
void BufferQueue::Disconnect() {
buffers.fill({});
{
std::unique_lock lock{queue_sequence_mutex};
queue_sequence.clear();
}
buffer_wait_event->GetWritableEvent().Signal();
is_connect = false;
free_buffers_condition.notify_one();
}
u32 BufferQueue::Query(QueryType type) {
LOG_WARNING(Service, "(STUBBED) called type={}", type);
switch (type) {
case QueryType::NativeWindowFormat:
return static_cast<u32>(PixelFormat::RGBA8888);
case QueryType::NativeWindowWidth:
case QueryType::NativeWindowHeight:
break;
case QueryType::NativeWindowMinUndequeuedBuffers:
return 0;
case QueryType::NativeWindowConsumerUsageBits:
return 0;
}
UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
return 0;
}
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
return buffer_wait_event->GetWritableEvent();
}
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
return buffer_wait_event->GetReadableEvent();
}
} // namespace Service::NVFlinger
@@ -1,154 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <condition_variable>
#include <list>
#include <mutex>
#include <optional>
#include "common/common_funcs.h"
#include "common/math_util.h"
#include "common/swap.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent;
class KWritableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
} // namespace Service::KernelHelpers
namespace Service::NVFlinger {
constexpr u32 buffer_slots = 0x40;
struct IGBPBuffer {
u32_le magic;
u32_le width;
u32_le height;
u32_le stride;
u32_le format;
u32_le usage;
INSERT_PADDING_WORDS(1);
u32_le index;
INSERT_PADDING_WORDS(3);
u32_le gpu_buffer_id;
INSERT_PADDING_WORDS(6);
u32_le external_format;
INSERT_PADDING_WORDS(10);
u32_le nvmap_handle;
u32_le offset;
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
class BufferQueue final {
public:
enum class QueryType {
NativeWindowWidth = 0,
NativeWindowHeight = 1,
NativeWindowFormat = 2,
/// The minimum number of buffers that must remain un-dequeued after a buffer has been
/// queued
NativeWindowMinUndequeuedBuffers = 3,
/// The consumer gralloc usage bits currently set by the consumer
NativeWindowConsumerUsageBits = 10,
};
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
KernelHelpers::ServiceContext& service_context_);
~BufferQueue();
enum class BufferTransformFlags : u32 {
/// No transform flags are set
Unset = 0x00,
/// Flip source image horizontally (around the vertical axis)
FlipH = 0x01,
/// Flip source image vertically (around the horizontal axis)
FlipV = 0x02,
/// Rotate source image 90 degrees clockwise
Rotate90 = 0x04,
/// Rotate source image 180 degrees
Rotate180 = 0x03,
/// Rotate source image 270 degrees clockwise
Rotate270 = 0x07,
};
enum class PixelFormat : u32 {
RGBA8888 = 1,
RGBX8888 = 2,
RGB888 = 3,
RGB565 = 4,
BGRA8888 = 5,
RGBA5551 = 6,
RRGBA4444 = 7,
};
struct Buffer {
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
u32 slot;
Status status = Status::Free;
IGBPBuffer igbp_buffer;
BufferTransformFlags transform;
Common::Rectangle<int> crop_rect;
u32 swap_interval;
Service::Nvidia::MultiFence multi_fence;
};
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
Service::Nvidia::MultiFence& multi_fence);
void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
void Connect();
void Disconnect();
u32 Query(QueryType type);
u32 GetId() const {
return id;
}
bool IsConnected() const {
return is_connect;
}
Kernel::KWritableEvent& GetWritableBufferWaitEvent();
Kernel::KReadableEvent& GetBufferWaitEvent();
private:
BufferQueue(const BufferQueue&) = delete;
u32 id{};
u64 layer_id{};
std::atomic_bool is_connect{};
std::list<u32> free_buffers;
std::array<Buffer, buffer_slots> buffers;
std::list<u32> queue_sequence;
Kernel::KEvent* buffer_wait_event{};
std::mutex free_buffers_mutex;
std::condition_variable free_buffers_condition;
std::mutex queue_sequence_mutex;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::NVFlinger
@@ -20,122 +20,102 @@ BufferQueueConsumer::~BufferQueueConsumer() = default;
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
std::chrono::nanoseconds expected_present,
u64 max_frame_number) {
s32 num_dropped_buffers{};
std::scoped_lock lock(core->mutex);
std::shared_ptr<IProducerListener> listener;
{
std::unique_lock lock(core->mutex);
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
const s32 num_acquired_buffers{
static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
return slot.buffer_state == BufferState::Acquired;
}))};
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
const s32 num_acquired_buffers{
static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
return slot.buffer_state == BufferState::Acquired;
}))};
if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
num_acquired_buffers, core->max_acquired_buffer_count);
return Status::InvalidOperation;
}
if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
num_acquired_buffers, core->max_acquired_buffer_count);
return Status::InvalidOperation;
}
// Check if the queue is empty.
if (core->queue.empty()) {
return Status::NoBufferAvailable;
}
// Check if the queue is empty.
if (core->queue.empty()) {
return Status::NoBufferAvailable;
}
auto front(core->queue.begin());
auto front(core->queue.begin());
// If expected_present is specified, we may not want to return a buffer yet.
if (expected_present.count() != 0) {
constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
// If expected_present is specified, we may not want to return a buffer yet.
if (expected_present.count() != 0) {
constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
// The expected_present argument indicates when the buffer is expected to be presented
// on-screen.
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
const auto& buffer_item{core->queue[1]};
// The expected_present argument indicates when the buffer is expected to be
// presented on-screen.
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
const auto& buffer_item{core->queue[1]};
// If dropping entry[0] would leave us with a buffer that the consumer is not yet
// ready for, don't drop it.
if (max_frame_number && buffer_item.frame_number > max_frame_number) {
break;
}
// If entry[1] is timely, drop entry[0] (and repeat).
const auto desired_present = buffer_item.timestamp;
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
desired_present > expected_present.count()) {
// This buffer is set to display in the near future, or desired_present is
// garbage.
LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
expected_present.count());
break;
}
LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
expected_present.count(), core->queue.size());
if (core->StillTracking(*front)) {
// Front buffer is still in mSlots, so mark the slot as free
slots[front->slot].buffer_state = BufferState::Free;
core->free_buffers.push_back(front->slot);
listener = core->connected_producer_listener;
++num_dropped_buffers;
}
core->queue.erase(front);
front = core->queue.begin();
// If dropping entry[0] would leave us with a buffer that the consumer is not yet ready
// for, don't drop it.
if (max_frame_number && buffer_item.frame_number > max_frame_number) {
break;
}
// See if the front buffer is ready to be acquired.
const auto desired_present = front->timestamp;
const auto buffer_is_due =
desired_present <= expected_present.count() ||
desired_present > expected_present.count() + MAX_REASONABLE_NSEC;
const auto consumer_is_ready =
max_frame_number > 0 ? front->frame_number <= max_frame_number : true;
if (!buffer_is_due || !consumer_is_ready) {
LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
// If entry[1] is timely, drop entry[0] (and repeat).
const auto desired_present = buffer_item.timestamp;
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
desired_present > expected_present.count()) {
// This buffer is set to display in the near future, or desired_present is garbage.
LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
expected_present.count());
return Status::PresentLater;
break;
}
LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
expected_present.count(), core->queue.size());
if (core->StillTracking(*front)) {
// Front buffer is still in mSlots, so mark the slot as free
slots[front->slot].buffer_state = BufferState::Free;
}
core->queue.erase(front);
front = core->queue.begin();
}
// See if the front buffer is ready to be acquired.
const auto desired_present = front->timestamp;
if (desired_present > expected_present.count() &&
desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
expected_present.count());
return Status::PresentLater;
}
const auto slot = front->slot;
*out_buffer = *front;
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
// If the front buffer is still being tracked, update its slot state
if (core->StillTracking(*front)) {
slots[slot].acquire_called = true;
slots[slot].needs_cleanup_on_release = false;
slots[slot].buffer_state = BufferState::Acquired;
slots[slot].fence = Fence::NoFence();
}
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr
// to avoid unnecessarily remapping this buffer on the consumer side.
if (out_buffer->acquire_called) {
out_buffer->graphic_buffer = nullptr;
}
core->queue.erase(front);
// We might have freed a slot while dropping old buffers, or the producer may be blocked
// waiting for the number of buffers in the queue to decrease.
core->SignalDequeueCondition();
LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
expected_present.count());
}
if (listener != nullptr) {
for (s32 i = 0; i < num_dropped_buffers; ++i) {
listener->OnBufferReleased();
}
const auto slot = front->slot;
*out_buffer = *front;
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
// If the front buffer is still being tracked, update its slot state
if (core->StillTracking(*front)) {
slots[slot].acquire_called = true;
slots[slot].needs_cleanup_on_release = false;
slots[slot].buffer_state = BufferState::Acquired;
slots[slot].fence = Fence::NoFence();
}
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
// avoid unnecessarily remapping this buffer on the consumer side.
if (out_buffer->acquire_called) {
out_buffer->graphic_buffer = nullptr;
}
core->queue.erase(front);
// We might have freed a slot while dropping old buffers, or the producer may be blocked
// waiting for the number of buffers in the queue to decrease.
core->SignalDequeueCondition();
return Status::NoError;
}
@@ -147,7 +127,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
std::shared_ptr<IProducerListener> listener;
{
std::unique_lock lock(core->mutex);
std::scoped_lock lock(core->mutex);
// If the frame number has changed because the buffer has been reallocated, we can ignore
// this ReleaseBuffer for the old buffer.
@@ -170,8 +150,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
slots[slot].fence = release_fence;
slots[slot].buffer_state = BufferState::Free;
core->free_buffers.push_back(slot);
listener = core->connected_producer_listener;
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
@@ -189,7 +167,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
return Status::BadValue;
}
core->dequeue_condition.notify_all();
core->SignalDequeueCondition();
}
// Call back without lock held
@@ -209,7 +187,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -10,16 +10,12 @@
namespace Service::android {
BufferQueueCore::BufferQueueCore() : lock{mutex, std::defer_lock} {
for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
free_slots.insert(slot);
}
}
BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default;
void BufferQueueCore::NotifyShutdown() {
std::unique_lock lk(mutex);
std::scoped_lock lock(mutex);
is_shutting_down = true;
@@ -35,7 +31,7 @@ bool BufferQueueCore::WaitForDequeueCondition() {
return false;
}
dequeue_condition.wait(lock);
dequeue_condition.wait(mutex);
return true;
}
@@ -86,26 +82,15 @@ s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
void BufferQueueCore::FreeBufferLocked(s32 slot) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
const auto had_buffer = slots[slot].graphic_buffer != nullptr;
slots[slot].graphic_buffer.reset();
if (slots[slot].buffer_state == BufferState::Acquired) {
slots[slot].needs_cleanup_on_release = true;
}
if (slots[slot].buffer_state != BufferState::Free) {
free_slots.insert(slot);
} else if (had_buffer) {
// If the slot was FREE, but we had a buffer, we need to move this slot from the free
// buffers list to the the free slots list.
free_buffers.remove(slot);
free_slots.insert(slot);
}
slots[slot].buffer_state = BufferState::Free;
slots[slot].frame_number = UINT32_MAX;
slots[slot].acquire_called = false;
slots[slot].frame_number = 0;
slots[slot].fence = Fence::NoFence();
}
@@ -126,8 +111,7 @@ bool BufferQueueCore::StillTracking(const BufferItem& item) const {
void BufferQueueCore::WaitWhileAllocatingLocked() const {
while (is_allocating) {
std::unique_lock lk(mutex);
is_allocating_condition.wait(lk);
is_allocating_condition.wait(mutex);
}
}
@@ -49,24 +49,8 @@ private:
bool StillTracking(const BufferItem& item) const;
void WaitWhileAllocatingLocked() const;
private:
class AutoLock final {
public:
AutoLock(std::shared_ptr<BufferQueueCore>& core_) : core{core_} {
core->lock.lock();
}
~AutoLock() {
core->lock.unlock();
}
private:
std::shared_ptr<BufferQueueCore>& core;
};
private:
mutable std::mutex mutex;
mutable std::unique_lock<std::mutex> lock;
bool is_abandoned{};
bool consumer_controlled_by_app{};
std::shared_ptr<IConsumerListener> consumer_listener;
@@ -75,10 +59,8 @@ private:
std::shared_ptr<IProducerListener> connected_producer_listener;
BufferQueueDefs::SlotsType slots{};
std::vector<BufferItem> queue;
std::set<s32> free_slots;
std::list<s32> free_buffers;
s32 override_max_buffer_count{};
mutable std::condition_variable dequeue_condition;
mutable std::condition_variable_any dequeue_condition;
const bool use_async_buffer{}; // This is always disabled on HOS
bool dequeue_buffer_cannot_block{};
PixelFormat default_buffer_format{PixelFormat::Rgba8888};
@@ -90,7 +72,7 @@ private:
u64 frame_counter{};
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable is_allocating_condition;
mutable std::condition_variable_any is_allocating_condition;
bool allow_allocation{true};
u64 buffer_age{};
bool is_shutting_down{};
@@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() {
Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -65,7 +65,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
std::shared_ptr<IConsumerListener> listener;
{
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -156,6 +156,14 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
case BufferState::Acquired:
++acquired_count;
break;
case BufferState::Free:
// We return the oldest of the free buffers to avoid stalling the producer if
// possible, since the consumer may still have pending reads of in-flight buffers
if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
slots[s].frame_number < slots[*found].frame_number) {
*found = s;
}
break;
default:
break;
}
@@ -183,27 +191,12 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
}
}
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
// If we disconnect and reconnect quickly, we can be in a state where our slots are empty
// but we have many buffers in the queue. This can cause us to run out of memory if we
// outrun the consumer. Wait here if it looks like we have too many buffers queued up.
const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
if (too_many_buffers) {
LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
} else {
if (!core->free_buffers.empty()) {
auto slot = core->free_buffers.begin();
*found = *slot;
core->free_buffers.erase(slot);
} else if (core->allow_allocation && !core->free_slots.empty()) {
auto slot = core->free_slots.begin();
// Only return free slots up to the max buffer count
if (*slot < max_buffer_count) {
*found = *slot;
core->free_slots.erase(slot);
}
}
}
// If no buffer is found, or if the queue has too many buffers outstanding, wait for a
@@ -240,7 +233,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
Status return_flags = Status::NoError;
bool attached_by_consumer = false;
{
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
if (format == PixelFormat::NoFormat) {
format = core->default_buffer_format;
@@ -317,12 +310,13 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
}
{
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
}
slots[*out_slot].frame_number = UINT32_MAX;
slots[*out_slot].graphic_buffer = graphic_buffer;
}
}
@@ -339,7 +333,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
Status BufferQueueProducer::DetachBuffer(s32 slot) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
@@ -374,7 +368,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
return Status::BadValue;
}
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
@@ -382,12 +376,21 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
return Status::NoInit;
}
if (core->free_buffers.empty()) {
return Status::NoMemory;
// Find the oldest valid slot
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
slots[s].frame_number < slots[found].frame_number) {
found = s;
}
}
}
const s32 found = core->free_buffers.front();
core->free_buffers.remove(found);
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
return Status::NoMemory;
}
LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
@@ -409,7 +412,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
return Status::BadValue;
}
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
Status return_flags = Status::NoError;
@@ -469,7 +472,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
BufferItem item;
{
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -554,7 +557,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
// mark it as freed
if (core->StillTracking(*front)) {
slots[front->slot].buffer_state = BufferState::Free;
core->free_buffers.push_front(front->slot);
// Reset the frame number of the freed buffer so that it is the first in line to
// be dequeued again
slots[front->slot].frame_number = 0;
}
// Overwrite the droppable buffer with the incoming one
*front = item;
@@ -582,10 +587,9 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
// Call back without the main BufferQueue lock held, but with the callback lock held so we can
// ensure that callbacks occur in order
{
std::unique_lock lock(callback_mutex);
std::scoped_lock lock(callback_mutex);
while (callback_ticket != current_callback_ticket) {
std::unique_lock<std::mutex> lk(callback_mutex);
callback_condition.wait(lk);
callback_condition.wait(callback_mutex);
}
if (frameAvailableListener != nullptr) {
@@ -604,7 +608,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (core->is_abandoned) {
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
@@ -621,8 +625,8 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
return;
}
core->free_buffers.push_front(slot);
slots[slot].buffer_state = BufferState::Free;
slots[slot].frame_number = 0;
slots[slot].fence = fence;
core->SignalDequeueCondition();
@@ -630,7 +634,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
}
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
if (out_value == nullptr) {
LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
@@ -687,7 +691,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
NativeWindowApi api, bool producer_controlled_by_app,
QueueBufferOutput* output) {
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
producer_controlled_by_app);
@@ -745,7 +749,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
std::shared_ptr<IConsumerListener> listener;
{
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
core->WaitWhileAllocatingLocked();
@@ -795,10 +799,11 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
return Status::BadValue;
}
BufferQueueCore::AutoLock lock(core);
std::scoped_lock lock(core->mutex);
slots[slot] = {};
slots[slot].graphic_buffer = buffer;
slots[slot].frame_number = 0;
// Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
// this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
@@ -77,7 +77,7 @@ private:
std::mutex callback_mutex;
s32 next_callback_ticket{};
s32 current_callback_ticket{};
std::condition_variable callback_condition;
std::condition_variable_any callback_condition;
};
} // namespace Service::android
@@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
: consumer{std::move(consumer_)} {}
ConsumerBase::~ConsumerBase() {
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
}
@@ -36,17 +36,17 @@ void ConsumerBase::FreeBufferLocked(s32 slot_index) {
}
void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
void ConsumerBase::OnBuffersReleased() {
std::unique_lock lock(mutex);
std::scoped_lock lock(mutex);
LOG_DEBUG(Service_NVFlinger, "called");
}
+1 -1
View File
@@ -322,7 +322,7 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
+1 -1
View File
@@ -207,7 +207,7 @@ private:
int index) const;
mutable std::mutex mutex;
mutable std::mutex mutex_callback;
mutable std::recursive_mutex mutex_callback;
bool configuring{false};
const std::string input_engine;
int last_callback_key = 0;
+62 -1
View File
@@ -4,6 +4,8 @@
#include <array>
#include <vector>
#include "common/scope_exit.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h"
@@ -59,6 +61,7 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.regs.index_array.first = parameters[3];
maxwell3d.regs.reg_array[0x446] = element_base; // vertex id base?
maxwell3d.regs.index_array.count = parameters[1];
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
maxwell3d.regs.vb_element_base = element_base;
maxwell3d.regs.vb_base_instance = base_instance;
maxwell3d.mme_draw.instance_count = instance_count;
@@ -81,10 +84,67 @@ void HLE_0217920100488FF7(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
}
constexpr std::array<std::pair<u64, HLEFunction>, 3> hle_funcs{{
// Multidraw Indirect
void HLE_3f5e74b9c9a50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
SCOPE_EXIT({
// Clean everything.
maxwell3d.regs.reg_array[0x446] = 0x0; // vertex id base?
maxwell3d.regs.index_array.count = 0;
maxwell3d.regs.vb_element_base = 0x0;
maxwell3d.regs.vb_base_instance = 0x0;
maxwell3d.mme_draw.instance_count = 0;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, 0x0);
maxwell3d.CallMethodFromMME(0x8e5, 0x0);
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
});
const u32 start_indirect = parameters[0];
const u32 end_indirect = parameters[1];
if (start_indirect >= end_indirect) {
// Nothing to do.
return;
}
const auto topology =
static_cast<Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology>(parameters[2]);
maxwell3d.regs.draw.topology.Assign(topology);
const u32 padding = parameters[3];
const std::size_t max_draws = parameters[4];
const u32 indirect_words = 5 + padding;
const std::size_t first_draw = start_indirect;
const std::size_t effective_draws = end_indirect - start_indirect;
const std::size_t last_draw = start_indirect + std::min(effective_draws, max_draws);
for (std::size_t index = first_draw; index < last_draw; index++) {
const std::size_t base = index * indirect_words + 5;
const u32 num_vertices = parameters[base];
const u32 instance_count = parameters[base + 1];
const u32 first_index = parameters[base + 2];
const u32 base_vertex = parameters[base + 3];
const u32 base_instance = parameters[base + 4];
maxwell3d.regs.index_array.first = first_index;
maxwell3d.regs.reg_array[0x446] = base_vertex;
maxwell3d.regs.index_array.count = num_vertices;
maxwell3d.regs.vb_element_base = base_vertex;
maxwell3d.regs.vb_base_instance = base_instance;
maxwell3d.mme_draw.instance_count = instance_count;
maxwell3d.CallMethodFromMME(0x8e3, 0x640);
maxwell3d.CallMethodFromMME(0x8e4, base_vertex);
maxwell3d.CallMethodFromMME(0x8e5, base_instance);
maxwell3d.dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
if (maxwell3d.ShouldExecute()) {
maxwell3d.Rasterizer().Draw(true, true);
}
maxwell3d.mme_draw.current_mode = Engines::Maxwell3D::MMEDrawMode::Undefined;
}
}
constexpr std::array<std::pair<u64, HLEFunction>, 4> hle_funcs{{
{0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
{0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
{0x0217920100488FF7, &HLE_0217920100488FF7},
{0x3f5e74b9c9a50164, &HLE_3f5e74b9c9a50164},
}};
class HLEMacroImpl final : public CachedMacro {
@@ -100,6 +160,7 @@ private:
Engines::Maxwell3D& maxwell3d;
HLEFunction func;
};
} // Anonymous namespace
HLEMacro::HLEMacro(Engines::Maxwell3D& maxwell3d_) : maxwell3d{maxwell3d_} {}
@@ -352,7 +352,7 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
shader_cache.OnCPUWrite(addr, size);
{
std::scoped_lock lock{texture_cache.mutex};
texture_cache.CachedWriteMemory(addr, size);
texture_cache.WriteMemory(addr, size);
}
{
std::scoped_lock lock{buffer_cache.mutex};
@@ -363,10 +363,6 @@ void RasterizerOpenGL::OnCPUWrite(VAddr addr, u64 size) {
void RasterizerOpenGL::SyncGuestHost() {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
shader_cache.SyncGuestHost();
{
std::scoped_lock lock{texture_cache.mutex};
texture_cache.FlushCachedWrites();
}
{
std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.FlushCachedWrites();
@@ -408,7 +408,7 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
pipeline_cache.OnCPUWrite(addr, size);
{
std::scoped_lock lock{texture_cache.mutex};
texture_cache.CachedWriteMemory(addr, size);
texture_cache.WriteMemory(addr, size);
}
{
std::scoped_lock lock{buffer_cache.mutex};
@@ -418,10 +418,6 @@ void RasterizerVulkan::OnCPUWrite(VAddr addr, u64 size) {
void RasterizerVulkan::SyncGuestHost() {
pipeline_cache.SyncGuestHost();
{
std::scoped_lock lock{texture_cache.mutex};
texture_cache.FlushCachedWrites();
}
{
std::scoped_lock lock{buffer_cache.mutex};
buffer_cache.FlushCachedWrites();
@@ -39,9 +39,6 @@ enum class ImageFlagBits : u32 {
Rescaled = 1 << 13,
CheckingRescalable = 1 << 14,
IsRescalable = 1 << 15,
// Cached CPU
CachedCpuModified = 1 << 16, ///< Contents have been modified from the CPU
};
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
+1 -40
View File
@@ -437,23 +437,6 @@ void TextureCache<P>::WriteMemory(VAddr cpu_addr, size_t size) {
});
}
template <class P>
void TextureCache<P>::CachedWriteMemory(VAddr cpu_addr, size_t size) {
const VAddr new_cpu_addr = Common::AlignDown(cpu_addr, CPU_PAGE_SIZE);
const size_t new_size = Common::AlignUp(size + cpu_addr - new_cpu_addr, CPU_PAGE_SIZE);
ForEachImageInRegion(new_cpu_addr, new_size, [this](ImageId image_id, Image& image) {
if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
return;
}
image.flags |= ImageFlagBits::CachedCpuModified;
cached_cpu_invalidate.insert(image_id);
if (True(image.flags & ImageFlagBits::Tracked)) {
UntrackImage(image, image_id);
}
});
}
template <class P>
void TextureCache<P>::DownloadMemory(VAddr cpu_addr, size_t size) {
std::vector<ImageId> images;
@@ -511,18 +494,6 @@ void TextureCache<P>::UnmapGPUMemory(GPUVAddr gpu_addr, size_t size) {
}
}
template <class P>
void TextureCache<P>::FlushCachedWrites() {
for (ImageId image_id : cached_cpu_invalidate) {
Image& image = slot_images[image_id];
if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
image.flags &= ~ImageFlagBits::CachedCpuModified;
image.flags |= ImageFlagBits::CpuModified;
}
}
cached_cpu_invalidate.clear();
}
template <class P>
void TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
@@ -1589,9 +1560,6 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
template <class P>
void TextureCache<P>::TrackImage(ImageBase& image, ImageId image_id) {
ASSERT(False(image.flags & ImageFlagBits::Tracked));
if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
return;
}
image.flags |= ImageFlagBits::Tracked;
if (False(image.flags & ImageFlagBits::Sparse)) {
rasterizer.UpdatePagesCachedCount(image.cpu_addr, image.guest_size_bytes, 1);
@@ -1648,9 +1616,6 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
}
total_used_memory -= Common::AlignUp(tentative_size, 1024);
if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
cached_cpu_invalidate.erase(image_id);
}
const GPUVAddr gpu_addr = image.gpu_addr;
const auto alloc_it = image_allocs_table.find(gpu_addr);
if (alloc_it == image_allocs_table.end()) {
@@ -1817,11 +1782,7 @@ template <class P>
void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool invalidate) {
Image& image = slot_images[image_id];
if (invalidate) {
if (True(image.flags & ImageFlagBits::CachedCpuModified)) {
cached_cpu_invalidate.erase(image_id);
}
image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified |
ImageFlagBits::CachedCpuModified);
image.flags &= ~(ImageFlagBits::CpuModified | ImageFlagBits::GpuModified);
if (False(image.flags & ImageFlagBits::Tracked)) {
TrackImage(image, image_id);
}
@@ -8,7 +8,6 @@
#include <span>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <queue>
@@ -51,9 +50,6 @@ class TextureCache {
/// Address shift for caching images into a hash table
static constexpr u64 PAGE_BITS = 20;
static constexpr u64 CPU_PAGE_BITS = 12;
static constexpr u64 CPU_PAGE_SIZE = 1ULL << CPU_PAGE_BITS;
/// Enables debugging features to the texture cache
static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
/// Implement blits as copies between framebuffers
@@ -140,9 +136,6 @@ public:
/// Mark images in a range as modified from the CPU
void WriteMemory(VAddr cpu_addr, size_t size);
/// Mark images in a range as modified from the CPU
void CachedWriteMemory(VAddr cpu_addr, size_t size);
/// Download contents of host images to guest memory in a region
void DownloadMemory(VAddr cpu_addr, size_t size);
@@ -152,8 +145,6 @@ public:
/// Remove images in a region
void UnmapGPUMemory(GPUVAddr gpu_addr, size_t size);
void FlushCachedWrites();
/// Blit an image with the given parameters
void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Surface& src,
@@ -375,8 +366,6 @@ private:
std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
std::unordered_set<ImageId> cached_cpu_invalidate;
VAddr virtual_invalid_space{};
bool has_deleted_images = false;