mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
37 Commits
v2.5.385
...
mach-excep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eee71d54ef | ||
|
|
43e073a18d | ||
|
|
bc41666d53 | ||
|
|
5b85d6a758 | ||
|
|
1fdc000815 | ||
|
|
3a57bb46ab | ||
|
|
2cb75e60b3 | ||
|
|
600ac6ec4f | ||
|
|
ed0cd628f8 | ||
|
|
33a825c17f | ||
|
|
660a165533 | ||
|
|
bfe2d5abb2 | ||
|
|
d5b36da6b0 | ||
|
|
328cebd5fc | ||
|
|
579cb7bd27 | ||
|
|
aab889535f | ||
|
|
e0362f7879 | ||
|
|
009ae1fb02 | ||
|
|
d40289e977 | ||
|
|
c2fd4af163 | ||
|
|
5bdee3a611 | ||
|
|
cb026a6946 | ||
|
|
cb5124da4b | ||
|
|
7c88af9c73 | ||
|
|
465a31bbd5 | ||
|
|
6deb43bde2 | ||
|
|
8e7dcb83a8 | ||
|
|
51ead1e00f | ||
|
|
27bcb7c29a | ||
|
|
aaed4a4983 | ||
|
|
748f232976 | ||
|
|
a33612cf7d | ||
|
|
5c123f3183 | ||
|
|
d30a7fb991 | ||
|
|
d4f661c27c | ||
|
|
6d05d0220d | ||
|
|
7fab935c2d |
@@ -17,7 +17,7 @@ jobs:
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "Qt: Update Base Translation"
|
||||
commit-message: "[ci skip] Qt: Update Base Translation."
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "PAD: Update to latest controller database"
|
||||
commit-message: "[ci skip] PAD: Update to latest controller database."
|
||||
|
||||
2
.github/workflows/linux_build_flatpak.yml
vendored
2
.github/workflows/linux_build_flatpak.yml
vendored
@@ -153,7 +153,7 @@ jobs:
|
||||
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: ci-artifacts
|
||||
|
||||
6
.github/workflows/linux_build_qt.yml
vendored
6
.github/workflows/linux_build_qt.yml
vendored
@@ -92,7 +92,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -174,7 +174,7 @@ jobs:
|
||||
|
||||
- name: Upload artifact
|
||||
if: inputs.buildAppImage == true
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: ci-artifacts
|
||||
|
||||
6
.github/workflows/macos_build.yml
vendored
6
.github/workflows/macos_build.yml
vendored
@@ -91,7 +91,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -112,7 +112,7 @@ jobs:
|
||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache ccache cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@@ -197,7 +197,7 @@ jobs:
|
||||
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: "*.tar.xz"
|
||||
|
||||
2
.github/workflows/release_cut_new.yml
vendored
2
.github/workflows/release_cut_new.yml
vendored
@@ -168,7 +168,7 @@ jobs:
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
name: Download all Artifacts
|
||||
with:
|
||||
path: ./ci-artifacts/
|
||||
|
||||
6
.github/workflows/windows_build_qt.yml
vendored
6
.github/workflows/windows_build_qt.yml
vendored
@@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
cmake --build build --config Release --target unittests
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: |
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Upload artifact - with symbols
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
|
||||
path: |
|
||||
|
||||
15
3rdparty/rcheevos/CHANGELOG.md
vendored
15
3rdparty/rcheevos/CHANGELOG.md
vendored
@@ -1,3 +1,18 @@
|
||||
# v12.2.0
|
||||
* add rc_client_create_subset_list
|
||||
* add rc_client_begin_fetch_game_titles
|
||||
* greatly improve performance parsing long AddSource chains
|
||||
* don't send pings if not processing frames; allows server to suspend session while emulator is paused
|
||||
* modify validation logic to return most severe error instead of first error found
|
||||
* improve validation warning when 'PauseIf {recall}' attempts to use non-PauseIf Remember
|
||||
* fix rounding error when subtracting floats from integers
|
||||
* fix infinite loop processing 'Remember {recall}' with no modifiers
|
||||
* fix measured value jumping to 0 if all measured-generating alts are paused
|
||||
* fix buffer overflow converting long user names between rc_client_external versions
|
||||
* fix validation warning when adding differently sized values
|
||||
* fix validation warning when ResetIf only applies to hit count inside an AndNext chain
|
||||
* fix validation warning when only last node of modified memref chain differs
|
||||
|
||||
# v12.1.0
|
||||
* add rc_client_get_user_subset_summary
|
||||
* add validation warning for using MeasuredIf without Measured
|
||||
|
||||
2
3rdparty/rcheevos/include/rc_api_info.h
vendored
2
3rdparty/rcheevos/include/rc_api_info.h
vendored
@@ -211,6 +211,8 @@ typedef struct rc_api_game_title_entry_t {
|
||||
const char* title;
|
||||
/* The image name for the game badge */
|
||||
const char* image_name;
|
||||
/* The URL for the game badge image */
|
||||
const char* image_url;
|
||||
}
|
||||
rc_api_game_title_entry_t;
|
||||
|
||||
|
||||
54
3rdparty/rcheevos/include/rc_client.h
vendored
54
3rdparty/rcheevos/include/rc_client.h
vendored
@@ -364,6 +364,22 @@ RC_EXPORT const rc_client_subset_t* RC_CCONV rc_client_get_subset_info(rc_client
|
||||
|
||||
RC_EXPORT void RC_CCONV rc_client_get_user_subset_summary(const rc_client_t* client, uint32_t subset_id, rc_client_user_game_summary_t* summary);
|
||||
|
||||
typedef struct rc_client_subset_list_t {
|
||||
const rc_client_subset_t** subsets;
|
||||
uint32_t num_subsets;
|
||||
} rc_client_subset_list_t;
|
||||
|
||||
/**
|
||||
* Creates a list of subsets for the currently loaded game.
|
||||
* Returns an allocated list that must be free'd by calling rc_client_destroy_subset_list.
|
||||
*/
|
||||
RC_EXPORT rc_client_subset_list_t* RC_CCONV rc_client_create_subset_list(rc_client_t* client);
|
||||
|
||||
/**
|
||||
* Destroys a list allocated by rc_client_create_subset_list_list.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_subset_list(rc_client_subset_list_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Fetch Game Hashes |
|
||||
\*****************************************************************************/
|
||||
@@ -398,6 +414,42 @@ RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_hash_library(
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_hash_library(rc_client_hash_library_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Fetch Game Titles |
|
||||
\*****************************************************************************/
|
||||
|
||||
typedef struct rc_client_game_title_entry_t {
|
||||
uint32_t game_id;
|
||||
const char* title;
|
||||
char badge_name[16];
|
||||
const char* badge_url;
|
||||
} rc_client_game_title_entry_t;
|
||||
|
||||
typedef struct rc_client_game_title_list_t {
|
||||
rc_client_game_title_entry_t* entries;
|
||||
uint32_t num_entries;
|
||||
} rc_client_game_title_list_t;
|
||||
|
||||
/**
|
||||
* Callback that is fired when a game titles request completes. list may be null if the query failed.
|
||||
*/
|
||||
typedef void(RC_CCONV* rc_client_fetch_game_titles_callback_t)(int result, const char* error_message,
|
||||
rc_client_game_title_list_t* list, rc_client_t* client,
|
||||
void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Starts an asynchronous request for titles and badge names for the specified games.
|
||||
* The caller must provide an array of game IDs and the number of IDs in the array.
|
||||
*/
|
||||
RC_EXPORT rc_client_async_handle_t* RC_CCONV rc_client_begin_fetch_game_titles(
|
||||
rc_client_t* client, const uint32_t* game_ids, uint32_t num_game_ids,
|
||||
rc_client_fetch_game_titles_callback_t callback, void* callback_userdata);
|
||||
|
||||
/**
|
||||
* Destroys a previously-allocated result from the rc_client_begin_fetch_game_titles() callback.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_game_title_list(rc_client_game_title_list_t* list);
|
||||
|
||||
/*****************************************************************************\
|
||||
| Achievements |
|
||||
\*****************************************************************************/
|
||||
@@ -503,7 +555,7 @@ enum {
|
||||
RC_EXPORT rc_client_achievement_list_t* RC_CCONV rc_client_create_achievement_list(rc_client_t* client, int category, int grouping);
|
||||
|
||||
/**
|
||||
* Destroys a list allocated by rc_client_get_achievement_list.
|
||||
* Destroys a list allocated by rc_client_create_achievement_list.
|
||||
*/
|
||||
RC_EXPORT void RC_CCONV rc_client_destroy_achievement_list(rc_client_achievement_list_t* list);
|
||||
|
||||
|
||||
2
3rdparty/rcheevos/include/rc_runtime_types.h
vendored
2
3rdparty/rcheevos/include/rc_runtime_types.h
vendored
@@ -173,6 +173,8 @@ enum {
|
||||
RC_OPERATOR_SUB,
|
||||
|
||||
RC_OPERATOR_SUB_PARENT, /* internal use */
|
||||
RC_OPERATOR_ADD_ACCUMULATOR, /* internal use */
|
||||
RC_OPERATOR_SUB_ACCUMULATOR, /* internal use */
|
||||
RC_OPERATOR_INDIRECT_READ /* internal use */
|
||||
};
|
||||
|
||||
|
||||
7
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
7
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
@@ -448,7 +448,8 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
||||
rc_json_field_t entry_fields[] = {
|
||||
RC_JSON_NEW_FIELD("ID"),
|
||||
RC_JSON_NEW_FIELD("Title"),
|
||||
RC_JSON_NEW_FIELD("ImageIcon")
|
||||
RC_JSON_NEW_FIELD("ImageIcon"),
|
||||
RC_JSON_NEW_FIELD("ImageUrl")
|
||||
};
|
||||
|
||||
memset(response, 0, sizeof(*response));
|
||||
@@ -482,6 +483,10 @@ int rc_api_process_fetch_game_titles_server_response(rc_api_fetch_game_titles_re
|
||||
if (!rc_json_get_required_string(&entry->image_name, &response->response, &entry_fields[2], "ImageIcon"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
rc_json_get_optional_string(&entry->image_url, &response->response, &entry_fields[3], "ImageUrl", "");
|
||||
if (!entry->image_url[0])
|
||||
entry->image_url = rc_api_build_avatar_url(&response->response.buffer, RC_IMAGE_TYPE_GAME, entry->image_name);
|
||||
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
|
||||
325
3rdparty/rcheevos/src/rc_client.c
vendored
325
3rdparty/rcheevos/src/rc_client.c
vendored
@@ -1686,6 +1686,67 @@ static void rc_client_free_pending_media(rc_client_pending_media_t* pending_medi
|
||||
free(pending_media);
|
||||
}
|
||||
|
||||
static void rc_client_log_active_assets(rc_client_t* client)
|
||||
{
|
||||
uint32_t num_achievements;
|
||||
uint32_t num_active_achievements;
|
||||
uint32_t num_unsupported_achievements;
|
||||
uint32_t num_leaderboards;
|
||||
uint32_t num_unsupported_leaderboards;
|
||||
const rc_client_achievement_info_t* ach;
|
||||
const rc_client_achievement_info_t* ach_stop;
|
||||
const rc_client_leaderboard_info_t* lbd;
|
||||
const rc_client_leaderboard_info_t* lbd_stop;
|
||||
|
||||
const rc_client_subset_info_t* subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
num_achievements = 0;
|
||||
num_active_achievements = 0;
|
||||
num_unsupported_achievements = 0;
|
||||
num_leaderboards = 0;
|
||||
num_unsupported_leaderboards = 0;
|
||||
|
||||
ach = subset->achievements;
|
||||
ach_stop = ach + subset->public_.num_achievements;
|
||||
for (; ach < ach_stop; ++ach) {
|
||||
if (ach->public_.category == RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE) {
|
||||
++num_achievements;
|
||||
if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE)
|
||||
++num_active_achievements;
|
||||
else if (ach->public_.state == RC_CLIENT_ACHIEVEMENT_STATE_DISABLED)
|
||||
++num_unsupported_achievements;
|
||||
}
|
||||
}
|
||||
|
||||
lbd = subset->leaderboards;
|
||||
lbd_stop = lbd + subset->public_.num_leaderboards;
|
||||
for (; lbd < lbd_stop; ++lbd) {
|
||||
++num_leaderboards;
|
||||
if (lbd->public_.state == RC_CLIENT_LEADERBOARD_STATE_DISABLED)
|
||||
++num_unsupported_leaderboards;
|
||||
}
|
||||
|
||||
if (num_unsupported_achievements) {
|
||||
if (num_unsupported_leaderboards) {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards (%u unsupported)",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active (%u unsupported), %u leaderboards",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_unsupported_achievements, num_leaderboards);
|
||||
}
|
||||
}
|
||||
else if (num_unsupported_leaderboards) {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards (%u unsupported)",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards, num_unsupported_leaderboards);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Set %u: %u/%u achievements active, %u leaderboards",
|
||||
subset->public_.id, num_active_achievements, num_achievements, num_leaderboards);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: address validation uses the read_memory callback to make sure the client
|
||||
* will return data for the requested address. As such, this function must
|
||||
* respect the `client->state.allow_background_memory_reads setting. Use
|
||||
@@ -1720,10 +1781,13 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
||||
|
||||
/* make the loaded game active if another game is not aleady being loaded. */
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
if (client->state.load == load_state)
|
||||
if (client->state.load == load_state) {
|
||||
client->game = load_state->game;
|
||||
else
|
||||
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||
}
|
||||
else {
|
||||
load_state->progress = RC_CLIENT_LOAD_GAME_STATE_ABORTED;
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (load_state->progress != RC_CLIENT_LOAD_GAME_STATE_ABORTED) {
|
||||
@@ -1807,6 +1871,9 @@ static void rc_client_activate_game(rc_client_load_state_t* load_state, rc_api_s
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Game %u loaded, hardcore %s%s", load_state->game->public_.id,
|
||||
client->state.hardcore ? "enabled" : "disabled",
|
||||
(client->state.spectator_mode != RC_CLIENT_SPECTATOR_MODE_OFF) ? ", spectating" : "");
|
||||
|
||||
if (client->state.log_level >= RC_CLIENT_LOG_LEVEL_INFO)
|
||||
rc_client_log_active_assets(client);
|
||||
}
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Subset %u loaded", load_state->subset->public_.id);
|
||||
@@ -2352,6 +2419,7 @@ static int rc_client_attach_load_state(rc_client_t* client, rc_client_load_state
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
client->state.load = load_state;
|
||||
client->state.frames_processed = client->state.frames_at_last_ping = 0;
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
else if (client->state.load != load_state) {
|
||||
@@ -3483,6 +3551,58 @@ const rc_client_subset_t* rc_client_get_subset_info(rc_client_t* client, uint32_
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc_client_subset_list_t* rc_client_create_subset_list(rc_client_t* client)
|
||||
{
|
||||
rc_client_subset_list_info_t* list;
|
||||
const rc_client_subset_info_t* subset;
|
||||
const rc_client_subset_t** subset_ptr;
|
||||
const uint32_t list_size = RC_ALIGN(sizeof(*list));
|
||||
uint32_t num_subsets = 0;
|
||||
|
||||
if (!client)
|
||||
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->create_subset_list)
|
||||
return (rc_client_subset_list_t*)client->state.external_client->create_subset_list();
|
||||
#endif
|
||||
|
||||
if (!client->game)
|
||||
return (rc_client_subset_list_t*)calloc(1, list_size);
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
|
||||
subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
if (subset->active)
|
||||
num_subsets++;
|
||||
}
|
||||
|
||||
list = (rc_client_subset_list_info_t*)malloc(list_size + num_subsets * sizeof(rc_client_subset_t*));
|
||||
list->public_.subsets = subset_ptr = (const rc_client_subset_t**)((uint8_t*)list + list_size);
|
||||
|
||||
subset = client->game->subsets;
|
||||
for (; subset; subset = subset->next) {
|
||||
if (subset->active)
|
||||
*subset_ptr++ = &subset->public_;
|
||||
}
|
||||
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
list->destroy_func = NULL;
|
||||
list->public_.num_subsets = (uint32_t)(subset_ptr - list->public_.subsets);
|
||||
return &list->public_;
|
||||
}
|
||||
|
||||
void rc_client_destroy_subset_list(rc_client_subset_list_t* list)
|
||||
{
|
||||
rc_client_subset_list_info_t* info = (rc_client_subset_list_info_t*)list;
|
||||
if (info->destroy_func)
|
||||
info->destroy_func(info);
|
||||
else
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Fetch Game Hashes ===== */
|
||||
|
||||
typedef struct rc_client_fetch_hash_library_callback_data_t {
|
||||
@@ -3595,6 +3715,158 @@ void rc_client_destroy_hash_library(rc_client_hash_library_t* list)
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Fetch Game Titles ===== */
|
||||
|
||||
typedef struct rc_client_fetch_game_titles_callback_data_t {
|
||||
rc_client_t* client;
|
||||
rc_client_fetch_game_titles_callback_t callback;
|
||||
void* callback_userdata;
|
||||
rc_client_async_handle_t async_handle;
|
||||
} rc_client_fetch_game_titles_callback_data_t;
|
||||
|
||||
static void rc_client_fetch_game_titles_callback(const rc_api_server_response_t* server_response, void* callback_data)
|
||||
{
|
||||
rc_client_fetch_game_titles_callback_data_t* titles_callback_data =
|
||||
(rc_client_fetch_game_titles_callback_data_t*)callback_data;
|
||||
rc_client_t* client = titles_callback_data->client;
|
||||
rc_api_fetch_game_titles_response_t titles_response;
|
||||
const char* error_message;
|
||||
int result;
|
||||
|
||||
result = rc_client_end_async(client, &titles_callback_data->async_handle);
|
||||
if (result) {
|
||||
if (result != RC_CLIENT_ASYNC_DESTROYED)
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Fetch game titles aborted");
|
||||
|
||||
free(titles_callback_data);
|
||||
return;
|
||||
}
|
||||
|
||||
result = rc_api_process_fetch_game_titles_server_response(&titles_response, server_response);
|
||||
error_message =
|
||||
rc_client_server_error_message(&result, server_response->http_status_code, &titles_response.response);
|
||||
if (error_message) {
|
||||
RC_CLIENT_LOG_ERR_FORMATTED(client, "Fetch game titles failed: %s", error_message);
|
||||
titles_callback_data->callback(result, error_message, NULL, client, titles_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_game_title_list_t* list;
|
||||
size_t strings_size = 0;
|
||||
const rc_api_game_title_entry_t* src;
|
||||
const rc_api_game_title_entry_t* stop;
|
||||
size_t list_size;
|
||||
|
||||
/* calculate string buffer size */
|
||||
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src) {
|
||||
if (src->title)
|
||||
strings_size += strlen(src->title) + 1;
|
||||
if (src->image_url)
|
||||
strings_size += strlen(src->image_url) + 1;
|
||||
}
|
||||
|
||||
list_size = sizeof(*list) + sizeof(rc_client_game_title_entry_t) * titles_response.num_entries + strings_size;
|
||||
list = (rc_client_game_title_list_t*)malloc(list_size);
|
||||
if (!list) {
|
||||
titles_callback_data->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client,
|
||||
titles_callback_data->callback_userdata);
|
||||
} else {
|
||||
rc_client_game_title_entry_t* entry = list->entries =
|
||||
(rc_client_game_title_entry_t*)((uint8_t*)list + sizeof(*list));
|
||||
char* strings = (char*)((uint8_t*)list + sizeof(*list) +
|
||||
sizeof(rc_client_game_title_entry_t) * titles_response.num_entries);
|
||||
|
||||
for (src = titles_response.entries, stop = src + titles_response.num_entries; src < stop; ++src, ++entry) {
|
||||
entry->game_id = src->id;
|
||||
|
||||
if (src->title) {
|
||||
const size_t len = strlen(src->title) + 1;
|
||||
entry->title = strings;
|
||||
memcpy(strings, src->title, len);
|
||||
strings += len;
|
||||
} else {
|
||||
entry->title = NULL;
|
||||
}
|
||||
|
||||
if (src->image_name)
|
||||
snprintf(entry->badge_name, sizeof(entry->badge_name), "%s", src->image_name);
|
||||
else
|
||||
entry->badge_name[0] = '\0';
|
||||
|
||||
if (src->image_url) {
|
||||
const size_t len = strlen(src->image_url) + 1;
|
||||
entry->badge_url = strings;
|
||||
memcpy(strings, src->image_url, len);
|
||||
strings += len;
|
||||
}
|
||||
else {
|
||||
entry->badge_url = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
list->num_entries = titles_response.num_entries;
|
||||
|
||||
titles_callback_data->callback(RC_OK, NULL, list, client, titles_callback_data->callback_userdata);
|
||||
}
|
||||
}
|
||||
|
||||
rc_api_destroy_fetch_game_titles_response(&titles_response);
|
||||
free(titles_callback_data);
|
||||
}
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_fetch_game_titles(rc_client_t* client, const uint32_t* game_ids,
|
||||
uint32_t num_game_ids,
|
||||
rc_client_fetch_game_titles_callback_t callback,
|
||||
void* callback_userdata)
|
||||
{
|
||||
rc_api_fetch_game_titles_request_t api_params;
|
||||
rc_client_fetch_game_titles_callback_data_t* callback_data;
|
||||
rc_client_async_handle_t* async_handle;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
const char* error_message;
|
||||
|
||||
if (!client) {
|
||||
callback(RC_INVALID_STATE, "client is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!game_ids || num_game_ids == 0) {
|
||||
callback(RC_INVALID_STATE, "game_ids is required", NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
api_params.game_ids = game_ids;
|
||||
api_params.num_game_ids = num_game_ids;
|
||||
result = rc_api_init_fetch_game_titles_request_hosted(&request, &api_params, &client->state.host);
|
||||
|
||||
if (result != RC_OK) {
|
||||
error_message = rc_error_str(result);
|
||||
callback(result, error_message, NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data = (rc_client_fetch_game_titles_callback_data_t*)calloc(1, sizeof(*callback_data));
|
||||
if (!callback_data) {
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), NULL, client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
callback_data->client = client;
|
||||
callback_data->callback = callback;
|
||||
callback_data->callback_userdata = callback_userdata;
|
||||
|
||||
async_handle = &callback_data->async_handle;
|
||||
rc_client_begin_async(client, async_handle);
|
||||
client->callbacks.server_call(&request, rc_client_fetch_game_titles_callback, callback_data, client);
|
||||
rc_api_destroy_request(&request);
|
||||
|
||||
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||
}
|
||||
|
||||
void rc_client_destroy_game_title_list(rc_client_game_title_list_t* list)
|
||||
{
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* ===== Achievements ===== */
|
||||
|
||||
static void rc_client_update_achievement_display_information(rc_client_t* client, rc_client_achievement_info_t* achievement, time_t recent_unlock_time)
|
||||
@@ -5160,30 +5432,37 @@ static void rc_client_ping(rc_client_scheduled_callback_data_t* callback_data, r
|
||||
char buffer[256];
|
||||
int result;
|
||||
|
||||
if (!client->callbacks.rich_presence_override ||
|
||||
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
/* if no frames have been processed since the last ping, the emulator is idle. let the
|
||||
* server session expire. it will be resumed/restarted once frames start getting
|
||||
* processed again. */
|
||||
if (client->state.frames_processed != client->state.frames_at_last_ping) {
|
||||
client->state.frames_at_last_ping = client->state.frames_processed;
|
||||
|
||||
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
||||
client->state.legacy_peek, client, NULL);
|
||||
if (!client->callbacks.rich_presence_override ||
|
||||
!client->callbacks.rich_presence_override(client, buffer, sizeof(buffer))) {
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
rc_runtime_get_richpresence(&client->game->runtime, buffer, sizeof(buffer),
|
||||
client->state.legacy_peek, client, NULL);
|
||||
|
||||
memset(&api_params, 0, sizeof(api_params));
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.game_id = client->game->public_.id;
|
||||
api_params.rich_presence = buffer;
|
||||
api_params.game_hash = client->game->public_.hash;
|
||||
api_params.hardcore = client->state.hardcore;
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
}
|
||||
|
||||
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
||||
if (result != RC_OK) {
|
||||
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
||||
}
|
||||
else {
|
||||
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
||||
memset(&api_params, 0, sizeof(api_params));
|
||||
api_params.username = client->user.username;
|
||||
api_params.api_token = client->user.token;
|
||||
api_params.game_id = client->game->public_.id;
|
||||
api_params.rich_presence = buffer;
|
||||
api_params.game_hash = client->game->public_.hash;
|
||||
api_params.hardcore = client->state.hardcore;
|
||||
|
||||
result = rc_api_init_ping_request_hosted(&request, &api_params, &client->state.host);
|
||||
if (result != RC_OK) {
|
||||
RC_CLIENT_LOG_WARN_FORMATTED(client, "Error generating ping request: %s", rc_error_str(result));
|
||||
}
|
||||
else {
|
||||
client->callbacks.server_call(&request, rc_client_ping_callback, client, client);
|
||||
}
|
||||
}
|
||||
|
||||
callback_data->when = now + 120 * 1000;
|
||||
@@ -5885,6 +6164,8 @@ void rc_client_do_frame(rc_client_t* client)
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
rc_client_raise_pending_events(client, client->game);
|
||||
|
||||
++client->state.frames_processed;
|
||||
}
|
||||
|
||||
/* we've processed a frame. if there's a pause delay in effect, process it */
|
||||
|
||||
36
3rdparty/rcheevos/src/rc_client_external.c
vendored
36
3rdparty/rcheevos/src/rc_client_external.c
vendored
@@ -9,13 +9,15 @@
|
||||
|
||||
/* https://media.retroachievements.org/Badge/123456_lock.png is 58 with null terminator */
|
||||
#define RC_CLIENT_IMAGE_URL_BUFFER_SIZE 64
|
||||
/* https://media.retroachievements.org/UserPic/TwentyCharUserNameXX.png is 69 with null terminator */
|
||||
#define RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE 80
|
||||
|
||||
typedef struct rc_client_external_conversions_t {
|
||||
rc_client_user_t user;
|
||||
rc_client_game_t game;
|
||||
rc_client_subset_t subsets[4];
|
||||
rc_client_achievement_t achievements[16];
|
||||
char user_avatar_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char user_avatar_url[RC_CLIENT_USER_IMAGE_URL_BUFFER_SIZE];
|
||||
char game_badge_url[RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char subset_badge_url[4][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
char achievement_badge_url[16][RC_CLIENT_IMAGE_URL_BUFFER_SIZE];
|
||||
@@ -24,7 +26,7 @@ typedef struct rc_client_external_conversions_t {
|
||||
uint32_t next_achievement_index;
|
||||
} rc_client_external_conversions_t;
|
||||
|
||||
static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t image_type, const char* image_name)
|
||||
static const char* rc_client_external_build_avatar_url(char buffer[], size_t buffer_size, uint32_t image_type, const char* image_name)
|
||||
{
|
||||
rc_api_fetch_image_request_t image_request;
|
||||
rc_api_request_t request;
|
||||
@@ -38,7 +40,7 @@ static const char* rc_client_external_build_avatar_url(char buffer[], uint32_t i
|
||||
if (result != RC_OK)
|
||||
return NULL;
|
||||
|
||||
strcpy_s(buffer, RC_CLIENT_IMAGE_URL_BUFFER_SIZE, request.url);
|
||||
snprintf(buffer, buffer_size, "%s", request.url);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -69,7 +71,9 @@ const rc_client_user_t* rc_client_external_convert_v1_user(const rc_client_t* cl
|
||||
RC_CONVERSION_FILL(converted, rc_client_user_t, v1_rc_client_user_t);
|
||||
|
||||
converted->avatar_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->user_avatar_url, RC_IMAGE_TYPE_USER, v1_user->username);
|
||||
client->state.external_client_conversions->user_avatar_url,
|
||||
sizeof(client->state.external_client_conversions->user_avatar_url),
|
||||
RC_IMAGE_TYPE_USER, v1_user->username);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -88,7 +92,9 @@ const rc_client_game_t* rc_client_external_convert_v1_game(const rc_client_t* cl
|
||||
RC_CONVERSION_FILL(converted, rc_client_game_t, v1_rc_client_game_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(
|
||||
client->state.external_client_conversions->game_badge_url, RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||
client->state.external_client_conversions->game_badge_url,
|
||||
sizeof(client->state.external_client_conversions->game_badge_url),
|
||||
RC_IMAGE_TYPE_GAME, v1_game->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -123,7 +129,9 @@ const rc_client_subset_t* rc_client_external_convert_v1_subset(const rc_client_t
|
||||
memcpy(converted, v1_subset, sizeof(v1_rc_client_subset_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_subset_t, v1_rc_client_subset_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->subset_badge_url[0]),
|
||||
RC_IMAGE_TYPE_GAME, v1_subset->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -161,8 +169,12 @@ const rc_client_achievement_t* rc_client_external_convert_v1_achievement(const r
|
||||
memcpy(converted, v1_achievement, sizeof(v1_rc_client_achievement_t));
|
||||
RC_CONVERSION_FILL(converted, rc_client_achievement_t, v1_rc_client_achievement_t);
|
||||
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||
converted->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT, v1_achievement->badge_name);
|
||||
converted->badge_locked_url = rc_client_external_build_avatar_url(badge_locked_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, v1_achievement->badge_name);
|
||||
|
||||
return converted;
|
||||
}
|
||||
@@ -246,9 +258,13 @@ rc_client_achievement_list_t* rc_client_external_convert_v1_achievement_list(con
|
||||
*achievement = &new_list->achievements[num_achievements++];
|
||||
memcpy(*achievement, *src_achievement, sizeof(**src_achievement));
|
||||
|
||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||
(*achievement)->badge_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url, RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||
(*achievement)->badge_locked_url = rc_client_external_build_avatar_url(badge_url,
|
||||
sizeof(client->state.external_client_conversions->achievement_badge_locked_url[0]),
|
||||
RC_IMAGE_TYPE_ACHIEVEMENT_LOCKED, (*achievement)->badge_name);
|
||||
badge_url += RC_CLIENT_IMAGE_URL_BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
8
3rdparty/rcheevos/src/rc_client_external.h
vendored
8
3rdparty/rcheevos/src/rc_client_external.h
vendored
@@ -61,6 +61,11 @@ typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_lead
|
||||
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_fetch_leaderboard_entries_around_user_func_t)(rc_client_t* client,
|
||||
uint32_t leaderboard_id, uint32_t count, rc_client_fetch_leaderboard_entries_callback_t callback, void* callback_userdata);
|
||||
|
||||
/* NOTE: rc_client_external_create_subset_list_func_t returns an internal wrapper structure which contains the public list
|
||||
* and a destructor function. */
|
||||
struct rc_client_subset_list_info_t;
|
||||
typedef struct rc_client_subset_list_info_t* (RC_CCONV* rc_client_external_create_subset_list_func_t)();
|
||||
|
||||
|
||||
typedef size_t (RC_CCONV *rc_client_external_progress_size_func_t)(void);
|
||||
typedef int (RC_CCONV *rc_client_external_serialize_progress_func_t)(uint8_t* buffer, size_t buffer_size);
|
||||
@@ -144,6 +149,9 @@ typedef struct rc_client_external_t
|
||||
rc_client_external_get_user_game_summary_func_t get_user_game_summary_v5;
|
||||
rc_client_external_get_user_subset_summary_func_t get_user_subset_summary;
|
||||
|
||||
/* VERSION 6 */
|
||||
rc_client_external_create_subset_list_func_t create_subset_list;
|
||||
|
||||
} rc_client_external_t;
|
||||
|
||||
#define RC_CLIENT_EXTERNAL_VERSION 5
|
||||
|
||||
10
3rdparty/rcheevos/src/rc_client_internal.h
vendored
10
3rdparty/rcheevos/src/rc_client_internal.h
vendored
@@ -222,6 +222,14 @@ typedef struct rc_client_subset_info_t {
|
||||
uint8_t pending_events;
|
||||
} rc_client_subset_info_t;
|
||||
|
||||
struct rc_client_subset_list_info_t;
|
||||
typedef void (RC_CCONV* rc_client_destroy_subset_list_func_t)(struct rc_client_subset_list_info_t* list);
|
||||
|
||||
typedef struct rc_client_subset_list_info_t {
|
||||
rc_client_subset_list_t public_;
|
||||
rc_client_destroy_subset_list_func_t destroy_func;
|
||||
} rc_client_subset_list_info_t;
|
||||
|
||||
/*****************************************************************************\
|
||||
| Game |
|
||||
\*****************************************************************************/
|
||||
@@ -316,6 +324,8 @@ typedef struct rc_client_state_t {
|
||||
rc_client_raintegration_t* raintegration;
|
||||
#endif
|
||||
|
||||
uint32_t frames_processed;
|
||||
uint32_t frames_at_last_ping;
|
||||
uint16_t unpaused_frame_decay;
|
||||
uint16_t required_unpaused_frames;
|
||||
|
||||
|
||||
19
3rdparty/rcheevos/src/rc_libretro.c
vendored
19
3rdparty/rcheevos/src/rc_libretro.c
vendored
@@ -649,6 +649,7 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
uint32_t i, j;
|
||||
rc_libretro_core_memory_info_t info;
|
||||
size_t offset;
|
||||
int found_aligning_padding = 0;
|
||||
|
||||
for (i = 0; i < console_regions->num_regions; ++i) {
|
||||
const rc_memory_region_t* console_region = &console_regions->region[i];
|
||||
@@ -656,6 +657,16 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
const uint32_t type = rc_libretro_memory_console_region_to_ram_type(console_region->type);
|
||||
uint32_t base_address = 0;
|
||||
|
||||
if (console_region->type == RC_MEMORY_TYPE_UNUSED && console_region_size >= 0x10000 && !found_aligning_padding) {
|
||||
if (console_regions->region[console_regions->num_regions - 1].end_address > 0x01000000) {
|
||||
/* assume anything exposing more than 16MB of regions with at least one 64KB+ UNUSED region
|
||||
* is padding so things align with real addresses. this indicates the memory is disjoint
|
||||
* in the system, so we cannot expect it to be contiguous in the RETRO_SYSTEM_RAM.
|
||||
* stop processing regions now, and just fill the remaining memory map with null filler. */
|
||||
found_aligning_padding = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j <= i; ++j) {
|
||||
const rc_memory_region_t* console_region2 = &console_regions->region[j];
|
||||
if (rc_libretro_memory_console_region_to_ram_type(console_region2->type) == type) {
|
||||
@@ -665,7 +676,13 @@ static void rc_libretro_memory_init_from_unmapped_memory(rc_libretro_memory_regi
|
||||
}
|
||||
offset = console_region->start_address - base_address;
|
||||
|
||||
get_core_memory_info(type, &info);
|
||||
if (!found_aligning_padding) {
|
||||
get_core_memory_info(type, &info);
|
||||
}
|
||||
else {
|
||||
info.data = NULL;
|
||||
info.size = console_region_size;
|
||||
}
|
||||
|
||||
if (offset < info.size) {
|
||||
info.size -= offset;
|
||||
|
||||
2
3rdparty/rcheevos/src/rc_version.h
vendored
2
3rdparty/rcheevos/src/rc_version.h
vendored
@@ -8,7 +8,7 @@
|
||||
RC_BEGIN_C_DECLS
|
||||
|
||||
#define RCHEEVOS_VERSION_MAJOR 12
|
||||
#define RCHEEVOS_VERSION_MINOR 1
|
||||
#define RCHEEVOS_VERSION_MINOR 2
|
||||
#define RCHEEVOS_VERSION_PATCH 0
|
||||
|
||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||
|
||||
21
3rdparty/rcheevos/src/rcheevos/condition.c
vendored
21
3rdparty/rcheevos/src/rcheevos/condition.c
vendored
@@ -374,7 +374,7 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||
}
|
||||
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||
break;
|
||||
|
||||
@@ -388,13 +388,13 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
/* type determined by parent */
|
||||
const uint8_t new_size = rc_operand_is_float(&parse->addsource_parent) ? RC_MEMSIZE_FLOAT : RC_MEMSIZE_32_BITS;
|
||||
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD && !rc_operand_is_memref(&parse->addsource_parent)) {
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR && !rc_operand_is_memref(&parse->addsource_parent)) {
|
||||
/* if the previous element was a constant we have to turn it into a memref by adding zero */
|
||||
rc_modified_memref_t* memref;
|
||||
rc_operand_t zero;
|
||||
rc_operand_set_const(&zero, 0);
|
||||
memref = rc_alloc_modified_memref(parse,
|
||||
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD, &zero);
|
||||
parse->addsource_parent.size, &parse->addsource_parent, RC_OPERATOR_ADD_ACCUMULATOR, &zero);
|
||||
parse->addsource_parent.value.memref = (rc_memref_t*)memref;
|
||||
parse->addsource_parent.type = RC_OPERAND_ADDRESS;
|
||||
}
|
||||
@@ -414,19 +414,27 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
}
|
||||
|
||||
/* subtract the condition from the chain */
|
||||
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB : RC_OPERATOR_SUB_PARENT;
|
||||
parse->addsource_oper = rc_operand_is_memref(&parse->addsource_parent) ? RC_OPERATOR_SUB_ACCUMULATOR : RC_OPERATOR_SUB_PARENT;
|
||||
rc_condition_convert_to_operand(condition, &cond_operand, parse);
|
||||
rc_operand_addsource(&cond_operand, parse, new_size);
|
||||
memcpy(&parse->addsource_parent, &cond_operand, sizeof(cond_operand));
|
||||
|
||||
/* indicate the next value can be added to the chain */
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
parse->addsource_oper = RC_OPERATOR_ADD_ACCUMULATOR;
|
||||
}
|
||||
|
||||
parse->indirect_parent.type = RC_OPERAND_NONE;
|
||||
break;
|
||||
|
||||
case RC_CONDITION_REMEMBER:
|
||||
if (condition->operand1.type == RC_OPERAND_RECALL &&
|
||||
condition->oper == RC_OPERATOR_NONE &&
|
||||
parse->addsource_parent.type == RC_OPERAND_NONE &&
|
||||
parse->indirect_parent.type == RC_OPERAND_NONE) {
|
||||
/* Remembering {recall} without any modifications is a no-op */
|
||||
break;
|
||||
}
|
||||
|
||||
rc_condition_convert_to_operand(condition, &condition->operand1, parse);
|
||||
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
@@ -465,6 +473,9 @@ void rc_condition_update_parse_state(rc_condition_t* condition, rc_parse_state_t
|
||||
default:
|
||||
if (parse->addsource_parent.type != RC_OPERAND_NONE) {
|
||||
/* type determined by leaf */
|
||||
if (parse->addsource_oper == RC_OPERATOR_ADD_ACCUMULATOR)
|
||||
parse->addsource_oper = RC_OPERATOR_ADD;
|
||||
|
||||
rc_operand_addsource(&condition->operand1, parse, condition->operand1.size);
|
||||
condition->operand1.is_combining = 1;
|
||||
|
||||
|
||||
8
3rdparty/rcheevos/src/rcheevos/condset.c
vendored
8
3rdparty/rcheevos/src/rcheevos/condset.c
vendored
@@ -62,8 +62,10 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
||||
do {
|
||||
rc_parse_condition_internal(&condition, &memaddr, &parse);
|
||||
|
||||
if (parse.offset < 0)
|
||||
if (parse.offset < 0) {
|
||||
rc_destroy_parse_state(&parse);
|
||||
return parse.offset;
|
||||
}
|
||||
|
||||
++index;
|
||||
|
||||
@@ -106,7 +108,9 @@ static int32_t rc_classify_conditions(rc_condset_t* self, const char* memaddr, c
|
||||
* logic in rc_find_next_classification */
|
||||
self->num_other_conditions += chain_length - 1;
|
||||
|
||||
return index;
|
||||
rc_destroy_parse_state(&parse);
|
||||
|
||||
return (int32_t)index;
|
||||
}
|
||||
|
||||
static int rc_find_next_classification(const char* memaddr) {
|
||||
|
||||
29
3rdparty/rcheevos/src/rcheevos/memref.c
vendored
29
3rdparty/rcheevos/src/rcheevos/memref.c
vendored
@@ -154,8 +154,14 @@ rc_modified_memref_t* rc_alloc_modified_memref(rc_parse_state_t* parse, uint8_t
|
||||
memcpy(&modified_memref->parent, parent, sizeof(modified_memref->parent));
|
||||
memcpy(&modified_memref->modifier, modifier, sizeof(modified_memref->modifier));
|
||||
modified_memref->modifier_type = modifier_type;
|
||||
modified_memref->depth = 0;
|
||||
modified_memref->memref.address = rc_operand_is_memref(modifier) ? modifier->value.memref->address : modifier->value.num;
|
||||
|
||||
if (rc_operand_is_memref(parent) && parent->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
|
||||
const rc_modified_memref_t* parent_modified_memref = (rc_modified_memref_t*)parent->value.memref;
|
||||
modified_memref->depth = parent_modified_memref->depth + 1;
|
||||
}
|
||||
|
||||
return modified_memref;
|
||||
}
|
||||
|
||||
@@ -729,11 +735,34 @@ uint32_t rc_get_modified_memref_value(const rc_modified_memref_t* memref, rc_pee
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_SUB_PARENT:
|
||||
/* sub parent is "-parent + modifier" */
|
||||
rc_typed_value_negate(&value);
|
||||
rc_typed_value_add(&value, &modifier);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
break;
|
||||
|
||||
case RC_OPERATOR_SUB_ACCUMULATOR:
|
||||
rc_typed_value_negate(&modifier);
|
||||
/* fallthrough */ /* to case RC_OPERATOR_SUB_ACCUMULATOR */
|
||||
|
||||
case RC_OPERATOR_ADD_ACCUMULATOR:
|
||||
/* when modifying the accumulator, force the modifier to match the accumulator
|
||||
* type instead of promoting them both to the less restrictive type.
|
||||
*
|
||||
* 18 - 17.5 will result in an integer. should it be 0 or 1?
|
||||
*
|
||||
* default: float is less restrictive, convert both to float for combine,
|
||||
* then convert to the memref type.
|
||||
* (int)((float)18 - 17.5) -> (int)(0.5) -> 0
|
||||
*
|
||||
* accumulator is integer: force modifier to be integer before combining
|
||||
* (int)(18 - (int)17.5) -> (int)(18 - 17) -> 1
|
||||
*/
|
||||
rc_typed_value_convert(&modifier, value.type);
|
||||
rc_typed_value_add(&value, &modifier);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
break;
|
||||
|
||||
default:
|
||||
rc_typed_value_combine(&value, &modifier, memref->modifier_type);
|
||||
rc_typed_value_convert(&value, memref->memref.value.type);
|
||||
|
||||
5
3rdparty/rcheevos/src/rcheevos/operand.c
vendored
5
3rdparty/rcheevos/src/rcheevos/operand.c
vendored
@@ -334,8 +334,11 @@ int rc_operands_are_equal(const rc_operand_t* left, const rc_operand_t* right) {
|
||||
const rc_modified_memref_t* left_memref = (const rc_modified_memref_t*)left->value.memref;
|
||||
const rc_modified_memref_t* right_memref = (const rc_modified_memref_t*)right->value.memref;
|
||||
return (left_memref->modifier_type == right_memref->modifier_type &&
|
||||
left_memref->depth == right_memref->depth &&
|
||||
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier) &&
|
||||
rc_operands_are_equal(&left_memref->parent, &right_memref->parent) &&
|
||||
rc_operands_are_equal(&left_memref->modifier, &right_memref->modifier));
|
||||
1 == 1
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
4
3rdparty/rcheevos/src/rcheevos/rc_internal.h
vendored
4
3rdparty/rcheevos/src/rcheevos/rc_internal.h
vendored
@@ -14,10 +14,11 @@ typedef struct rc_scratch_string {
|
||||
rc_scratch_string_t;
|
||||
|
||||
typedef struct rc_modified_memref_t {
|
||||
rc_memref_t memref; /* for compatibility with rc_operand_t.value.memref */
|
||||
rc_memref_t memref; /* For compatibility with rc_operand_t.value.memref */
|
||||
rc_operand_t parent; /* The parent memref this memref is derived from (type will always be a memref type) */
|
||||
rc_operand_t modifier; /* The modifier to apply to the parent. */
|
||||
uint8_t modifier_type; /* How to apply the modifier to the parent. (RC_OPERATOR_*) */
|
||||
uint16_t depth; /* The number of parents this memref has. */
|
||||
}
|
||||
rc_modified_memref_t;
|
||||
|
||||
@@ -382,7 +383,6 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
|
||||
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
|
||||
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
|
||||
|
||||
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
|
||||
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);
|
||||
|
||||
RC_END_C_DECLS
|
||||
|
||||
829
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
829
3rdparty/rcheevos/src/rcheevos/rc_validate.c
vendored
File diff suppressed because it is too large
Load Diff
10
3rdparty/rcheevos/src/rcheevos/trigger.c
vendored
10
3rdparty/rcheevos/src/rcheevos/trigger.c
vendored
@@ -235,8 +235,14 @@ int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, void* unus
|
||||
is_paused |= sub_paused;
|
||||
}
|
||||
|
||||
/* if paused, the measured value may not be captured, keep the old value */
|
||||
if (!is_paused) {
|
||||
if (is_paused) {
|
||||
/* if the trigger is fully paused, ignore any updates to the measured value */
|
||||
}
|
||||
else if (measured_value.type == RC_VALUE_TYPE_NONE) {
|
||||
/* if a measured value was not captured, keep the old value (it's possible to pause
|
||||
* an alt that is generating the measured value without fully pausing the trigger) */
|
||||
}
|
||||
else {
|
||||
rc_typed_value_convert(&measured_value, RC_VALUE_TYPE_UNSIGNED);
|
||||
self->measured_value = measured_value.value.u32;
|
||||
}
|
||||
|
||||
36
3rdparty/rcheevos/src/rcheevos/value.c
vendored
36
3rdparty/rcheevos/src/rcheevos/value.c
vendored
@@ -67,6 +67,7 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
next_clause = &self->conditions;
|
||||
do {
|
||||
/* count the number of joiners and add one to determine the number of clauses. */
|
||||
buffer[0] = 'A'; /* reset to AddSource */
|
||||
done = 0;
|
||||
num_measured_conditions = 1;
|
||||
buffer_ptr = *memaddr;
|
||||
@@ -97,8 +98,8 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
}
|
||||
} while (!done);
|
||||
|
||||
/* if last condition is SubSource, we'll need to add a dummy condition for the Measured */
|
||||
if (buffer[0] == 'B')
|
||||
/* if last condition is not AddSource, we'll need to add a dummy condition for the Measured */
|
||||
if (buffer[0] != 'A')
|
||||
++num_measured_conditions;
|
||||
|
||||
condset_with_conditions = RC_ALLOC_WITH_TRAILING(rc_condset_with_trailing_conditions_t,
|
||||
@@ -121,10 +122,18 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
for (;; ++(*memaddr)) {
|
||||
switch (**memaddr) {
|
||||
case '_': /* add next */
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
case '$': /* maximum of */
|
||||
case '\0': /* end of string */
|
||||
case ':': /* end of leaderboard clause */
|
||||
case ')': /* end of rich presence macro */
|
||||
/* the last condition needs to be Measured - AddSource can be changed here,
|
||||
* SubSource will be handled later */
|
||||
if (buffer[0] == 'A')
|
||||
buffer[0] = 'M';
|
||||
|
||||
*ptr = '\0';
|
||||
break;
|
||||
|
||||
@@ -176,33 +185,34 @@ static void rc_parse_legacy_value(rc_value_t* self, const char** memaddr, rc_par
|
||||
return;
|
||||
}
|
||||
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
|
||||
*next = cond;
|
||||
next = &cond->next;
|
||||
|
||||
if (**memaddr != '_') /* add next */
|
||||
break;
|
||||
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
++cond;
|
||||
}
|
||||
|
||||
/* end of clause */
|
||||
if (cond->type == RC_CONDITION_SUB_SOURCE) {
|
||||
/* cannot change SubSource to Measured. add a dummy condition */
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
if (parse->buffer)
|
||||
/* -- end of clause -- */
|
||||
|
||||
/* clause must end in a Measured. if it doesn't, append one */
|
||||
if (cond->type != RC_CONDITION_MEASURED) {
|
||||
if (!parse->buffer)
|
||||
cond = &local_cond;
|
||||
else
|
||||
++cond;
|
||||
|
||||
buffer_ptr = "A:0";
|
||||
buffer_ptr = "M:0";
|
||||
rc_parse_condition_internal(cond, &buffer_ptr, parse);
|
||||
*next = cond;
|
||||
next = &cond->next;
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
}
|
||||
|
||||
/* convert final AddSource condition to Measured */
|
||||
cond->type = RC_CONDITION_MEASURED;
|
||||
cond->next = NULL;
|
||||
rc_condition_update_parse_state(cond, parse);
|
||||
*next = NULL;
|
||||
|
||||
/* finalize clause */
|
||||
*next_clause = condset;
|
||||
|
||||
22
3rdparty/rcheevos/src/rhash/hash.c
vendored
22
3rdparty/rcheevos/src/rhash/hash.c
vendored
@@ -1300,7 +1300,18 @@ static void rc_hash_initialize_iterator_from_path(rc_hash_iterator_t* iterator,
|
||||
bsearch(&search, handlers, num_handlers, sizeof(*handler), rc_hash_iterator_find_handler);
|
||||
if (handler) {
|
||||
handler->handler(iterator, handler->data);
|
||||
} else {
|
||||
|
||||
if (iterator->callbacks.verbose_message) {
|
||||
int count = 0;
|
||||
while (iterator->consoles[count])
|
||||
++count;
|
||||
|
||||
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, ext);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rc_hash_iterator_error_formatted(iterator, "No console mapping specified for %s file extension - trying full file hash", ext);
|
||||
|
||||
/* if we didn't match the extension, default to something that does a whole file hash */
|
||||
if (!iterator->consoles[0])
|
||||
iterator->consoles[0] = RC_CONSOLE_GAMEBOY;
|
||||
@@ -1332,15 +1343,6 @@ int rc_hash_iterate(char hash[33], rc_hash_iterator_t* iterator) {
|
||||
|
||||
if (iterator->index == -1) {
|
||||
rc_hash_initialize_iterator_from_path(iterator, iterator->path);
|
||||
|
||||
if (iterator->callbacks.verbose_message) {
|
||||
int count = 0;
|
||||
while (iterator->consoles[count])
|
||||
++count;
|
||||
|
||||
rc_hash_iterator_verbose_formatted(iterator, "Found %d potential consoles for %s file extension", count, rc_path_get_extension(iterator->path));
|
||||
}
|
||||
|
||||
iterator->index = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13722,6 +13722,8 @@ SLED-51676:
|
||||
region: "PAL"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLED-51807:
|
||||
name: "Summer Heat Beach Volleyball"
|
||||
region: "PAL-Unk"
|
||||
@@ -13996,6 +13998,7 @@ SLED-53109:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLED-53137:
|
||||
name: "Stolen [Demo]"
|
||||
@@ -16671,6 +16674,8 @@ SLES-50958:
|
||||
region: "PAL-M4"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLES-50963:
|
||||
name: "Riding Spirits"
|
||||
region: "PAL-M5"
|
||||
@@ -17825,6 +17830,8 @@ SLES-51393:
|
||||
SLES-51397:
|
||||
name: "IndyCar Series"
|
||||
region: "PAL-M5"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-51398:
|
||||
name: "World Championship Snooker 2003"
|
||||
region: "PAL-E"
|
||||
@@ -19948,6 +19955,8 @@ SLES-52298:
|
||||
name: "IndyCar Series 2005"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-52308:
|
||||
name: "Karaoke Stage"
|
||||
region: "PAL-M5"
|
||||
@@ -20118,6 +20127,9 @@ SLES-52378:
|
||||
name: "Euro Rally Champion"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
textureInsideRT: 1 # Fixes broken fog rendering.
|
||||
SLES-52379:
|
||||
name: "Shrek 2"
|
||||
region: "PAL-E"
|
||||
@@ -22269,6 +22281,7 @@ SLES-53044:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLES-53045:
|
||||
name: "Street Racing Syndicate"
|
||||
@@ -22592,6 +22605,7 @@ SLES-53151:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLES-53152:
|
||||
name: "Mashed Fully Loaded"
|
||||
@@ -25050,6 +25064,8 @@ SLES-53957:
|
||||
SLES-53958:
|
||||
name: "Noble Racing"
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLES-53959:
|
||||
name: "Pac-Man World 3"
|
||||
region: "PAL-M5"
|
||||
@@ -30127,6 +30143,11 @@ SLES-55544:
|
||||
region: "PAL-M5"
|
||||
roundModes:
|
||||
vu1RoundMode: 0 # Fixes VU size spam and potential graphical issues with GH3 engine.
|
||||
gsHWFixes:
|
||||
cpuCLUTRender: 1 # Fixes broken rainbow rendering.
|
||||
halfPixelOffset: 4 # Mostly aligns post-processing.
|
||||
nativeScaling: 1 # Fixes post-processing smoothness and position.
|
||||
autoFlush: 1 # Fixes edge garbage and shadow definition.
|
||||
SLES-55545:
|
||||
name: "WWE SmackDown! vs. Raw 2010"
|
||||
region: "PAL-M5"
|
||||
@@ -32394,6 +32415,7 @@ SLKA-25283:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLKA-25284:
|
||||
name: "사쿠라 대전 3 ~파리는 불타고 있는가~"
|
||||
@@ -48603,6 +48625,7 @@ SLPM-66277:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLPM-66278:
|
||||
name: "新・豪血寺一族 -煩悩解放-"
|
||||
@@ -66745,6 +66768,8 @@ SLUS-20597:
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLUS-20598:
|
||||
name: "Everblue 2"
|
||||
region: "NTSC-U"
|
||||
@@ -66968,6 +66993,8 @@ SLUS-20641:
|
||||
name: "IndyCar Series"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Aligns post-processing and fixes depth line.
|
||||
SLUS-20642:
|
||||
name: "Auto Modellista"
|
||||
region: "NTSC-U"
|
||||
@@ -68225,6 +68252,7 @@ SLUS-20872:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLUS-20873:
|
||||
name: "Silent Hill 4 - The Room"
|
||||
@@ -74172,8 +74200,10 @@ SLUS-21866:
|
||||
roundModes:
|
||||
vu1RoundMode: 0 # Fixes VU size spam and potential graphical issues with GH3 engine.
|
||||
gsHWFixes:
|
||||
cpuCLUTRender: 1 # Fixes broken rainbow rendering.
|
||||
halfPixelOffset: 4 # Mostly aligns post-processing.
|
||||
nativeScaling: 1 # Fixes post-processing smoothness and position.
|
||||
autoFlush: 1 # Fixes edge garbage and shadow definition.
|
||||
SLUS-21867:
|
||||
name: "Guitar Hero - Van Halen"
|
||||
region: "NTSC-U"
|
||||
@@ -74971,6 +75001,8 @@ SLUS-29049:
|
||||
region: "NTSC-U"
|
||||
gameFixes:
|
||||
- XGKickHack # Fixes corrupted graphics.
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes light bleed through walls.
|
||||
SLUS-29050:
|
||||
name: "EverQuest Online Adventures [Regular Demo]"
|
||||
region: "NTSC-U"
|
||||
@@ -75330,6 +75362,7 @@ SLUS-29147:
|
||||
cpuSpriteRenderLevel: 2 # Needed for above.
|
||||
autoFlush: 1 # Fixes headlight brightness.
|
||||
cpuCLUTRender: 1 # Fixes broken headlights.
|
||||
nativeScaling: 4 # Aligns post effects.
|
||||
minimumBlendingLevel: 3
|
||||
SLUS-29148:
|
||||
name: "The Incredible Hulk - Ultimate Destruction [Demo]"
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,paddle1:b2,paddle2:b5,platform:Windows,
|
||||
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
@@ -850,6 +850,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
|
||||
# Mac OS X
|
||||
030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
03000000c82d00001930000000000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00001930000000020000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00001930000001000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
|
||||
03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
@@ -895,7 +896,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
@@ -985,6 +986,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||
030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
@@ -993,7 +995,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
|
||||
@@ -1395,6 +1396,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,x:b10,y:b11,back:b12,platform:Linux,
|
||||
030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
|
||||
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
|
||||
@@ -1591,6 +1596,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,
|
||||
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000120c0000160e000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
|
||||
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
|
||||
@@ -1701,7 +1707,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
|
||||
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
|
||||
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
@@ -1858,4 +1863,5 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000790000002201000011010000,ZhiXu GuliKit D,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/Threading.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include "common/HostSys.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
@@ -19,12 +20,15 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <thread>
|
||||
#include <time.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/thread_state.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mutex>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
@@ -294,7 +298,7 @@ static CPUInfo CalcCPUInfo()
|
||||
std::vector<DarwinMisc::CPUClass> classes = DarwinMisc::GetCPUClasses();
|
||||
out.num_clusters = static_cast<u32>(classes.size());
|
||||
out.num_big_cores = classes.empty() ? 0 : classes[0].num_physical;
|
||||
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
|
||||
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
|
||||
out.num_small_cores = 0;
|
||||
for (std::size_t i = 1; i < classes.size(); i++)
|
||||
{
|
||||
@@ -568,15 +572,206 @@ void HostSys::EndCodeWrite()
|
||||
|
||||
#endif // _M_ARM64
|
||||
|
||||
#define USE_MACH_EXCEPTION_PORTS
|
||||
|
||||
namespace PageFaultHandler
|
||||
{
|
||||
#ifdef USE_MACH_EXCEPTION_PORTS
|
||||
static void SignalHandler(mach_port_t port);
|
||||
#else
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
|
||||
#endif
|
||||
|
||||
static std::recursive_mutex s_exception_handler_mutex;
|
||||
static bool s_in_exception_handler = false;
|
||||
static bool s_installed = false;
|
||||
} // namespace PageFaultHandler
|
||||
|
||||
#ifdef USE_MACH_EXCEPTION_PORTS
|
||||
|
||||
#if defined(_M_X86)
|
||||
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 x86_THREAD_STATE64
|
||||
#define thread_state64_t x86_thread_state64_t
|
||||
#elif defined(_M_ARM64)
|
||||
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 ARM_THREAD_STATE64
|
||||
#define thread_state64_t arm_thread_state64_t
|
||||
#else
|
||||
#error Unknown Darwin Platform
|
||||
#endif
|
||||
|
||||
void PageFaultHandler::SignalHandler(mach_port_t port)
|
||||
{
|
||||
Threading::SetNameOfCurrentThread("Mach Exception Thread");
|
||||
|
||||
#pragma pack(4)
|
||||
struct
|
||||
{
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[THREAD_STATE64_COUNT];
|
||||
mach_msg_trailer_t trailer;
|
||||
} msg_in;
|
||||
|
||||
struct
|
||||
{
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[THREAD_STATE64_COUNT];
|
||||
} msg_out;
|
||||
#pragma pack()
|
||||
memset(&msg_in, 0xee, sizeof(msg_in));
|
||||
memset(&msg_out, 0xee, sizeof(msg_out));
|
||||
mach_msg_size_t send_size = 0;
|
||||
mach_msg_option_t option = MACH_RCV_MSG;
|
||||
while (true)
|
||||
{
|
||||
kern_return_t r;
|
||||
if ((r = mach_msg_overwrite(&msg_out.Head, option, send_size, sizeof(msg_in), port,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0)))
|
||||
{
|
||||
pxFail(fmt::format("CRITICAL: mach_msg_overwrite: {:x}", r).c_str());
|
||||
}
|
||||
|
||||
if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS)
|
||||
{
|
||||
// the other thread exited
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_in.Head.msgh_id != 2406)
|
||||
{
|
||||
pxFailRel("unknown message received");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_in.flavor != THREAD_STATE64)
|
||||
{
|
||||
pxFailRel(fmt::format("unknown flavour {}, expected {}", msg_in.flavor, THREAD_STATE64).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
s_exception_handler_mutex.lock();
|
||||
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
|
||||
|
||||
HandlerResult result = HandlerResult::ExecuteNextHandler;
|
||||
if (!s_in_exception_handler)
|
||||
{
|
||||
s_in_exception_handler = true;
|
||||
|
||||
#ifdef _M_ARM64
|
||||
result = HandlePageFault(reinterpret_cast<void*>(state->__pc), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
|
||||
#else
|
||||
result = HandlePageFault(reinterpret_cast<void*>(state->__rip), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
|
||||
#endif
|
||||
s_in_exception_handler = false;
|
||||
}
|
||||
|
||||
// Set up the reply.
|
||||
msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);
|
||||
msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;
|
||||
msg_out.Head.msgh_local_port = MACH_PORT_NULL;
|
||||
msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;
|
||||
msg_out.NDR = msg_in.NDR;
|
||||
|
||||
if (result != HandlerResult::ContinueExecution) // cooked
|
||||
{
|
||||
msg_out.RetCode = KERN_FAILURE;
|
||||
msg_out.flavor = 0;
|
||||
msg_out.new_stateCnt = 0;
|
||||
|
||||
// The crash handler on macOS or Linux doesn't use context passed to it
|
||||
// Stubbing it here is fine
|
||||
CrashHandler::CrashSignalHandler(-1, nullptr, nullptr);
|
||||
|
||||
pxFailRel("CrashSignalHandler returned when it should have terminated us!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV)
|
||||
msg_out.RetCode = KERN_SUCCESS;
|
||||
msg_out.flavor = THREAD_STATE64;
|
||||
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
|
||||
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
|
||||
}
|
||||
|
||||
msg_out.Head.msgh_size =
|
||||
offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);
|
||||
send_size = msg_out.Head.msgh_size;
|
||||
option |= MACH_SEND_MSG;
|
||||
|
||||
s_exception_handler_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool PageFaultHandler::Install(Error* error)
|
||||
{
|
||||
exception_mask_t masks[EXC_TYPES_COUNT];
|
||||
mach_port_t ports[EXC_TYPES_COUNT];
|
||||
exception_behavior_t behaviors[EXC_TYPES_COUNT];
|
||||
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
|
||||
mach_msg_type_number_t count = EXC_TYPES_COUNT;
|
||||
|
||||
kern_return_t r = task_get_exception_ports(mach_task_self(), EXC_MASK_ALL,
|
||||
masks, &count, ports, behaviors, flavors);
|
||||
|
||||
mach_port_t port;
|
||||
if ((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
|
||||
{
|
||||
pxFailRel(fmt::format("mach_port_allocate: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::thread sig_thread(PageFaultHandler::SignalHandler, port);
|
||||
sig_thread.detach();
|
||||
|
||||
if ((r = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_insert_right: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
|
||||
if ((r = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("thread_set_exception_ports: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((r = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
mach_port_t previous;
|
||||
if ((r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
s_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
|
||||
{
|
||||
#if defined(_M_X86)
|
||||
@@ -644,3 +839,4 @@ bool PageFaultHandler::Install(Error* error)
|
||||
s_installed = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -133,7 +133,7 @@ void DisplaySurface::handleCloseEvent(QCloseEvent* event)
|
||||
// In the latter case, it's going to destroy us, so don't let Qt do it first.
|
||||
// Treat a close event while fullscreen as an exit, that way ALT+F4 closes PCSX2,
|
||||
// rather than just the game.
|
||||
if (QtHost::IsVMValid() && !isActuallyFullscreen())
|
||||
if (QtHost::IsVMValid() && !isFullScreen())
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, "requestShutdown", Q_ARG(bool, true),
|
||||
Q_ARG(bool, true), Q_ARG(bool, false));
|
||||
@@ -147,10 +147,44 @@ void DisplaySurface::handleCloseEvent(QCloseEvent* event)
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
bool DisplaySurface::isActuallyFullscreen() const
|
||||
bool DisplaySurface::isFullScreen() const
|
||||
{
|
||||
// DisplaySurface is always in a container, so we need to check parent window
|
||||
return parent()->windowState() & Qt::WindowFullScreen;
|
||||
// DisplaySurface may be in a container
|
||||
return (parent() ? parent()->windowState() : windowState()) & Qt::WindowFullScreen;
|
||||
}
|
||||
|
||||
void DisplaySurface::setFocus()
|
||||
{
|
||||
if (m_container)
|
||||
m_container->setFocus();
|
||||
else
|
||||
requestActivate();
|
||||
}
|
||||
|
||||
QByteArray DisplaySurface::saveGeometry() const
|
||||
{
|
||||
if (m_container)
|
||||
return m_container->saveGeometry();
|
||||
else
|
||||
{
|
||||
// QWindow lacks saveGeometry, so create a dummy widget and copy geometry across.
|
||||
QWidget dummy = QWidget();
|
||||
dummy.setGeometry(geometry());
|
||||
return dummy.saveGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySurface::restoreGeometry(const QByteArray& geometry)
|
||||
{
|
||||
if (m_container)
|
||||
m_container->restoreGeometry(geometry);
|
||||
else
|
||||
{
|
||||
// QWindow lacks restoreGeometry, so create a dummy widget and copy geometry across.
|
||||
QWidget dummy = QWidget();
|
||||
dummy.restoreGeometry(geometry);
|
||||
setGeometry(dummy.geometry());
|
||||
}
|
||||
}
|
||||
|
||||
void DisplaySurface::updateCenterPos()
|
||||
@@ -393,10 +427,18 @@ bool DisplaySurface::event(QEvent* event)
|
||||
return event->isAccepted();
|
||||
|
||||
case QEvent::Move:
|
||||
{
|
||||
updateCenterPos();
|
||||
return true;
|
||||
}
|
||||
|
||||
// These events only work on the top level control.
|
||||
// Which is this container when render to seperate or fullscreen is active (Windows).
|
||||
case QEvent::Close:
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
case QEvent::WindowStateChange:
|
||||
if (static_cast<QWindowStateChangeEvent*>(event)->oldState() & Qt::WindowMinimized)
|
||||
emit windowRestoredEvent();
|
||||
return false;
|
||||
|
||||
default:
|
||||
return QWindow::event(event);
|
||||
@@ -418,7 +460,7 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
return true;
|
||||
|
||||
// These events only work on the top level control.
|
||||
// Which is this container when render to seperate or fullscreen is active.
|
||||
// Which is this container when render to seperate or fullscreen is active (Non-Windows).
|
||||
case QEvent::Close:
|
||||
handleCloseEvent(static_cast<QCloseEvent*>(event));
|
||||
return true;
|
||||
@@ -427,8 +469,8 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
emit windowRestoredEvent();
|
||||
return false;
|
||||
|
||||
case QEvent::ChildRemoved:
|
||||
if (static_cast<QChildEvent*>(event)->child() == m_container)
|
||||
case QEvent::ChildWindowRemoved:
|
||||
if (static_cast<QChildWindowEvent*>(event)->child() == this)
|
||||
{
|
||||
object->removeEventFilter(this);
|
||||
m_container = nullptr;
|
||||
|
||||
@@ -30,6 +30,12 @@ public:
|
||||
void updateRelativeMode(bool enabled);
|
||||
void updateCursor(bool hidden);
|
||||
|
||||
bool isFullScreen() const;
|
||||
void setFocus();
|
||||
|
||||
QByteArray saveGeometry() const;
|
||||
void restoreGeometry(const QByteArray& geometry);
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowResizedEvent(int width, int height, float scale);
|
||||
void windowRestoredEvent();
|
||||
@@ -47,7 +53,6 @@ private Q_SLOTS:
|
||||
void onResizeDebounceTimer();
|
||||
|
||||
private:
|
||||
bool isActuallyFullscreen() const;
|
||||
void updateCenterPos();
|
||||
|
||||
QPoint m_relative_mouse_start_pos{};
|
||||
|
||||
@@ -100,6 +100,13 @@ static quint32 s_current_running_crc;
|
||||
static bool s_record_on_start = false;
|
||||
static QString s_path_to_recording_for_record_on_start;
|
||||
|
||||
// DX cannot fullscreen when the display surface is in a container.
|
||||
// QWindow, however, seems to lack CSD under wayland, so needs the container.
|
||||
// MAC is unknown
|
||||
#ifdef _WIN32
|
||||
#define DISPLAY_SURFACE_WINDOW
|
||||
#endif
|
||||
|
||||
MainWindow::MainWindow()
|
||||
{
|
||||
pxAssert(!g_main_window);
|
||||
@@ -1008,10 +1015,15 @@ void MainWindow::updateWindowTitle()
|
||||
if (windowTitle() != main_title)
|
||||
setWindowTitle(main_title);
|
||||
|
||||
if (m_display_container && !isRenderingToMain())
|
||||
if (m_display_surface && !isRenderingToMain())
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (m_display_surface->title() != display_title)
|
||||
m_display_surface->setTitle(display_title);
|
||||
#else
|
||||
if (m_display_container->windowTitle() != display_title)
|
||||
m_display_container->setWindowTitle(display_title);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (g_log_window)
|
||||
@@ -1040,7 +1052,13 @@ void MainWindow::updateWindowState(bool force_visible)
|
||||
|
||||
// Update the display widget too if rendering separately.
|
||||
if (m_display_surface && !isRenderingToMain())
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
QtUtils::SetWindowResizeable(m_display_surface, resizeable);
|
||||
#else
|
||||
QtUtils::SetWindowResizeable(m_display_container, resizeable);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::setProgressBar(int current, int total)
|
||||
@@ -1075,12 +1093,12 @@ bool MainWindow::isRenderingFullscreen() const
|
||||
if (!MTGS::IsOpen() || !m_display_surface)
|
||||
return false;
|
||||
|
||||
return m_display_container->isFullScreen();
|
||||
return m_display_surface->isFullScreen();
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingToMain() const
|
||||
{
|
||||
return (m_display_surface && m_ui.mainContainer->indexOf(m_display_container) == 1);
|
||||
return (m_display_container && m_ui.mainContainer->indexOf(m_display_container) == 1);
|
||||
}
|
||||
|
||||
bool MainWindow::shouldHideMouseCursor() const
|
||||
@@ -1108,16 +1126,25 @@ bool MainWindow::shouldMouseLock() const
|
||||
if (m_display_created == false || m_display_surface == nullptr)
|
||||
return false;
|
||||
|
||||
bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
|
||||
(!m_controller_settings_window || m_controller_settings_window->isHidden()) &&
|
||||
(!m_settings_window || m_settings_window->isHidden());
|
||||
const bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
|
||||
(!m_controller_settings_window || m_controller_settings_window->isHidden()) &&
|
||||
(!m_settings_window || m_settings_window->isHidden());
|
||||
|
||||
auto* displayWindow = isRenderingToMain() ? window() : m_display_container->window();
|
||||
|
||||
if (displayWindow == nullptr)
|
||||
if (!windowsHidden)
|
||||
return false;
|
||||
|
||||
return windowsHidden && (displayWindow->isActiveWindow() || displayWindow->isFullScreen());
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (isRenderingToMain())
|
||||
{
|
||||
const auto* displayWindow = window();
|
||||
return displayWindow ? (displayWindow->isActiveWindow() || displayWindow->isFullScreen()) : false;
|
||||
}
|
||||
else
|
||||
return m_display_surface->isActive() || m_display_surface->isFullScreen();
|
||||
#else
|
||||
const auto* displayWindow = isRenderingToMain() ? window() : m_display_container->window();
|
||||
return displayWindow ? (displayWindow->isActiveWindow() || displayWindow->isFullScreen()) : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MainWindow::shouldAbortForMemcardBusy(const VMLock& lock)
|
||||
@@ -1180,7 +1207,7 @@ void MainWindow::switchToEmulationView()
|
||||
g_emu_thread->setVMPaused(false);
|
||||
|
||||
if (m_display_surface)
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::refreshGameList(bool invalidate_cache, bool popup_on_error)
|
||||
@@ -2129,7 +2156,7 @@ void MainWindow::onVMResumed()
|
||||
if (m_display_surface)
|
||||
{
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2449,21 +2476,25 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
if (!is_fullscreen && !is_rendering_to_main)
|
||||
saveDisplayWindowGeometryToConfig();
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
auto* displayWindow = m_display_surface;
|
||||
#else
|
||||
auto* displayWindow = m_display_container;
|
||||
#endif
|
||||
if (fullscreen)
|
||||
{
|
||||
m_display_container->showFullScreen();
|
||||
}
|
||||
displayWindow->showFullScreen();
|
||||
else
|
||||
{
|
||||
// Needs to exit fullscreen before resizing
|
||||
displayWindow->showNormal();
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->setGeometry(geometry());
|
||||
displayWindow->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showNormal();
|
||||
}
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
updateWindowState();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
@@ -2500,7 +2531,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
|
||||
updateWindowState();
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_container->setFocus();
|
||||
m_display_surface->setFocus();
|
||||
return wi;
|
||||
}
|
||||
|
||||
@@ -2517,9 +2548,14 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
m_display_surface = new DisplaySurface();
|
||||
if (fullscreen || !render_to_main)
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->setTitle(windowTitle());
|
||||
m_display_surface->setIcon(windowIcon());
|
||||
#else
|
||||
m_display_container = m_display_surface->createWindowContainer();
|
||||
m_display_container->setWindowTitle(windowTitle());
|
||||
m_display_container->setWindowIcon(windowIcon());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2528,20 +2564,38 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
// On Wayland, while move/restoreGeometry can't position the window, it can influence which screen they show up on
|
||||
// On Wayland, while move/restoreGeometry can't position the window, it can influence which screen they show up on.
|
||||
// Other platforms can position windows fine, but the only thing that matters here is the screen.
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
m_display_surface->setFramePosition(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_surface->showFullScreen();
|
||||
#else
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showFullScreen();
|
||||
#endif
|
||||
}
|
||||
else if (!render_to_main)
|
||||
{
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_surface->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_surface->showNormal();
|
||||
#else
|
||||
if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain())
|
||||
m_display_container->setGeometry(geometry());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
m_display_container->showNormal();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2570,12 +2624,21 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
||||
width = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(width) / dpr)), 1));
|
||||
height = static_cast<qint32>(std::max(static_cast<int>(std::lroundf(static_cast<float>(height) / dpr)), 1));
|
||||
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (!m_display_container)
|
||||
{
|
||||
// no parent - rendering to separate window. easy.
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(m_display_surface, width, height);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (!m_display_container->parent())
|
||||
{
|
||||
// no parent - rendering to separate window. easy.
|
||||
QtUtils::ResizePotentiallyFixedSizeWindow(m_display_container, width, height);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// we are rendering to the main window. we have to add in the extra height from the toolbar/status bar.
|
||||
const s32 extra_height = this->height() - m_display_container->height();
|
||||
@@ -2608,7 +2671,7 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
if (!m_display_surface)
|
||||
return;
|
||||
|
||||
if (!isRenderingFullscreen() && !isRenderingToMain())
|
||||
if (!m_display_surface->isFullScreen() && !isRenderingToMain())
|
||||
saveDisplayWindowGeometryToConfig();
|
||||
|
||||
if (isRenderingToMain())
|
||||
@@ -2622,12 +2685,18 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
|
||||
}
|
||||
}
|
||||
|
||||
// displau surface is always in a container
|
||||
pxAssert(m_display_container != nullptr);
|
||||
m_display_container->deleteLater();
|
||||
m_display_container = nullptr;
|
||||
// m_display_surface will be destroyed by the container's dtor
|
||||
m_display_surface = nullptr;
|
||||
if (m_display_container)
|
||||
{
|
||||
m_display_container->deleteLater();
|
||||
m_display_container = nullptr;
|
||||
// m_display_surface will be destroyed by the container's dtor
|
||||
m_display_surface = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_display_surface->deleteLater();
|
||||
m_display_surface = nullptr;
|
||||
}
|
||||
|
||||
updateDisplayRelatedActions(false, false, false);
|
||||
}
|
||||
@@ -2639,14 +2708,6 @@ void MainWindow::updateDisplayWidgetCursor()
|
||||
m_display_surface->updateCursor(s_vm_valid && !s_vm_paused && shouldHideMouseCursor());
|
||||
}
|
||||
|
||||
void MainWindow::focusDisplayWidget()
|
||||
{
|
||||
if (!m_display_surface || centralWidget() != m_display_container)
|
||||
return;
|
||||
|
||||
m_display_container->setFocus();
|
||||
}
|
||||
|
||||
void MainWindow::setupMouseMoveHandler()
|
||||
{
|
||||
auto mouse_cb_fn = [](int x, int y) {
|
||||
@@ -2677,7 +2738,11 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
|
||||
// logical (DIP) frame rect
|
||||
const QSize logicalSize = displayWindow->size();
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
const QPoint logicalPosition = isRenderingToMain() ? (displayWindow->position() + displayWindow->parent()->position()) : displayWindow->position();
|
||||
#else
|
||||
const QPoint logicalPosition = displayWindow->position() + displayWindow->parent()->position();
|
||||
#endif
|
||||
|
||||
// The offset to the origin of the current screen is in device-independent pixels while the origin itself is native!
|
||||
// The logicalPosition is the sum of these two values, so we need to separate them and only scale the offset
|
||||
@@ -2708,13 +2773,13 @@ void MainWindow::checkMousePosition(int x, int y)
|
||||
|
||||
void MainWindow::saveDisplayWindowGeometryToConfig()
|
||||
{
|
||||
if (m_display_container->windowState() & Qt::WindowFullScreen)
|
||||
if (m_display_surface->isFullScreen())
|
||||
{
|
||||
// if we somehow ended up here, don't save the fullscreen state to the config
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray geometry = m_display_container->saveGeometry();
|
||||
const QByteArray geometry = m_display_surface->saveGeometry();
|
||||
const QByteArray geometry_b64 = geometry.toBase64();
|
||||
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry");
|
||||
if (old_geometry_b64 != geometry_b64.constData())
|
||||
@@ -2730,15 +2795,23 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig()
|
||||
const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64));
|
||||
if (!geometry.isEmpty())
|
||||
{
|
||||
m_display_container->restoreGeometry(geometry);
|
||||
m_display_surface->restoreGeometry(geometry);
|
||||
|
||||
// make sure we're not loading a dodgy config which had fullscreen set...
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->setWindowStates(m_display_surface->windowStates() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
#else
|
||||
m_display_container->setWindowState(m_display_container->windowState() & ~(Qt::WindowFullScreen | Qt::WindowActive));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// default size
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
m_display_surface->resize(640, 480);
|
||||
#else
|
||||
m_display_container->resize(640, 480);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3308,7 +3381,7 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
g_emu_thread->setFullscreen(false, false);
|
||||
|
||||
// Process events untill both EmuThread and Qt have finished exiting fullscreen
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() || m_display_container->isFullScreen()))
|
||||
while (QtHost::IsVMValid() && (g_emu_thread->isFullscreen() || m_display_surface->isFullScreen()))
|
||||
{
|
||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
}
|
||||
@@ -3320,7 +3393,27 @@ MainWindow::VMLock MainWindow::pauseAndLockVM()
|
||||
g_main_window->raise();
|
||||
g_main_window->activateWindow();
|
||||
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen);
|
||||
#ifdef DISPLAY_SURFACE_WINDOW
|
||||
if (!m_display_container)
|
||||
{
|
||||
// Create a temporary parent for the dialog.
|
||||
QWidget* dialog_parent = new QWidget();
|
||||
dialog_parent->setAttribute(Qt::WA_NativeWindow);
|
||||
QWindow* window_handle = dialog_parent->windowHandle();
|
||||
|
||||
// Set the transient parent to the display surface.
|
||||
// This will position the dialog_parent over the display surface (and thus so will any dialogs)
|
||||
// and also enforces the focus lock of modal dialogs against the display surface.
|
||||
// This works even without showing the dialog_parent window.
|
||||
window_handle->setTransientParent(m_display_surface);
|
||||
|
||||
return VMLock(dialog_parent, was_paused, was_fullscreen, true);
|
||||
}
|
||||
else
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen, false);
|
||||
#else
|
||||
return VMLock(m_display_container, was_paused, was_fullscreen, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::rescanFile(const std::string& path)
|
||||
@@ -3328,11 +3421,12 @@ void MainWindow::rescanFile(const std::string& path)
|
||||
m_game_list_widget->rescanFile(path);
|
||||
}
|
||||
|
||||
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen)
|
||||
MainWindow::VMLock::VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen, bool owns_parent)
|
||||
: m_dialog_parent(dialog_parent)
|
||||
, m_has_lock(true)
|
||||
, m_was_paused(was_paused)
|
||||
, m_was_fullscreen(was_fullscreen)
|
||||
, m_owns_dialog_parent(owns_parent)
|
||||
{
|
||||
QtHost::LockVMWithDialog();
|
||||
}
|
||||
@@ -3342,11 +3436,13 @@ MainWindow::VMLock::VMLock(VMLock&& lock)
|
||||
, m_has_lock(lock.m_has_lock)
|
||||
, m_was_paused(lock.m_was_paused)
|
||||
, m_was_fullscreen(lock.m_was_fullscreen)
|
||||
, m_owns_dialog_parent(lock.m_owns_dialog_parent)
|
||||
{
|
||||
lock.m_dialog_parent = nullptr;
|
||||
lock.m_has_lock = false;
|
||||
lock.m_was_paused = true;
|
||||
lock.m_was_fullscreen = false;
|
||||
lock.m_owns_dialog_parent = false;
|
||||
}
|
||||
|
||||
MainWindow::VMLock::~VMLock()
|
||||
@@ -3354,6 +3450,12 @@ MainWindow::VMLock::~VMLock()
|
||||
if (m_has_lock)
|
||||
QtHost::UnlockVMWithDialog();
|
||||
|
||||
if (m_owns_dialog_parent && m_dialog_parent)
|
||||
{
|
||||
m_dialog_parent->deleteLater();
|
||||
m_dialog_parent = nullptr;
|
||||
}
|
||||
|
||||
if (m_was_fullscreen)
|
||||
{
|
||||
g_main_window->m_is_temporarily_windowed = false;
|
||||
|
||||
@@ -65,13 +65,14 @@ public:
|
||||
void cancelResume();
|
||||
|
||||
private:
|
||||
VMLock(QWidget* dialog_parent, bool was_paused, bool was_exclusive_fullscreen);
|
||||
VMLock(QWidget* dialog_parent, bool was_paused, bool was_exclusive_fullscreen, bool owns_parent);
|
||||
friend MainWindow;
|
||||
|
||||
QWidget* m_dialog_parent;
|
||||
bool m_has_lock;
|
||||
bool m_was_paused;
|
||||
bool m_was_fullscreen;
|
||||
bool m_owns_dialog_parent;
|
||||
};
|
||||
|
||||
/// Default filter for opening a file.
|
||||
@@ -138,7 +139,6 @@ private Q_SLOTS:
|
||||
void displayResizeRequested(qint32 width, qint32 height);
|
||||
void mouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
void releaseRenderWindow();
|
||||
void focusDisplayWidget();
|
||||
void setupMouseMoveHandler();
|
||||
void onGameListRefreshComplete();
|
||||
void onGameListRefreshProgress(const QString& status, int current, int total);
|
||||
|
||||
@@ -366,6 +366,25 @@ namespace QtUtils
|
||||
}
|
||||
}
|
||||
|
||||
void SetWindowResizeable(QWindow* window, bool resizeable)
|
||||
{
|
||||
if (resizeable)
|
||||
{
|
||||
// Min/max numbers come from uic.
|
||||
window->setMinimumWidth(1);
|
||||
window->setMinimumHeight(1);
|
||||
window->setMaximumWidth(16777215);
|
||||
window->setMaximumHeight(16777215);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->setMinimumWidth(window->width());
|
||||
window->setMinimumHeight(window->height());
|
||||
window->setMaximumWidth(window->width());
|
||||
window->setMaximumHeight(window->height());
|
||||
}
|
||||
}
|
||||
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height)
|
||||
{
|
||||
width = std::max(width, 1);
|
||||
@@ -376,6 +395,22 @@ namespace QtUtils
|
||||
widget->resize(width, height);
|
||||
}
|
||||
|
||||
void ResizePotentiallyFixedSizeWindow(QWindow* window, int width, int height)
|
||||
{
|
||||
width = std::max(width, 1);
|
||||
height = std::max(height, 1);
|
||||
|
||||
if (window->minimumHeight() == window->maximumHeight())
|
||||
{
|
||||
window->setMinimumWidth(width);
|
||||
window->setMinimumHeight(height);
|
||||
window->setMaximumWidth(width);
|
||||
window->setMaximumHeight(height);
|
||||
}
|
||||
|
||||
window->resize(width, height);
|
||||
}
|
||||
|
||||
QString AbstractItemModelToCSV(QAbstractItemModel* model, int role, bool useQuotes)
|
||||
{
|
||||
QString csv;
|
||||
|
||||
@@ -102,9 +102,11 @@ namespace QtUtils
|
||||
|
||||
/// Changes whether a window is resizable.
|
||||
void SetWindowResizeable(QWidget* widget, bool resizeable);
|
||||
void SetWindowResizeable(QWindow* window, bool resizeable);
|
||||
|
||||
/// Adjusts the fixed size for a window if it's not resizeable.
|
||||
void ResizePotentiallyFixedSizeWindow(QWidget* widget, int width, int height);
|
||||
void ResizePotentiallyFixedSizeWindow(QWindow* window, int width, int height);
|
||||
|
||||
/// Returns the common window info structure for a Qt Window/Widget.
|
||||
template <class T>
|
||||
|
||||
@@ -17,7 +17,8 @@ AchievementLoginDialog::AchievementLoginDialog(QWidget* parent, Achievements::Lo
|
||||
, m_reason(reason)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
QtUtils::SetScalableIcon(m_ui.loginIcon, QIcon::fromTheme(QStringLiteral("login-box-line")), QSize(32, 32));
|
||||
const QString base_path(QtHost::GetResourcesBasePath());
|
||||
QtUtils::SetScalableIcon(m_ui.loginIcon, QIcon(QStringLiteral("%1/icons/ra-icon.svg").arg(base_path)), QSize(50, 50));
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
// Adjust text if needed based on reason.
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "common/HeterogeneousContainers.h"
|
||||
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
|
||||
GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
|
||||
@@ -40,6 +41,9 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
m_ui.cheatList->expandAll();
|
||||
|
||||
m_ui.cheatList->viewport()->installEventFilter(this);
|
||||
m_ui.cheatList->viewport()->setMouseTracking(true);
|
||||
|
||||
SettingsInterface* sif = dialog()->getSettingsInterface();
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.allCRCsCheckbox, "EmuCore", "ShowCheatsForAllCRCs", false);
|
||||
@@ -83,7 +87,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@@ -95,7 +99,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
|
||||
void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
{
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@@ -109,6 +113,31 @@ void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
setCheatEnabled(std::move(cheat_name), current_checked, true);
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::onCheatListItemHovered(const QModelIndex& index)
|
||||
{
|
||||
const QModelIndex source_index = m_model_proxy->mapToSource(index);
|
||||
const QModelIndex sibling_index = source_index.siblingAtColumn(0);
|
||||
QStandardItem* item = m_model->itemFromIndex(sibling_index);
|
||||
if (!item)
|
||||
{
|
||||
// No item is selected.
|
||||
m_ui.appliedLabel->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<Patch::patch_place_type> place;
|
||||
|
||||
bool ok;
|
||||
int place_value = item->data(PLACE_ROLE).toInt(&ok);
|
||||
if (ok)
|
||||
{
|
||||
// The patch commands in the group are all applied at the same time.
|
||||
place = static_cast<Patch::patch_place_type>(place_value);
|
||||
}
|
||||
|
||||
m_ui.appliedLabel->setText(tr("<strong>Applied:</strong> %1").arg(Patch::PlaceToString(place)));
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::onReloadClicked()
|
||||
{
|
||||
reloadList();
|
||||
@@ -136,6 +165,32 @@ void GameCheatSettingsWidget::disableAllCheats()
|
||||
si->Save();
|
||||
}
|
||||
|
||||
bool GameCheatSettingsWidget::eventFilter(QObject* watched, QEvent* event)
|
||||
{
|
||||
if (watched == m_ui.cheatList->viewport())
|
||||
{
|
||||
switch (event->type())
|
||||
{
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
onCheatListItemHovered(m_ui.cheatList->indexAt(mouse_event->position().toPoint()));
|
||||
return true;
|
||||
}
|
||||
case QEvent::Leave:
|
||||
{
|
||||
onCheatListItemHovered(QModelIndex());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SettingsWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void GameCheatSettingsWidget::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
@@ -185,7 +240,7 @@ void GameCheatSettingsWidget::setStateRecursively(QStandardItem* parent, bool en
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
QStandardItem* item = parent ? parent->child(i, 0) : m_model->item(i, 0);
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (data.isValid())
|
||||
{
|
||||
if ((item->checkState() == Qt::Checked) != enabled)
|
||||
@@ -277,7 +332,9 @@ QList<QStandardItem*> GameCheatSettingsWidget::populateTreeViewRow(const Patch::
|
||||
const std::string_view name_part = pi.GetNamePart();
|
||||
nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled);
|
||||
nameItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
||||
nameItem->setData(QString::fromStdString(pi.name), Qt::UserRole);
|
||||
nameItem->setData(QString::fromStdString(pi.name), NAME_ROLE);
|
||||
if (pi.place.has_value())
|
||||
nameItem->setData(static_cast<int>(*pi.place), PLACE_ROLE);
|
||||
if (!name_part.empty())
|
||||
nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length()));
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
~GameCheatSettingsWidget();
|
||||
|
||||
void disableAllCheats();
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
@@ -39,6 +40,7 @@ protected:
|
||||
private Q_SLOTS:
|
||||
void onCheatListItemDoubleClicked(const QModelIndex& index);
|
||||
void onCheatListItemChanged(QStandardItem* item);
|
||||
void onCheatListItemHovered(const QModelIndex& index);
|
||||
void onReloadClicked();
|
||||
void updateListEnabled();
|
||||
void reloadList();
|
||||
@@ -50,6 +52,12 @@ private:
|
||||
void setStateForAll(bool enabled);
|
||||
void setStateRecursively(QStandardItem* parent, bool enabled);
|
||||
|
||||
enum Roles
|
||||
{
|
||||
NAME_ROLE = Qt::UserRole,
|
||||
PLACE_ROLE = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
Ui::GameCheatSettingsWidget m_ui;
|
||||
QStandardItemModel* m_model = nullptr;
|
||||
QSortFilterProxyModel* m_model_proxy = nullptr;
|
||||
|
||||
@@ -90,6 +90,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="appliedLabel">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -165,11 +165,12 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
|
||||
// Enabling the debug layer will fail if the Graphics Tools feature is not installed.
|
||||
if (enable_debug_layer)
|
||||
{
|
||||
ComPtr<ID3D12Debug> debug12;
|
||||
ComPtr<ID3D12Debug1> debug12;
|
||||
hr = D3D12GetDebugInterface(IID_PPV_ARGS(debug12.put()));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
debug12->EnableDebugLayer();
|
||||
debug12->SetEnableGPUBasedValidation(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -342,12 +343,14 @@ bool GSDevice12::CreateCommandLists()
|
||||
|
||||
void GSDevice12::MoveToNextCommandList()
|
||||
{
|
||||
m_current_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
||||
m_current_fence_value++;
|
||||
const int next_command_list = (m_current_command_list + 1) % NUM_COMMAND_LISTS;
|
||||
|
||||
// We may have to wait if this command list hasn't finished on the GPU.
|
||||
CommandListResources& res = m_command_lists[m_current_command_list];
|
||||
CommandListResources& res = m_command_lists[next_command_list];
|
||||
WaitForFence(res.ready_fence_value, false);
|
||||
|
||||
m_current_command_list = next_command_list;
|
||||
m_current_fence_value++;
|
||||
res.ready_fence_value = m_current_fence_value;
|
||||
res.init_command_list_used = false;
|
||||
|
||||
@@ -1224,8 +1227,8 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
|
||||
{
|
||||
//const bool isAMD = (vendor_id == 0x1002 || vendor_id == 0x1022);
|
||||
|
||||
m_features.texture_barrier = false;
|
||||
m_features.multidraw_fb_copy = GSConfig.OverrideTextureBarriers != 0;
|
||||
m_features.texture_barrier = GSConfig.OverrideTextureBarriers != 0;
|
||||
m_features.multidraw_fb_copy = false;
|
||||
m_features.broken_point_sampler = false;
|
||||
m_features.primitive_id = true;
|
||||
m_features.prefer_new_textures = true;
|
||||
@@ -2178,13 +2181,13 @@ void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
|
||||
m_index_stream_buffer.CommitMemory(size);
|
||||
}
|
||||
|
||||
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor)
|
||||
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, bool depth_read)
|
||||
{
|
||||
GSTexture12* vkRt = static_cast<GSTexture12*>(rt);
|
||||
GSTexture12* vkDs = static_cast<GSTexture12*>(ds);
|
||||
pxAssert(vkRt || vkDs);
|
||||
|
||||
if (m_current_render_target != vkRt || m_current_depth_target != vkDs)
|
||||
if (m_current_render_target != vkRt || m_current_depth_target != vkDs || m_current_depth_read_only != depth_read)
|
||||
{
|
||||
// framebuffer change
|
||||
EndRenderPass();
|
||||
@@ -2211,13 +2214,14 @@ void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
||||
|
||||
m_current_render_target = vkRt;
|
||||
m_current_depth_target = vkDs;
|
||||
m_current_depth_read_only = depth_read;
|
||||
|
||||
if (!InRenderPass())
|
||||
{
|
||||
if (vkRt)
|
||||
vkRt->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
if (vkDs)
|
||||
vkDs->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
vkDs->TransitionToState(depth_read ? (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) : D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
}
|
||||
|
||||
// This is used to set/initialize the framebuffer for tfx rendering.
|
||||
@@ -3207,7 +3211,7 @@ void GSDevice12::SetStencilRef(u8 ref)
|
||||
m_dirty_flags |= DIRTY_FLAG_STENCIL_REF;
|
||||
}
|
||||
|
||||
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
|
||||
void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback)
|
||||
{
|
||||
D3D12DescriptorHandle handle;
|
||||
if (sr)
|
||||
@@ -3225,7 +3229,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
|
||||
dtex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
}
|
||||
dtex->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
handle = dtex->GetSRVDescriptor();
|
||||
handle = feedback ? dtex->GetFBLDescriptor() : dtex->GetSRVDescriptor();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3312,7 +3316,7 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
|
||||
{
|
||||
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
|
||||
{
|
||||
if (m_tfx_textures[i] == tex->GetSRVDescriptor())
|
||||
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
|
||||
{
|
||||
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_TEXTURES;
|
||||
@@ -3327,6 +3331,7 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
|
||||
{
|
||||
EndRenderPass();
|
||||
m_current_depth_target = nullptr;
|
||||
m_current_depth_read_only = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3447,7 +3452,7 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC ds = {};
|
||||
if (m_current_depth_target)
|
||||
{
|
||||
ds.cpuDescriptor = m_current_depth_target->GetWriteDescriptor();
|
||||
ds.cpuDescriptor = m_current_depth_read_only ? m_current_depth_target->GetReadDepthViewDescriptor() : m_current_depth_target->GetWriteDescriptor();
|
||||
ds.DepthEndingAccess.Type = depth_end;
|
||||
ds.DepthBeginningAccess.Type = depth_begin;
|
||||
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
|
||||
@@ -3467,7 +3472,8 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
}
|
||||
|
||||
GetCommandList()->BeginRenderPass(m_current_render_target ? 1 : 0,
|
||||
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr, D3D12_RENDER_PASS_FLAG_NONE);
|
||||
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr,
|
||||
(m_current_depth_target && m_current_depth_read_only) ? (D3D12_RENDER_PASS_FLAG_BIND_READ_ONLY_DEPTH) : D3D12_RENDER_PASS_FLAG_NONE);
|
||||
}
|
||||
|
||||
void GSDevice12::EndRenderPass()
|
||||
@@ -3549,11 +3555,13 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
|
||||
if (m_current_render_target)
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(1, &m_current_render_target->GetWriteDescriptor().cpu_handle, FALSE,
|
||||
m_current_depth_target ? &m_current_depth_target->GetWriteDescriptor().cpu_handle : nullptr);
|
||||
m_current_depth_target ?
|
||||
(m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle) :
|
||||
nullptr);
|
||||
}
|
||||
else if (m_current_depth_target)
|
||||
{
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, &m_current_depth_target->GetWriteDescriptor().cpu_handle);
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, m_current_depth_read_only ? &m_current_depth_target->GetReadDepthViewDescriptor().cpu_handle : &m_current_depth_target->GetWriteDescriptor().cpu_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3826,7 +3834,20 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
// TODO: Backport from vk.
|
||||
if (stencil_DATE_One)
|
||||
{
|
||||
config.ps.date = 0;
|
||||
config.alpha_second_pass.ps.date = 0;
|
||||
if (!config.ps.IsFeedbackLoop())
|
||||
{
|
||||
config.require_one_barrier = false;
|
||||
config.require_full_barrier = false;
|
||||
}
|
||||
if (!config.alpha_second_pass.ps.IsFeedbackLoop())
|
||||
{
|
||||
config.alpha_second_pass.require_one_barrier = false;
|
||||
config.alpha_second_pass.require_full_barrier = false;
|
||||
}
|
||||
}
|
||||
|
||||
GSTexture12* colclip_rt = static_cast<GSTexture12*>(g_gs_device->GetColorClipTexture());
|
||||
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
|
||||
@@ -3841,6 +3862,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
// figure out the pipeline
|
||||
UpdateHWPipelineSelector(config);
|
||||
|
||||
// Handle RT hazard when no barrier was requested
|
||||
if (m_features.texture_barrier && config.tex && (config.tex == config.rt) && !(config.require_one_barrier || config.require_full_barrier))
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
// now blit the colclip texture back to the original target
|
||||
if (colclip_rt)
|
||||
{
|
||||
@@ -3896,15 +3929,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
if (config.blend.constant_enable)
|
||||
SetBlendConstants(config.blend.constant);
|
||||
|
||||
// Depth testing and sampling, bind resource as dsv read only and srv at the same time without the need of a copy.
|
||||
if (config.tex && config.tex == config.ds)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
// Transition dsv as read only.
|
||||
draw_ds->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_READ);
|
||||
}
|
||||
|
||||
// Primitive ID tracking DATE setup.
|
||||
GSTexture12* date_image = nullptr;
|
||||
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
|
||||
@@ -3955,7 +3979,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
// we're not drawing to the RT, so we can use it as a source
|
||||
if (config.require_one_barrier && !m_features.multidraw_fb_copy)
|
||||
if (config.require_one_barrier && !m_features.texture_barrier)
|
||||
PSSetShaderResource(2, draw_rt, true);
|
||||
}
|
||||
|
||||
@@ -3985,16 +4009,29 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
m_pipeline_selector.ds = true;
|
||||
}
|
||||
|
||||
if (draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy) || (config.tex && config.tex == config.rt)))
|
||||
const bool feedback = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
|
||||
if (feedback && !m_features.texture_barrier)
|
||||
{
|
||||
// Requires a copy of the RT.
|
||||
// Used as "bind rt" flag when texture barrier is unsupported for tex is fb.
|
||||
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true));
|
||||
if (!draw_rt_clone)
|
||||
if (draw_rt_clone)
|
||||
{
|
||||
GL_PUSH("D3D12: Copy RT to temp texture {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
EndRenderPass();
|
||||
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
if (config.require_one_barrier)
|
||||
PSSetShaderResource(2, draw_rt_clone, true);
|
||||
if (config.tex && config.tex == config.rt)
|
||||
PSSetShaderResource(0, draw_rt_clone, true);
|
||||
}
|
||||
else
|
||||
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
|
||||
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, config.tex && config.tex == config.ds);
|
||||
|
||||
// Begin render pass if new target or out of the area.
|
||||
if (!m_in_render_pass)
|
||||
@@ -4011,8 +4048,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
|
||||
stencil_DATE ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE :
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
|
||||
stencil_DATE ? (draw_rt_clone ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE :
|
||||
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD) :
|
||||
stencil_DATE ? (feedback ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE :
|
||||
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD) :
|
||||
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
|
||||
clear_color, draw_ds ? draw_ds->GetClearDepth() : 0.0f, 1);
|
||||
}
|
||||
@@ -4040,7 +4077,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
UploadHWDrawVerticesAndIndices(config);
|
||||
|
||||
// now we can do the actual draw
|
||||
SendHWDraw(pipe, config, draw_rt_clone, draw_rt, config.require_one_barrier, config.require_full_barrier, false);
|
||||
SendHWDraw(pipe, config, draw_rt, feedback, config.require_one_barrier, config.require_full_barrier);
|
||||
|
||||
// blend second pass
|
||||
if (config.blend_multi_pass.enable)
|
||||
@@ -4070,15 +4107,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
pipe.cms = config.alpha_second_pass.colormask;
|
||||
pipe.dss = config.alpha_second_pass.depth;
|
||||
pipe.bs = config.blend;
|
||||
SendHWDraw(pipe, config, draw_rt_clone, draw_rt, config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier, true);
|
||||
SendHWDraw(pipe, config, draw_rt, feedback, config.alpha_second_pass.require_one_barrier, config.alpha_second_pass.require_full_barrier);
|
||||
}
|
||||
|
||||
if (draw_rt_clone)
|
||||
Recycle(draw_rt_clone);
|
||||
|
||||
if (date_image)
|
||||
Recycle(date_image);
|
||||
|
||||
if (draw_rt_clone)
|
||||
Recycle(draw_rt_clone);
|
||||
|
||||
// now blit the colclip texture back to the original target
|
||||
if (colclip_rt)
|
||||
{
|
||||
@@ -4113,43 +4150,45 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt_clone, GSTexture12* draw_rt, const bool one_barrier, const bool full_barrier, const bool skip_first_barrier)
|
||||
void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt, const bool feedback, const bool one_barrier, const bool full_barrier)
|
||||
{
|
||||
if (draw_rt_clone)
|
||||
if (BindDrawPipeline(pipe) && !m_features.texture_barrier) [[unlikely]]
|
||||
{
|
||||
DrawIndexedPrimitive();
|
||||
return;
|
||||
}
|
||||
|
||||
if (feedback)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
if ((one_barrier || full_barrier) && !config.ps.IsFeedbackLoop()) [[unlikely]]
|
||||
Console.Warning("D3D12: Possible unnecessary copy detected.");
|
||||
Console.Warning("D3D12: Possible unnecessary barrier detected.");
|
||||
#endif
|
||||
auto CopyAndBind = [&](GSVector4i drawarea) {
|
||||
EndRenderPass();
|
||||
if (one_barrier || full_barrier)
|
||||
PSSetShaderResource(2, draw_rt, false, true);
|
||||
if (config.tex && config.tex == config.rt)
|
||||
PSSetShaderResource(0, draw_rt, false, true);
|
||||
|
||||
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
|
||||
draw_rt->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
|
||||
if (one_barrier || full_barrier)
|
||||
PSSetShaderResource(2, draw_rt_clone, true);
|
||||
if (config.tex && config.tex == config.rt)
|
||||
PSSetShaderResource(0, draw_rt_clone, true);
|
||||
};
|
||||
|
||||
if (m_features.multidraw_fb_copy && full_barrier)
|
||||
if (full_barrier)
|
||||
{
|
||||
pxAssert(config.drawlist && !config.drawlist->empty());
|
||||
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
|
||||
const u32 indices_per_prim = config.indices_per_prim;
|
||||
|
||||
pxAssert(config.drawlist && !config.drawlist->empty());
|
||||
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
|
||||
GL_PUSH("Split the draw");
|
||||
g_perfmon.Put(GSPerfMon::Barriers, draw_list_size);
|
||||
|
||||
for (u32 n = 0, p = 0; n < draw_list_size; n++)
|
||||
{
|
||||
const u32 count = (*config.drawlist)[n] * indices_per_prim;
|
||||
|
||||
GSVector4i bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
// While this may also be true before the barrier, we only write using the main resource.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
|
||||
// Copy only the part needed by the draw.
|
||||
CopyAndBind(bbox);
|
||||
if (BindDrawPipeline(pipe))
|
||||
DrawIndexedPrimitive(p, count);
|
||||
p += count;
|
||||
@@ -4158,10 +4197,16 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
return;
|
||||
}
|
||||
|
||||
if (one_barrier)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
|
||||
if (!skip_first_barrier)
|
||||
CopyAndBind(config.drawarea);
|
||||
EndRenderPass();
|
||||
// Specify null for the after resource as both resources are used after the barrier.
|
||||
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
|
||||
barrier.Aliasing = {draw_rt->GetResource(), nullptr};
|
||||
GetCommandList()->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
}
|
||||
|
||||
if (BindDrawPipeline(pipe))
|
||||
|
||||
@@ -456,17 +456,17 @@ public:
|
||||
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count);
|
||||
void IASetIndexBuffer(const void* index, size_t count);
|
||||
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state);
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor);
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, bool depth_read = false);
|
||||
|
||||
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
|
||||
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
|
||||
bool BindDrawPipeline(const PipelineSelector& p);
|
||||
|
||||
void RenderHW(GSHWDrawConfig& config) override;
|
||||
void SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt_clone, GSTexture12* draw_rt, const bool one_barrier, const bool full_barrier, const bool skip_first_barrier);
|
||||
void SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig& config, GSTexture12* draw_rt, const bool feedback, const bool one_barrier, const bool full_barrier);
|
||||
|
||||
void UpdateHWPipelineSelector(GSHWDrawConfig& config);
|
||||
void UploadHWDrawVerticesAndIndices(const GSHWDrawConfig& config);
|
||||
@@ -580,6 +580,7 @@ private:
|
||||
|
||||
GSTexture12* m_current_render_target = nullptr;
|
||||
GSTexture12* m_current_depth_target = nullptr;
|
||||
bool m_current_depth_read_only = false;
|
||||
|
||||
D3D12_VIEWPORT m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
GSVector4i m_scissor = GSVector4i::zero();
|
||||
|
||||
@@ -15,14 +15,19 @@
|
||||
#include "D3D12MemAlloc.h"
|
||||
|
||||
GSTexture12::GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<D3D12MA::Allocation> allocation,
|
||||
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<ID3D12Resource> resource_fbl,
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
|
||||
: m_resource(std::move(resource))
|
||||
, m_resource_fbl(std::move(resource_fbl))
|
||||
, m_allocation(std::move(allocation))
|
||||
, m_srv_descriptor(srv_descriptor)
|
||||
, m_write_descriptor(write_descriptor)
|
||||
, m_read_dsv_descriptor(ro_dsv_descriptor)
|
||||
, m_uav_descriptor(uav_descriptor)
|
||||
, m_fbl_descriptor(fbl_descriptor)
|
||||
, m_write_descriptor_type(wdtype)
|
||||
, m_dxgi_format(dxgi_format)
|
||||
, m_resource_state(resource_state)
|
||||
@@ -55,6 +60,7 @@ void GSTexture12::Destroy(bool defer)
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->DeferDescriptorDestruction(dev->GetDSVHeapManager(), &m_write_descriptor);
|
||||
dev->DeferDescriptorDestruction(dev->GetDSVHeapManager(), &m_read_dsv_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::None:
|
||||
default:
|
||||
@@ -64,8 +70,13 @@ void GSTexture12::Destroy(bool defer)
|
||||
if (m_uav_descriptor)
|
||||
dev->DeferDescriptorDestruction(dev->GetDescriptorHeapManager(), &m_uav_descriptor);
|
||||
|
||||
if (m_fbl_descriptor)
|
||||
dev->DeferDescriptorDestruction(dev->GetDescriptorHeapManager(), &m_fbl_descriptor);
|
||||
|
||||
dev->DeferResourceDestruction(m_allocation.get(), m_resource.get());
|
||||
dev->DeferResourceDestruction(m_allocation.get(), m_resource_fbl.get());
|
||||
m_resource.reset();
|
||||
m_resource_fbl.reset();
|
||||
m_allocation.reset();
|
||||
}
|
||||
else
|
||||
@@ -79,6 +90,7 @@ void GSTexture12::Destroy(bool defer)
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&m_write_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&m_read_dsv_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::None:
|
||||
default:
|
||||
@@ -88,7 +100,11 @@ void GSTexture12::Destroy(bool defer)
|
||||
if (m_uav_descriptor)
|
||||
dev->GetDescriptorHeapManager().Free(&m_uav_descriptor);
|
||||
|
||||
if (m_fbl_descriptor)
|
||||
dev->GetDescriptorHeapManager().Free(&m_fbl_descriptor);
|
||||
|
||||
m_resource.reset();
|
||||
m_resource_fbl.reset();
|
||||
m_allocation.reset();
|
||||
}
|
||||
|
||||
@@ -135,7 +151,9 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
// RT's tend to be larger, so we'll keep them committed for speed.
|
||||
pxAssert(levels == 1);
|
||||
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
||||
allocationDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE;
|
||||
optimized_clear_value.Format = rtv_format;
|
||||
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
}
|
||||
@@ -167,20 +185,63 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource;
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource_fbl;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
HRESULT hr = dev->GetAllocator()->CreateResource(&allocationDesc, &desc, state,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr, allocation.put(),
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Create texture failed: 0x%08X", hr);
|
||||
|
||||
return {};
|
||||
if (type == Type::RenderTarget)
|
||||
{
|
||||
const D3D12_RESOURCE_ALLOCATION_INFO allocInfo = dev->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
|
||||
|
||||
HRESULT hr = dev->GetAllocator()->AllocateMemory(&allocationDesc, &allocInfo, allocation.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Allocate texture memory failed: 0x%08X", hr);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc, state,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr,
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Create texture resource 1 failed: 0x%08X", hr);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
hr = dev->GetAllocator()->CreateAliasingResource(allocation.get(), 0, &desc, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr,
|
||||
IID_PPV_ARGS(resource_fbl.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Create texture resource 2 failed: 0x%08X", hr);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HRESULT hr = dev->GetAllocator()->CreateResource(&allocationDesc, &desc, state,
|
||||
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr, allocation.put(),
|
||||
IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Create texture failed: 0x%08X", hr);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
@@ -195,7 +256,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
write_descriptor_type = WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
dev->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -204,9 +265,15 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
case Type::DepthStencil:
|
||||
{
|
||||
write_descriptor_type = WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor, false))
|
||||
{
|
||||
dev->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &ro_dsv_descriptor, true))
|
||||
{
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -218,14 +285,47 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int w
|
||||
|
||||
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&write_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (resource_fbl)
|
||||
{
|
||||
if (!CreateSRVDescriptor(resource_fbl.get(), levels, srv_format, &fbl_descriptor))
|
||||
{
|
||||
dev->GetDescriptorHeapManager().Free(&uav_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
dev->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
dev->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
dev->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<GSTexture12>(
|
||||
new GSTexture12(type, format, width, height, levels, dxgi_format, std::move(resource), std::move(allocation),
|
||||
srv_descriptor, write_descriptor, uav_descriptor, write_descriptor_type, state));
|
||||
new GSTexture12(type, format, width, height, levels, dxgi_format, std::move(resource), std::move(resource_fbl), std::move(allocation),
|
||||
srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor, fbl_descriptor, write_descriptor_type, state));
|
||||
}
|
||||
|
||||
std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
|
||||
@@ -234,7 +334,7 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
{
|
||||
const D3D12_RESOURCE_DESC desc = resource->GetDesc();
|
||||
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
|
||||
D3D12DescriptorHandle srv_descriptor, write_descriptor, ro_dsv_descriptor, uav_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
@@ -247,16 +347,22 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
write_descriptor_type = WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (type == Type::DepthStencil)
|
||||
{
|
||||
write_descriptor_type = WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor, false))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &ro_dsv_descriptor, true))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&write_descriptor);
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -265,15 +371,27 @@ std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resou
|
||||
{
|
||||
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
|
||||
{
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&write_descriptor);
|
||||
switch (write_descriptor_type)
|
||||
{
|
||||
case WriteDescriptorType::RTV:
|
||||
GSDevice12::GetInstance()->GetRTVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
case WriteDescriptorType::DSV:
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&ro_dsv_descriptor);
|
||||
GSDevice12::GetInstance()->GetDSVHeapManager().Free(&write_descriptor);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GSDevice12::GetInstance()->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<GSTexture12>(new GSTexture12(type, format, static_cast<u32>(desc.Width), desc.Height,
|
||||
desc.MipLevels, desc.Format, std::move(resource), {}, srv_descriptor, write_descriptor, uav_descriptor,
|
||||
write_descriptor_type, resource_state));
|
||||
desc.MipLevels, desc.Format, std::move(resource), {}, {}, srv_descriptor, write_descriptor, {}, uav_descriptor,
|
||||
{}, write_descriptor_type, resource_state));
|
||||
}
|
||||
|
||||
bool GSTexture12::CreateSRVDescriptor(
|
||||
@@ -297,7 +415,7 @@ bool GSTexture12::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT form
|
||||
{
|
||||
if (!GSDevice12::GetInstance()->GetRTVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
Console.Error("Failed to allocate RTV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -306,15 +424,15 @@ bool GSTexture12::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT form
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
|
||||
bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh, bool read_only)
|
||||
{
|
||||
if (!GSDevice12::GetInstance()->GetDSVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
Console.Error("Failed to allocate DSV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE};
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, read_only ? D3D12_DSV_FLAG_READ_ONLY_DEPTH : D3D12_DSV_FLAG_NONE };
|
||||
GSDevice12::GetInstance()->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
@@ -609,7 +727,36 @@ void GSTexture12::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RE
|
||||
if (m_resource_state == state)
|
||||
return;
|
||||
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
// Read only depth requires special handling as we might want to write stencil.
|
||||
// Also batch the transition barriers as per recommendation from docs.
|
||||
if (state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition to read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, m_resource_state, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, m_resource_state, D3D12_RESOURCE_STATE_DEPTH_WRITE}}},
|
||||
};
|
||||
GSDevice12::GetInstance()->GetCommandList()->ResourceBarrier(m_resource_state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else if (m_resource_state == (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
// Transition from read depth/write stencil
|
||||
const D3D12_RESOURCE_BARRIER barriers[2] = {
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 0, (D3D12_RESOURCE_STATE_DEPTH_READ | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE), state}}},
|
||||
{D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), 1, D3D12_RESOURCE_STATE_DEPTH_WRITE, state}}},
|
||||
};
|
||||
GSDevice12::GetInstance()->GetCommandList()->ResourceBarrier(state == D3D12_RESOURCE_STATE_DEPTH_WRITE ? 1 : 2, barriers);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal transition
|
||||
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
|
||||
}
|
||||
|
||||
m_resource_state = state;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,13 @@ public:
|
||||
|
||||
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetReadDepthViewDescriptor() const { return m_read_dsv_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetUAVDescriptor() const { return m_uav_descriptor; }
|
||||
__fi const D3D12DescriptorHandle& GetFBLDescriptor() const { return m_fbl_descriptor; }
|
||||
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_resource_state; }
|
||||
__fi DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; }
|
||||
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
|
||||
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
|
||||
|
||||
void* GetNativeHandle() const override;
|
||||
|
||||
@@ -68,14 +71,16 @@ private:
|
||||
};
|
||||
|
||||
GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<D3D12MA::Allocation> allocation,
|
||||
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<ID3D12Resource> resource_fbl,
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
|
||||
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& ro_dsv_descriptor,
|
||||
const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& fbl_descriptor,
|
||||
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
|
||||
|
||||
static bool CreateSRVDescriptor(
|
||||
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh, bool read_only);
|
||||
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
|
||||
|
||||
ID3D12GraphicsCommandList* GetCommandBufferForUpdate();
|
||||
@@ -83,11 +88,14 @@ private:
|
||||
void CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> m_resource;
|
||||
wil::com_ptr_nothrow<ID3D12Resource> m_resource_fbl;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> m_allocation;
|
||||
|
||||
D3D12DescriptorHandle m_srv_descriptor = {};
|
||||
D3D12DescriptorHandle m_write_descriptor = {};
|
||||
D3D12DescriptorHandle m_read_dsv_descriptor = {};
|
||||
D3D12DescriptorHandle m_uav_descriptor = {};
|
||||
D3D12DescriptorHandle m_fbl_descriptor = {};
|
||||
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
|
||||
|
||||
DXGI_FORMAT m_dxgi_format = DXGI_FORMAT_UNKNOWN;
|
||||
|
||||
@@ -8037,6 +8037,14 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale));
|
||||
m_conf.scissor = (DATE && !DATE_BARRIER) ? m_conf.drawarea : scissor;
|
||||
|
||||
// ComputeDrawlistGetSize expects the original index layout, so needs to be called before we modify it via HandleProvokingVertexFirst/SetupIA.
|
||||
if (m_conf.require_full_barrier && (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
|
||||
{
|
||||
ComputeDrawlistGetSize(rt->m_scale);
|
||||
m_conf.drawlist = &m_drawlist;
|
||||
m_conf.drawlist_bbox = &m_drawlist_bbox;
|
||||
}
|
||||
|
||||
HandleProvokingVertexFirst();
|
||||
|
||||
SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0);
|
||||
@@ -8125,13 +8133,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
m_conf.alpha_second_pass.enable = false;
|
||||
}
|
||||
|
||||
if (m_conf.require_full_barrier && (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
|
||||
{
|
||||
ComputeDrawlistGetSize(rt->m_scale);
|
||||
m_conf.drawlist = &m_drawlist;
|
||||
m_conf.drawlist_bbox = &m_drawlist_bbox;
|
||||
}
|
||||
|
||||
if (!m_channel_shuffle_width)
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
else
|
||||
|
||||
@@ -2430,6 +2430,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
|
||||
|
||||
GSTexture* primid_texture = nullptr;
|
||||
GSTexture* draw_rt_clone = nullptr;
|
||||
GSTexture* colclip_rt = g_gs_device->GetColorClipTexture();
|
||||
|
||||
if (colclip_rt)
|
||||
@@ -2505,23 +2506,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
break;
|
||||
}
|
||||
|
||||
GSTexture* draw_rt_clone = nullptr;
|
||||
|
||||
if (config.require_one_barrier && !m_features.texture_barrier)
|
||||
{
|
||||
// Requires a copy of the RT.
|
||||
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true);
|
||||
if (draw_rt_clone)
|
||||
{
|
||||
GL_PUSH("GL: Copy RT to temp texture {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
CopyRect(colclip_rt ? colclip_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
}
|
||||
else
|
||||
Console.Warning("GL: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
IASetVertexBuffer(config.verts, config.nverts, GetVertexAlignment(config.vs.expand));
|
||||
m_vertex.start *= GetExpansionFactor(config.vs.expand);
|
||||
|
||||
@@ -2550,9 +2534,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
PSSetShaderResource(0, config.tex);
|
||||
if (config.pal)
|
||||
PSSetShaderResource(1, config.pal);
|
||||
if (draw_rt_clone)
|
||||
PSSetShaderResource(2, draw_rt_clone);
|
||||
else if (config.require_one_barrier || config.require_full_barrier)
|
||||
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
|
||||
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
|
||||
|
||||
SetupSampler(config.sampler);
|
||||
@@ -2668,6 +2650,25 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
glTextureBarrier();
|
||||
}
|
||||
|
||||
if (draw_rt && (config.require_one_barrier || (config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
|
||||
{
|
||||
// Requires a copy of the RT.
|
||||
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
|
||||
if (draw_rt_clone)
|
||||
{
|
||||
GL_PUSH("GL: Copy RT to temp texture {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
if (config.require_one_barrier)
|
||||
PSSetShaderResource(2, draw_rt_clone);
|
||||
if (config.tex && config.tex == config.rt)
|
||||
PSSetShaderResource(0, draw_rt_clone);
|
||||
}
|
||||
else
|
||||
Console.Warning("GL: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
|
||||
OMSetColorMaskState(config.colormask);
|
||||
SetupOM(config.depth);
|
||||
|
||||
@@ -206,8 +206,18 @@ bool GSDeviceVK::SelectInstanceExtensions(ExtensionList* extension_list, const W
|
||||
if (enable_debug_utils && !SupportsExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false))
|
||||
Console.Warning("VK: Debug report requested, but extension is not available.");
|
||||
|
||||
oe->vk_ext_swapchain_maintenance1 = (wi.type != WindowInfo::Type::Surfaceless &&
|
||||
SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false));
|
||||
oe->vk_swapchain_maintenance1 = wi.type != WindowInfo::Type::Surfaceless;
|
||||
if (wi.type != WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
oe->vk_swapchain_maintenance1 = true;
|
||||
// VK_EXT_swapchain_maintenance1 requires VK_EXT_surface_maintenance1.
|
||||
// VK_KHR_swapchain_maintenance1 might require VK_KHR_surface_maintenance1 (It does on Nvidia).
|
||||
// If either VK_KHR_surface_maintenance1 is supported, or VK_EXT_swapchain_maintenance1 is unsupported, don't try VK_EXT_swapchain_maintenance1.
|
||||
oe->vk_swapchain_maintenance1_is_khr = SupportsExtension(VK_KHR_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false) ||
|
||||
!SupportsExtension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME, false);
|
||||
}
|
||||
else
|
||||
oe->vk_swapchain_maintenance1 = false;
|
||||
|
||||
// Needed for exclusive fullscreen control.
|
||||
SupportsExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false);
|
||||
@@ -411,6 +421,18 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
|
||||
m_optional_extensions.vk_ext_line_rasterization = SupportsExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
|
||||
m_optional_extensions.vk_khr_driver_properties = SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false);
|
||||
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1)
|
||||
{
|
||||
const bool khr_swapchain_maintenance1 = SupportsExtension(VK_KHR_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false);
|
||||
// vk_swapchain_maintenance1_is_khr will be set if we havn't enabled VK_EXT_surface_maintenance1
|
||||
// This will happen if either the VK_EXT_surface_maintenance1 was unsupported, or we instead found the KHR version.
|
||||
// As the EXT version depends on the surface maintenance1 extension, we need to check that aswell.
|
||||
m_optional_extensions.vk_swapchain_maintenance1 = khr_swapchain_maintenance1 ? khr_swapchain_maintenance1 :
|
||||
(!m_optional_extensions.vk_swapchain_maintenance1_is_khr && SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false));
|
||||
|
||||
m_optional_extensions.vk_swapchain_maintenance1_is_khr = khr_swapchain_maintenance1;
|
||||
}
|
||||
|
||||
// glslang generates debug info instructions before phi nodes at the beginning of blocks when non-semantic debug info
|
||||
// is enabled, triggering errors by spirv-val. Gate it by an environment variable if you want source debugging until
|
||||
// this is fixed.
|
||||
@@ -420,10 +442,6 @@ bool GSDeviceVK::SelectDeviceExtensions(ExtensionList* extension_list, bool enab
|
||||
SupportsExtension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, false);
|
||||
}
|
||||
|
||||
m_optional_extensions.vk_ext_swapchain_maintenance1 =
|
||||
m_optional_extensions.vk_ext_swapchain_maintenance1 &&
|
||||
SupportsExtension(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME, false);
|
||||
|
||||
#ifdef _WIN32
|
||||
m_optional_extensions.vk_ext_full_screen_exclusive =
|
||||
enable_surface && SupportsExtension(VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME, false);
|
||||
@@ -611,8 +629,10 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
|
||||
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_feature = {
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_khr_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR};
|
||||
|
||||
if (m_optional_extensions.vk_ext_provoking_vertex)
|
||||
{
|
||||
@@ -634,10 +654,18 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
attachment_feedback_loop_feature.attachmentFeedbackLoopLayout = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &attachment_feedback_loop_feature);
|
||||
}
|
||||
if (m_optional_extensions.vk_ext_swapchain_maintenance1)
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1)
|
||||
{
|
||||
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
{
|
||||
swapchain_maintenance1_khr_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_khr_feature);
|
||||
}
|
||||
else
|
||||
{
|
||||
swapchain_maintenance1_ext_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_ext_feature);
|
||||
}
|
||||
}
|
||||
|
||||
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
|
||||
@@ -704,8 +732,10 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
|
||||
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_feature = {
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr, VK_TRUE};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_khr_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR, nullptr, VK_TRUE};
|
||||
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
|
||||
|
||||
@@ -718,8 +748,10 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature);
|
||||
if (m_optional_extensions.vk_ext_attachment_feedback_loop_layout)
|
||||
Vulkan::AddPointerToChain(&features2, &attachment_feedback_loop_feature);
|
||||
if (m_optional_extensions.vk_ext_swapchain_maintenance1)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1 && m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_khr_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1 && !m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_ext_feature);
|
||||
|
||||
// query
|
||||
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
|
||||
@@ -794,8 +826,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
m_optional_extensions.vk_ext_calibrated_timestamps = false;
|
||||
}
|
||||
|
||||
m_optional_extensions.vk_ext_swapchain_maintenance1 &=
|
||||
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
|
||||
m_optional_extensions.vk_swapchain_maintenance1 &= m_optional_extensions.vk_swapchain_maintenance1_is_khr ?
|
||||
(swapchain_maintenance1_khr_feature.swapchainMaintenance1 == VK_TRUE) :
|
||||
(swapchain_maintenance1_ext_feature.swapchainMaintenance1 == VK_TRUE);
|
||||
|
||||
Console.WriteLn(
|
||||
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
|
||||
@@ -805,8 +838,9 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
m_optional_extensions.vk_ext_calibrated_timestamps ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_EXT_rasterization_order_attachment_access is %s",
|
||||
m_optional_extensions.vk_ext_rasterization_order_attachment_access ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_EXT_swapchain_maintenance1 is %s",
|
||||
m_optional_extensions.vk_ext_swapchain_maintenance1 ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_%s_swapchain_maintenance1 is %s",
|
||||
m_optional_extensions.vk_swapchain_maintenance1_is_khr ? "KHR" : "EXT",
|
||||
m_optional_extensions.vk_swapchain_maintenance1 ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_EXT_full_screen_exclusive is %s",
|
||||
m_optional_extensions.vk_ext_full_screen_exclusive ? "supported" : "NOT supported");
|
||||
Console.WriteLn("VK_KHR_driver_properties is %s",
|
||||
@@ -1266,7 +1300,8 @@ void GSDeviceVK::SubmitCommandBuffer(VKSwapChain* present_swap_chain)
|
||||
{
|
||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||
if (res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
ResizeWindow(0, 0, m_window_info.surface_scale);
|
||||
// Defer until next frame, otherwise resizing would invalidate swapchain before next present.
|
||||
m_resize_requested = true;
|
||||
else
|
||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
||||
|
||||
@@ -2183,6 +2218,8 @@ bool GSDeviceVK::UpdateWindow()
|
||||
|
||||
void GSDeviceVK::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
m_resize_requested = false;
|
||||
|
||||
if (!m_swap_chain || (m_swap_chain->GetWidth() == static_cast<u32>(new_window_width) &&
|
||||
m_swap_chain->GetHeight() == static_cast<u32>(new_window_height)))
|
||||
{
|
||||
@@ -2290,10 +2327,9 @@ GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
VkResult res = m_swap_chain->AcquireNextImage();
|
||||
VkResult res = m_resize_requested ? VK_ERROR_OUT_OF_DATE_KHR : m_swap_chain->AcquireNextImage();
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
m_swap_chain->ReleaseCurrentImage();
|
||||
|
||||
if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
@@ -2313,6 +2349,8 @@ GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
|
||||
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
}
|
||||
else
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
|
||||
// This can happen when multiple resize events happen in quick succession.
|
||||
// In this case, just wait until the next frame to try again.
|
||||
@@ -5545,7 +5583,6 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
|
||||
|
||||
void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
|
||||
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
|
||||
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
|
||||
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
|
||||
@@ -5678,24 +5715,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.require_one_barrier && !m_features.texture_barrier)
|
||||
{
|
||||
// requires a copy of the RT
|
||||
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, colclip_rt ? GSTexture::Format::ColorClip : GSTexture::Format::Color, true));
|
||||
if (draw_rt_clone)
|
||||
{
|
||||
EndRenderPass();
|
||||
|
||||
GL_PUSH("VK: Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
|
||||
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
PSSetShaderResource(2, draw_rt_clone, true);
|
||||
}
|
||||
else
|
||||
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
// Switch to colclip target for colclip hw rendering
|
||||
if (pipe.ps.colclip_hw)
|
||||
{
|
||||
@@ -5773,6 +5792,26 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
|
||||
}
|
||||
|
||||
if (draw_rt && (config.require_one_barrier || (config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
|
||||
{
|
||||
// Requires a copy of the RT.
|
||||
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true));
|
||||
if (draw_rt_clone)
|
||||
{
|
||||
GL_PUSH("VK: Copy RT to temp texture {%d,%d %dx%d}",
|
||||
config.drawarea.left, config.drawarea.top,
|
||||
config.drawarea.width(), config.drawarea.height());
|
||||
EndRenderPass();
|
||||
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
||||
if (config.require_one_barrier)
|
||||
PSSetShaderResource(2, draw_rt_clone, true);
|
||||
if (config.tex && config.tex == config.rt)
|
||||
PSSetShaderResource(0, draw_rt_clone, true);
|
||||
}
|
||||
else
|
||||
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
// We don't need the very first barrier if this is the first draw after switching to feedback loop,
|
||||
// because the layout change in itself enforces the execution dependency. colclip hw needs a barrier between
|
||||
// setup and the first draw to read it. TODO: Make colclip hw use subpasses instead.
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
bool vk_ext_rasterization_order_attachment_access : 1;
|
||||
bool vk_ext_full_screen_exclusive : 1;
|
||||
bool vk_ext_line_rasterization : 1;
|
||||
bool vk_ext_swapchain_maintenance1 : 1;
|
||||
bool vk_swapchain_maintenance1 : 1;
|
||||
bool vk_swapchain_maintenance1_is_khr : 1;
|
||||
bool vk_khr_driver_properties : 1;
|
||||
bool vk_khr_shader_non_semantic_info : 1;
|
||||
bool vk_ext_attachment_feedback_loop_layout : 1;
|
||||
@@ -367,6 +368,7 @@ public:
|
||||
|
||||
private:
|
||||
std::unique_ptr<VKSwapChain> m_swap_chain;
|
||||
bool m_resize_requested = false;
|
||||
|
||||
VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
@@ -241,4 +241,7 @@ VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false)
|
||||
// VK_EXT_swapchain_maintenance1
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkReleaseSwapchainImagesEXT, false)
|
||||
|
||||
// VK_KHR_swapchain_maintenance1
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkReleaseSwapchainImagesKHR, false)
|
||||
|
||||
#endif // VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
@@ -366,7 +366,17 @@ bool VKSwapChain::CreateSwapChain()
|
||||
|
||||
// Store the old/current swap chain when recreating for resize
|
||||
// Old swap chain is destroyed regardless of whether the create call succeeds
|
||||
VkSwapchainKHR old_swap_chain = m_swap_chain;
|
||||
VkSwapchainKHR old_swap_chain;
|
||||
// RDNA4 experences a 2s delay in the following 2-3 vkAcquireNextImageKHR calls if we pass the old swapchain to the new one.
|
||||
// Instead, pass null. This requires us to have freed the old image, which we already do with the swapchain maintenance extension.
|
||||
if (GSDeviceVK::GetInstance()->IsDeviceAMD() && GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_swapchain_maintenance1)
|
||||
{
|
||||
vkDestroySwapchainKHR(GSDeviceVK::GetInstance()->GetDevice(), m_swap_chain, nullptr);
|
||||
old_swap_chain = VK_NULL_HANDLE;
|
||||
}
|
||||
else
|
||||
old_swap_chain = m_swap_chain;
|
||||
|
||||
m_swap_chain = VK_NULL_HANDLE;
|
||||
|
||||
// Now we can actually create the swap chain
|
||||
@@ -549,17 +559,29 @@ void VKSwapChain::ReleaseCurrentImage()
|
||||
return;
|
||||
|
||||
if ((m_image_acquire_result.value() == VK_SUCCESS || m_image_acquire_result.value() == VK_SUBOPTIMAL_KHR) &&
|
||||
GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_ext_swapchain_maintenance1)
|
||||
GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_swapchain_maintenance1)
|
||||
{
|
||||
GSDeviceVK::GetInstance()->WaitForGPUIdle();
|
||||
|
||||
const VkReleaseSwapchainImagesInfoEXT info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
VkResult res = vkReleaseSwapchainImagesEXT(GSDeviceVK::GetInstance()->GetDevice(), &info);
|
||||
VkResult res;
|
||||
if (GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_swapchain_maintenance1_is_khr)
|
||||
{
|
||||
const VkReleaseSwapchainImagesInfoKHR info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_KHR,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
res = vkReleaseSwapchainImagesKHR(GSDeviceVK::GetInstance()->GetDevice(), &info);
|
||||
}
|
||||
else
|
||||
{
|
||||
const VkReleaseSwapchainImagesInfoEXT info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
res = vkReleaseSwapchainImagesEXT(GSDeviceVK::GetInstance()->GetDevice(), &info);
|
||||
}
|
||||
if (res != VK_SUCCESS)
|
||||
LOG_VULKAN_ERROR(res, "vkReleaseSwapchainImagesEXT() failed: ");
|
||||
LOG_VULKAN_ERROR(res, "vkReleaseSwapchainImages() failed: ");
|
||||
}
|
||||
|
||||
m_image_acquire_result.reset();
|
||||
|
||||
@@ -198,10 +198,7 @@ namespace FullscreenUI
|
||||
{
|
||||
private:
|
||||
std::string m_dialogId;
|
||||
std::atomic_bool m_completed{false};
|
||||
std::atomic_bool m_success{false};
|
||||
int m_reqMiB = 0;
|
||||
std::atomic_bool m_dialogClosed{false}; // Check if dialog was already closed
|
||||
|
||||
static std::vector<std::shared_ptr<HddCreateInProgress>> s_activeOperations;
|
||||
static std::mutex s_operationsMutex;
|
||||
@@ -213,20 +210,6 @@ namespace FullscreenUI
|
||||
{
|
||||
}
|
||||
|
||||
~HddCreateInProgress()
|
||||
{
|
||||
SafeCloseDialog();
|
||||
}
|
||||
|
||||
void SafeCloseDialog()
|
||||
{
|
||||
bool expected = false;
|
||||
if (m_dialogClosed.compare_exchange_strong(expected, true))
|
||||
{
|
||||
ImGuiFullscreen::CloseProgressDialog(m_dialogId.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static bool StartCreation(const std::string& filePath, int sizeInGB, bool use48BitLBA)
|
||||
{
|
||||
if (filePath.empty() || sizeInGB <= 0)
|
||||
@@ -244,12 +227,10 @@ namespace FullscreenUI
|
||||
{
|
||||
if (!FileSystem::DeleteFilePath(filePath.c_str()))
|
||||
{
|
||||
Host::RunOnCPUThread([filePath]() {
|
||||
ShowToast(
|
||||
fmt::format("{} HDD Creation Failed", ICON_FA_TRIANGLE_EXCLAMATION),
|
||||
fmt::format("Failed to delete existing HDD image file '{}'. Please check file permissions and try again.", Path::GetFileName(filePath)),
|
||||
5.0f);
|
||||
});
|
||||
ShowToast(
|
||||
fmt::format("{} HDD Creation Failed", ICON_FA_TRIANGLE_EXCLAMATION),
|
||||
fmt::format("Failed to delete existing HDD image file '{}'. Please check file permissions and try again.", Path::GetFileName(filePath)),
|
||||
5.0f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -269,14 +250,14 @@ namespace FullscreenUI
|
||||
instance->Start();
|
||||
|
||||
if (!instance->errored)
|
||||
Host::RunOnCPUThread([size_gb = static_cast<int>(instance->neededSize / static_cast<u64>(_1gb))]() {
|
||||
MTGS::RunOnGSThread([size_gb = static_cast<int>(instance->neededSize / static_cast<u64>(_1gb))]() {
|
||||
ShowToast(
|
||||
ICON_FA_CIRCLE_CHECK,
|
||||
fmt::format("HDD image ({} GB) created successfully.", size_gb),
|
||||
3.0f);
|
||||
});
|
||||
else
|
||||
Host::RunOnCPUThread([]() {
|
||||
MTGS::RunOnGSThread([]() {
|
||||
ShowToast(
|
||||
ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
"Failed to create HDD image.",
|
||||
@@ -301,10 +282,7 @@ namespace FullscreenUI
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(s_operationsMutex);
|
||||
for (auto& operation : s_activeOperations)
|
||||
{
|
||||
operation->SetCanceled();
|
||||
operation->SafeCloseDialog();
|
||||
}
|
||||
s_activeOperations.clear();
|
||||
}
|
||||
|
||||
@@ -323,17 +301,9 @@ namespace FullscreenUI
|
||||
ImGuiFullscreen::UpdateProgressDialog(m_dialogId.c_str(), message, 0, m_reqMiB, writtenMiB);
|
||||
}
|
||||
|
||||
virtual void SetError() override
|
||||
{
|
||||
SafeCloseDialog();
|
||||
HddCreate::SetError();
|
||||
}
|
||||
|
||||
virtual void Cleanup() override
|
||||
{
|
||||
SafeCloseDialog();
|
||||
m_success.store(!errored, std::memory_order_release);
|
||||
m_completed.store(true, std::memory_order_release);
|
||||
ImGuiFullscreen::CloseProgressDialog(m_dialogId.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -349,9 +319,7 @@ namespace FullscreenUI
|
||||
|
||||
if (sizeInGB < min_size || sizeInGB > max_size)
|
||||
{
|
||||
Host::RunOnCPUThread([min_size, max_size]() {
|
||||
ShowToast(std::string(), fmt::format("Invalid HDD size. Size must be between {} and {} GB.", min_size, max_size).c_str());
|
||||
});
|
||||
ShowToast(std::string(), fmt::format("Invalid HDD size. Size must be between {} and {} GB.", min_size, max_size).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -9360,6 +9328,8 @@ TRANSLATE_NOOP("FullscreenUI", "Reset System");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?");
|
||||
TRANSLATE_NOOP("FullscreenUI", "This game has no achievements.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "This game has no leaderboards.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Game List");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Launch a game from images scanned from your game directories.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Start Game");
|
||||
@@ -9405,7 +9375,7 @@ TRANSLATE_NOOP("FullscreenUI", "Selects the color style to be used for Big Pictu
|
||||
TRANSLATE_NOOP("FullscreenUI", "When Big Picture mode is started, the game list will be displayed instead of the main menu.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show a save state selector UI when switching slots instead of showing a notification bubble.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Background");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Select a custom background image to use in Big Picture Mode menus.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Select a custom background image to use in Big Picture Mode menus.\n\nSupported formats: PNG, JPG, JPEG, BMP.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Removes the custom background image.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Sets the transparency of the custom background image.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Select how to display the background image.");
|
||||
@@ -9415,6 +9385,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when a game is started.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you minimize the window or switch to another application, and unpauses when you switch back.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when a controller with bindings is disconnected.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you open the quick menu, and unpauses when you close it.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Display a modal dialog when a save state load/save operation fails.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Automatically saves the emulator state when powering down or exiting. You can then resume directly from where you left off next time.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix");
|
||||
@@ -9424,6 +9395,7 @@ TRANSLATE_NOOP("FullscreenUI", "Game Display");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Automatically switches to fullscreen mode when a game is started.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Switches between full screen and windowed when the window is double-clicked.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hides the mouse pointer/cursor when the emulator is in fullscreen mode.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "On-Screen Display");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitors are.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "%d%%");
|
||||
@@ -9849,6 +9821,9 @@ TRANSLATE_NOOP("FullscreenUI", "Last Played: {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Size: {:.2f} MB");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Are you sure you want to reset the play time for '{}' ({})?\n\nYour current play time is {}.\n\nThis action cannot be undone.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Backup Slot {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Slot {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State To Slot {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Left: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Right: ");
|
||||
@@ -9882,13 +9857,14 @@ TRANSLATE_NOOP("FullscreenUI", "AMOLED");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fit");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fill");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Stretch");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Tile");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Right");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center Right");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Center");
|
||||
@@ -10108,6 +10084,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pause On Start");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Focus Loss");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Controller Disconnection");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Menu");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Prompt On State Load/Save Failure");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Shutdown");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Create Save State Backups");
|
||||
@@ -10117,6 +10094,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enable Discord Presence");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Start Fullscreen");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Double-Click Toggles Fullscreen");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hide Cursor In Fullscreen");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Start Big Picture UI");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Scale");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Messages Position");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Performance Position");
|
||||
@@ -10297,7 +10275,6 @@ TRANSLATE_NOOP("FullscreenUI", "Delete Save");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Close Menu");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default Boot");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Delete State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Full Boot");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Reset Play Time");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Reset");
|
||||
|
||||
@@ -553,7 +553,7 @@ void Patch::ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pna
|
||||
(*num_unlabelled_patches)++;
|
||||
|
||||
// Try to extract the place value of the patch lines so we can
|
||||
// display them in the GUI if they all match. TODO: Don't duplicate
|
||||
// display it in the GUI if they all match. TODO: Don't duplicate
|
||||
// all this parsing logic twice.
|
||||
if (unknown_place)
|
||||
continue;
|
||||
@@ -571,6 +571,7 @@ void Patch::ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pna
|
||||
// place values.
|
||||
current_patch.place = std::nullopt;
|
||||
unknown_place = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
current_patch.place = place;
|
||||
|
||||
@@ -1252,10 +1252,10 @@ void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32>
|
||||
{
|
||||
if (backup)
|
||||
full_message = fmt::format(
|
||||
TRANSLATE_FS("SaveState", "Failed to load state from slot {}: {}"), *slot, message);
|
||||
TRANSLATE_FS("SaveState", "Failed to load state from backup slot {}: {}"), *slot, message);
|
||||
else
|
||||
full_message = fmt::format(
|
||||
TRANSLATE_FS("SaveState", "Failed to load state from backup slot {}: {}"), *slot, message);
|
||||
TRANSLATE_FS("SaveState", "Failed to load state from slot {}: {}"), *slot, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user