Merge remote-tracking branch 'upstream/main' - update to 0.4.0

This commit is contained in:
kalaposfos13 2024-10-31 21:17:42 +01:00
commit e3408a22cf
50 changed files with 1897 additions and 291 deletions

View File

@ -43,6 +43,7 @@ jobs:
outputs: outputs:
date: ${{ steps.vars.outputs.date }} date: ${{ steps.vars.outputs.date }}
shorthash: ${{ steps.vars.outputs.shorthash }} shorthash: ${{ steps.vars.outputs.shorthash }}
fullhash: ${{ steps.vars.outputs.fullhash }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Get date and git hash - name: Get date and git hash
@ -50,8 +51,10 @@ jobs:
run: | run: |
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_ENV
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT echo "shorthash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
windows-sdl: windows-sdl:
runs-on: windows-latest runs-on: windows-latest
@ -287,7 +290,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -343,7 +346,7 @@ jobs:
submodules: recursive submodules: recursive
- name: Install dependencies - name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration - name: Cache CMake Configuration
uses: actions/cache@v4 uses: actions/cache@v4
@ -420,9 +423,62 @@ jobs:
tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
draft: false draft: false
prerelease: true prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }})" body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
artifacts: ./artifacts/*.zip artifacts: ./artifacts/*.zip
- name: Publish to Release Repository
env:
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}
run: |
ARTIFACTS_DIR=./artifacts
REPO_WINDOWS="shadps4-emu/shadps4-binaries-Windows"
REPO_LINUX="shadps4-emu/shadps4-binaries-Linux"
REPO_MAC="shadps4-emu/shadps4-binaries-Mac"
for file in "$ARTIFACTS_DIR"/*.zip; do
filename=$(basename "$file")
REPO=""
# Determine repository based on file name
if [[ "$filename" == *"win64"* ]]; then
REPO=$REPO_WINDOWS
elif [[ "$filename" == *"linux"* ]] || [[ "$filename" == *"ubuntu64"* ]]; then
REPO=$REPO_LINUX
elif [[ "$filename" == *"macos"* ]]; then
REPO=$REPO_MAC
fi
# If REPO is empty, skip file
if [[ -z "$REPO" ]]; then
echo "No matching repository for $filename"
continue
fi
# Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
"tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false,
"prerelease": true,
"body": "Commit: [${{ needs.get-info.outputs.fullhash }}](https://github.com/shadps4-emu/shadPS4/commit/${{ needs.get-info.outputs.fullhash }})"
}' "https://api.github.com/repos/$REPO/releases" | jq -r '.id')
else
echo "Release already exists in $REPO with ID $release_id"
fi
# Artifact upload
echo "Uploading $filename to release $release_id in $REPO"
upload_url="https://uploads.github.com/repos/$REPO/releases/$release_id/assets?name=$filename"
curl -X POST -H "Authorization: token $GITHUB_TOKEN" -H "Content-Type: application/octet-stream" --data-binary @"$file" "$upload_url"
done
- name: Get current pre-release information - name: Get current pre-release information
env: env:
GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }} GITHUB_TOKEN: ${{ secrets.SHADPS4_TOKEN_REPO }}

8
.gitignore vendored
View File

@ -382,10 +382,10 @@ FodyWeavers.xsd
# VS Code files for those working on multiple tools # VS Code files for those working on multiple tools
.vscode/* .vscode/*
!.vscode/settings.json .vscode/settings.json
!.vscode/tasks.json .vscode/tasks.json
!.vscode/launch.json .vscode/launch.json
!.vscode/extensions.json .vscode/extensions.json
*.code-workspace *.code-workspace
/CMakeUserPresets.json /CMakeUserPresets.json
/compile_commands.json /compile_commands.json

View File

@ -290,8 +290,6 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/audio3d/audio3d_error.h src/core/libraries/audio3d/audio3d_error.h
src/core/libraries/audio3d/audio3d_impl.cpp src/core/libraries/audio3d/audio3d_impl.cpp
src/core/libraries/audio3d/audio3d_impl.h src/core/libraries/audio3d/audio3d_impl.h
src/core/libraries/ime/ime.cpp
src/core/libraries/ime/ime.h
src/core/libraries/game_live_streaming/gamelivestreaming.cpp src/core/libraries/game_live_streaming/gamelivestreaming.cpp
src/core/libraries/game_live_streaming/gamelivestreaming.h src/core/libraries/game_live_streaming/gamelivestreaming.h
src/core/libraries/remote_play/remoteplay.cpp src/core/libraries/remote_play/remoteplay.cpp
@ -311,13 +309,17 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
src/core/libraries/libc_internal/libc_internal.h src/core/libraries/libc_internal/libc_internal.h
) )
set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp set(IME_LIB src/core/libraries/ime/error_dialog.cpp
src/core/libraries/dialogs/error_dialog.h src/core/libraries/ime/error_dialog.h
src/core/libraries/dialogs/ime_dialog_ui.cpp src/core/libraries/ime/ime_common.h
src/core/libraries/dialogs/ime_dialog_ui.h src/core/libraries/ime/ime_dialog_ui.cpp
src/core/libraries/dialogs/ime_dialog.cpp src/core/libraries/ime/ime_dialog_ui.h
src/core/libraries/dialogs/ime_dialog.h src/core/libraries/ime/ime_dialog.cpp
src/core/libraries/dialogs/error_codes.h src/core/libraries/ime/ime_dialog.h
src/core/libraries/ime/ime_ui.cpp
src/core/libraries/ime/ime_ui.h
src/core/libraries/ime/ime.cpp
src/core/libraries/ime/ime.h
) )
set(PAD_LIB src/core/libraries/pad/pad.cpp set(PAD_LIB src/core/libraries/pad/pad.cpp
@ -346,6 +348,13 @@ set(FIBER_LIB src/core/libraries/fiber/fiber.cpp
src/core/libraries/fiber/fiber.h src/core/libraries/fiber/fiber.h
) )
set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
src/core/libraries/videodec/videodec2_impl.h
src/core/libraries/videodec/videodec2.cpp
src/core/libraries/videodec/videodec2.h
src/core/libraries/videodec/videodec2_avc.h
)
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
src/core/libraries/np_manager/np_manager.h src/core/libraries/np_manager/np_manager.h
src/core/libraries/np_score/np_score.cpp src/core/libraries/np_score/np_score.cpp
@ -499,8 +508,9 @@ set(CORE src/core/aerolib/stubs.cpp
${RANDOM_LIB} ${RANDOM_LIB}
${USBD_LIB} ${USBD_LIB}
${MISC_LIBS} ${MISC_LIBS}
${DIALOGS_LIB} ${IME_LIB}
${FIBER_LIB} ${FIBER_LIB}
${VDEC_LIB}
${DEV_TOOLS} ${DEV_TOOLS}
src/core/debug_state.cpp src/core/debug_state.cpp
src/core/debug_state.h src/core/debug_state.h

View File

@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Remoteplay) \ SUB(Lib, Remoteplay) \
SUB(Lib, SharePlay) \ SUB(Lib, SharePlay) \
SUB(Lib, Fiber) \ SUB(Lib, Fiber) \
SUB(Lib, Vdec2) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View File

@ -86,6 +86,7 @@ enum class Class : u8 {
Lib_Remoteplay, ///< The LibSceRemotePlay implementation Lib_Remoteplay, ///< The LibSceRemotePlay implementation
Lib_SharePlay, ///< The LibSceSharePlay implemenation Lib_SharePlay, ///< The LibSceSharePlay implemenation
Lib_Fiber, ///< The LibSceFiber implementation. Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Frontend, ///< Emulator UI Frontend, ///< Emulator UI
Render, ///< Video Core Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend Render_Vulkan, ///< Vulkan backend

View File

@ -86,30 +86,27 @@ static std::filesystem::path GetBundleParentDirectory() {
static auto UserPaths = [] { static auto UserPaths = [] {
#ifdef __APPLE__ #ifdef __APPLE__
// Start by assuming the base directory is the bundle's parent directory. // Set the current path to the directory containing the app bundle.
std::filesystem::path base_dir = GetBundleParentDirectory(); std::filesystem::current_path(GetBundleParentDirectory());
std::filesystem::path user_dir = base_dir / PORTABLE_DIR; #endif
// Check if the "user" directory exists in the current path:
// Try the portable user directory first.
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
if (!std::filesystem::exists(user_dir)) { if (!std::filesystem::exists(user_dir)) {
// If it doesn't exist, use the new hardcoded path: // If it doesn't exist, use the standard path for the platform instead.
// NOTE: On Windows we currently just create the portable directory instead.
#ifdef __APPLE__
user_dir = user_dir =
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4"; std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
}
#elif defined(__linux__) #elif defined(__linux__)
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
// Check if the "user" directory exists in the current path:
if (!std::filesystem::exists(user_dir)) {
// If it doesn't exist, use XDG_DATA_HOME if it is set, and provide a standard default
const char* xdg_data_home = getenv("XDG_DATA_HOME"); const char* xdg_data_home = getenv("XDG_DATA_HOME");
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) { if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4"; user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
} else { } else {
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
} }
}
#else
const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
#endif #endif
}
std::unordered_map<PathType, fs::path> paths; std::unordered_map<PathType, fs::path> paths;

View File

@ -8,7 +8,7 @@
namespace Common { namespace Common {
constexpr char VERSION[] = "0.3.1 WIP"; constexpr char VERSION[] = "0.4.1 WIP";
constexpr bool isRelease = false; constexpr bool isRelease = false;
} // namespace Common } // namespace Common

View File

@ -19,6 +19,7 @@ using namespace Core::Devtools;
using L = Core::Devtools::Layer; using L = Core::Devtools::Layer;
static bool show_simple_fps = false; static bool show_simple_fps = false;
static bool visibility_toggled = false;
static float fps_scale = 1.0f; static float fps_scale = 1.0f;
static bool show_advanced_debug = false; static bool show_advanced_debug = false;
@ -296,20 +297,24 @@ void L::Draw() {
const auto fn = DebugState.flip_frame_count.load(); const auto fn = DebugState.flip_frame_count.load();
frame_graph.AddFrame(fn, io.DeltaTime); frame_graph.AddFrame(fn, io.DeltaTime);
} }
if (IsKeyPressed(ImGuiKey_F10, false)) { if (IsKeyPressed(ImGuiKey_F10, false)) {
if (io.KeyCtrl) { if (io.KeyCtrl) {
show_advanced_debug = !show_advanced_debug; show_advanced_debug = !show_advanced_debug;
} else { } else {
show_simple_fps = !show_simple_fps; show_simple_fps = !show_simple_fps;
} }
visibility_toggled = true;
} }
if (show_simple_fps) { if (show_simple_fps) {
if (Begin("Video Info", nullptr, if (Begin("Video Info", nullptr,
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) { ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking)) {
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_FirstUseEver); // Set window position to top left if it was toggled on
if (visibility_toggled) {
SetWindowPos("Video Info", {999999.0f, 0.0f}, ImGuiCond_Always);
visibility_toggled = false;
}
if (BeginPopupContextWindow()) { if (BeginPopupContextWindow()) {
#define M(label, value) \ #define M(label, value) \
if (MenuItem(label, nullptr, fps_scale == value)) \ if (MenuItem(label, nullptr, fps_scale == value)) \

View File

@ -1,12 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001; // not initialized
constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002; // already initialized
constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003; // Parameter is invalid
constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004; // Unexpected fatal error
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005; // not in a callable state
constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006; // Process is busy
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007; // Invalid user ID

View File

@ -508,3 +508,86 @@ constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003;
constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004;
constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005;
constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006;
// ImeDialog library
constexpr int ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED = 0x80ED0001;
constexpr int ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED = 0x80ED0002;
constexpr int ORBIS_ERROR_DIALOG_ERROR_PARAM_INVALID = 0x80ED0003;
constexpr int ORBIS_ERROR_DIALOG_ERROR_UNEXPECTED_FATAL = 0x80ED0004;
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_STATE = 0x80ED0005;
constexpr int ORBIS_ERROR_DIALOG_ERROR_SERVICE_BUSY = 0x80ED0006;
constexpr int ORBIS_ERROR_DIALOG_ERROR_INVALID_USER_ID = 0x80ED0007;
// Ime library
constexpr int ORBIS_IME_ERROR_BUSY = 0x80BC0001;
constexpr int ORBIS_IME_ERROR_NOT_OPENED = 0x80BC0002;
constexpr int ORBIS_IME_ERROR_NO_MEMORY = 0x80BC0003;
constexpr int ORBIS_IME_ERROR_CONNECTION_FAILED = 0x80BC0004;
constexpr int ORBIS_IME_ERROR_TOO_MANY_REQUESTS = 0x80BC0005;
constexpr int ORBIS_IME_ERROR_INVALID_TEXT = 0x80BC0006;
constexpr int ORBIS_IME_ERROR_EVENT_OVERFLOW = 0x80BC0007;
constexpr int ORBIS_IME_ERROR_NOT_ACTIVE = 0x80BC0008;
constexpr int ORBIS_IME_ERROR_IME_SUSPENDING = 0x80BC0009;
constexpr int ORBIS_IME_ERROR_DEVICE_IN_USE = 0x80BC000A;
constexpr int ORBIS_IME_ERROR_INVALID_USER_ID = 0x80BC0010;
constexpr int ORBIS_IME_ERROR_INVALID_TYPE = 0x80BC0011;
constexpr int ORBIS_IME_ERROR_INVALID_SUPPORTED_LANGUAGES = 0x80BC0012;
constexpr int ORBIS_IME_ERROR_INVALID_ENTER_LABEL = 0x80BC0013;
constexpr int ORBIS_IME_ERROR_INVALID_INPUT_METHOD = 0x80BC0014;
constexpr int ORBIS_IME_ERROR_INVALID_OPTION = 0x80BC0015;
constexpr int ORBIS_IME_ERROR_INVALID_MAX_TEXT_LENGTH = 0x80BC0016;
constexpr int ORBIS_IME_ERROR_INVALID_INPUT_TEXT_BUFFER = 0x80BC0017;
constexpr int ORBIS_IME_ERROR_INVALID_POSX = 0x80BC0018;
constexpr int ORBIS_IME_ERROR_INVALID_POSY = 0x80BC0019;
constexpr int ORBIS_IME_ERROR_INVALID_HORIZONTAL_ALIGNMENT = 0x80BC001A;
constexpr int ORBIS_IME_ERROR_INVALID_VERTICAL_ALIGNMENT = 0x80BC001B;
constexpr int ORBIS_IME_ERROR_INVALID_EXTENDED = 0x80BC001C;
constexpr int ORBIS_IME_ERROR_INVALID_KEYBOARD_TYPE = 0x80BC001D;
constexpr int ORBIS_IME_ERROR_INVALID_WORK = 0x80BC0020;
constexpr int ORBIS_IME_ERROR_INVALID_ARG = 0x80BC0021;
constexpr int ORBIS_IME_ERROR_INVALID_HANDLER = 0x80BC0022;
constexpr int ORBIS_IME_ERROR_NO_RESOURCE_ID = 0x80BC0023;
constexpr int ORBIS_IME_ERROR_INVALID_MODE = 0x80BC0024;
constexpr int ORBIS_IME_ERROR_INVALID_PARAM = 0x80BC0030;
constexpr int ORBIS_IME_ERROR_INVALID_ADDRESS = 0x80BC0031;
constexpr int ORBIS_IME_ERROR_INVALID_RESERVED = 0x80BC0032;
constexpr int ORBIS_IME_ERROR_INVALID_TIMING = 0x80BC0033;
constexpr int ORBIS_IME_ERROR_INTERNAL = 0x80BC00FF;
// Videodec2 library
constexpr int ORBIS_VIDEODEC2_ERROR_API_FAIL = 0x811D0100;
constexpr int ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE = 0x811D0101;
constexpr int ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER = 0x811D0102;
constexpr int ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE = 0x811D0103;
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_SIZE = 0x811D0104;
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_POINTER = 0x811D0105;
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_SIZE = 0x811D0106;
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_POINTER = 0x811D0107;
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_BUFFER_ALIGNMENT = 0x811D0108;
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_ONION_MEMORY = 0x811D0109;
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_GARLIC_MEMORY = 0x811D010A;
constexpr int ORBIS_VIDEODEC2_ERROR_NOT_DIRECT_MEMORY = 0x811D010B;
constexpr int ORBIS_VIDEODEC2_ERROR_MEMORY_INFO = 0x811D010C;
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE = 0x811D010D;
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER = 0x811D010E;
constexpr int ORBIS_VIDEODEC2_ERROR_OUTPUT_INFO = 0x811D010F;
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE = 0x811D0110;
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STATE = 0x811D0111;
constexpr int ORBIS_VIDEODEC2_ERROR_PRESET_VALUE = 0x811D0112;
constexpr int ORBIS_VIDEODEC2_ERROR_CONFIG_INFO = 0x811D0200;
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_PIPE_ID = 0x811D0201;
constexpr int ORBIS_VIDEODEC2_ERROR_COMPUTE_QUEUE_ID = 0x811D0202;
constexpr int ORBIS_VIDEODEC2_ERROR_RESOURCE_TYPE = 0x811D0203;
constexpr int ORBIS_VIDEODEC2_ERROR_CODEC_TYPE = 0x811D0204;
constexpr int ORBIS_VIDEODEC2_ERROR_PROFILE_LEVEL = 0x811D0205;
constexpr int ORBIS_VIDEODEC2_ERROR_PIPELINE_DEPTH = 0x811D0206;
constexpr int ORBIS_VIDEODEC2_ERROR_AFFINITY_MASK = 0x811D0207;
constexpr int ORBIS_VIDEODEC2_ERROR_THREAD_PRIORITY = 0x811D0208;
constexpr int ORBIS_VIDEODEC2_ERROR_DPB_FRAME_COUNT = 0x811D0209;
constexpr int ORBIS_VIDEODEC2_ERROR_FRAME_WIDTH_HEIGHT = 0x811D020A;
constexpr int ORBIS_VIDEODEC2_ERROR_EXTRA_CONFIG_INFO = 0x811D020B;
constexpr int ORBIS_VIDEODEC2_ERROR_NEW_SEQUENCE = 0x811D0300;
constexpr int ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT = 0x811D0301;
constexpr int ORBIS_VIDEODEC2_ERROR_OVERSIZE_DECODE = 0x811D0302;
constexpr int ORBIS_VIDEODEC2_ERROR_INVALID_SEQUENCE = 0x811D0303;
constexpr int ORBIS_VIDEODEC2_ERROR_FATAL_STREAM = 0x811D0304;

View File

@ -15,7 +15,7 @@
namespace Libraries::Fiber { namespace Libraries::Fiber {
constexpr static u64 kFiberSignature = 0x054ad954; static constexpr u64 kFiberSignature = 0x054ad954;
thread_local SceFiber* gCurrentFiber = nullptr; thread_local SceFiber* gCurrentFiber = nullptr;
thread_local void* gFiberThread = nullptr; thread_local void* gFiberThread = nullptr;

View File

@ -359,12 +359,13 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
eq->AddEvent(kernel_event); eq->AddEvent(kernel_event);
Platform::IrqC::Instance()->Register( Platform::IrqC::Instance()->Register(
Platform::InterruptId::GfxEop, static_cast<Platform::InterruptId>(id),
[=](Platform::InterruptId irq) { [=](Platform::InterruptId irq) {
ASSERT_MSG(irq == Platform::InterruptId::GfxEop, ASSERT_MSG(irq == static_cast<Platform::InterruptId>(id),
"An unexpected IRQ occured"); // We need to convert IRQ# to event id and do "An unexpected IRQ occured"); // We need to convert IRQ# to event id and do
// proper filtering in trigger function // proper filtering in trigger function
eq->TriggerEvent(GnmEventIdents::GfxEop, SceKernelEvent::Filter::GraphicsCore, nullptr); eq->TriggerEvent(static_cast<GnmEventIdents>(id), SceKernelEvent::Filter::GraphicsCore,
nullptr);
}, },
eq); eq);
return ORBIS_OK; return ORBIS_OK;
@ -468,7 +469,6 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() {
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) { s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
LOG_TRACE(Lib_GnmDriver, "called"); LOG_TRACE(Lib_GnmDriver, "called");
ASSERT_MSG(id == GnmEventIdents::GfxEop);
if (!eq) { if (!eq) {
return ORBIS_KERNEL_ERROR_EBADF; return ORBIS_KERNEL_ERROR_EBADF;
@ -476,7 +476,7 @@ s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
eq->RemoveEvent(id); eq->RemoveEvent(id);
Platform::IrqC::Instance()->Unregister(Platform::InterruptId::GfxEop, eq); Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq);
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -1,14 +1,108 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <queue>
#include "ime.h" #include "ime.h"
#include "ime_ui.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/singleton.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
#include "core/linker.h"
namespace Libraries::Ime { namespace Libraries::Ime {
static std::queue<OrbisImeEvent> g_ime_events;
static ImeState g_ime_state{};
static ImeUi g_ime_ui;
class ImeHandler {
public:
ImeHandler(const OrbisImeKeyboardParam* param) {
Init(param, false);
}
ImeHandler(const OrbisImeParam* param) {
Init(param, true);
}
~ImeHandler() = default;
void Init(const void* param, bool ime_mode) {
if (ime_mode) {
m_param.ime = *(OrbisImeParam*)param;
} else {
m_param.key = *(OrbisImeKeyboardParam*)param;
}
m_ime_mode = ime_mode;
// Open an event to let the game know the IME has started
OrbisImeEvent openEvent{};
openEvent.id = (ime_mode ? OrbisImeEventId::OPEN : OrbisImeEventId::KEYBOARD_OPEN);
if (ime_mode) {
sceImeGetPanelSize(&m_param.ime, &openEvent.param.rect.width,
&openEvent.param.rect.height);
openEvent.param.rect.x = m_param.ime.posx;
openEvent.param.rect.y = m_param.ime.posy;
} else {
openEvent.param.resourceIdArray.userId = 1;
openEvent.param.resourceIdArray.resourceId[0] = 1;
}
Execute(nullptr, &openEvent, true);
if (ime_mode) {
g_ime_state = ImeState(&m_param.ime);
g_ime_ui = ImeUi(&g_ime_state, &m_param.ime);
}
}
s32 Update(OrbisImeEventHandler handler) {
std::unique_lock lock{g_ime_state.queue_mutex};
while (!g_ime_state.event_queue.empty()) {
OrbisImeEvent event = g_ime_state.event_queue.front();
g_ime_state.event_queue.pop();
Execute(handler, &event, false);
}
return ORBIS_OK;
}
void Execute(OrbisImeEventHandler handler, OrbisImeEvent* event, bool use_param_handler) {
const auto* linker = Common::Singleton<Core::Linker>::Instance();
if (m_ime_mode) {
OrbisImeParam param = m_param.ime;
if (use_param_handler) {
linker->ExecuteGuest(param.handler, param.arg, event);
} else {
linker->ExecuteGuest(handler, param.arg, event);
}
} else {
OrbisImeKeyboardParam param = m_param.key;
if (use_param_handler) {
linker->ExecuteGuest(param.handler, param.arg, event);
} else {
linker->ExecuteGuest(handler, param.arg, event);
}
}
}
bool IsIme() {
return m_ime_mode;
}
private:
union ImeParam {
OrbisImeKeyboardParam key;
OrbisImeParam ime;
} m_param{};
bool m_ime_mode = false;
};
static std::unique_ptr<ImeHandler> g_ime_handler;
int PS4_SYSV_ABI FinalizeImeModule() { int PS4_SYSV_ABI FinalizeImeModule() {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_ERROR(Lib_Ime, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -34,8 +128,19 @@ int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeClose() { s32 PS4_SYSV_ABI sceImeClose() {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (!g_ime_handler->IsIme()) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
g_ime_handler.release();
g_ime_ui = ImeUi();
g_ime_state = ImeState();
return ORBIS_OK; return ORBIS_OK;
} }
@ -104,13 +209,42 @@ int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeGetPanelSize() { s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "called");
if (!width || !height) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
switch (param->type) {
case OrbisImeType::DEFAULT:
case OrbisImeType::BASIC_LATIN:
case OrbisImeType::URL:
case OrbisImeType::MAIL:
// We set our custom sizes, commented sizes are the original ones
*width = 500; // 793
*height = 100; // 408
break;
case OrbisImeType::NUMBER:
*width = 370;
*height = 402;
break;
}
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeKeyboardClose() { s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "(STUBBED) called");
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
if (g_ime_handler->IsIme()) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
g_ime_handler.release();
return ORBIS_OK; return ORBIS_OK;
} }
@ -124,9 +258,19 @@ int PS4_SYSV_ABI sceImeKeyboardGetResourceId() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeKeyboardOpen() { s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_ERROR(Lib_Ime, "(STUBBED) called");
return ORBIS_OK;
if (!param) {
return ORBIS_IME_ERROR_INVALID_ADDRESS;
}
if (g_ime_handler) {
return ORBIS_IME_ERROR_BUSY;
}
// g_ime_handler = std::make_unique<ImeHandler>(param);
// return ORBIS_OK;
return ORBIS_IME_ERROR_CONNECTION_FAILED; // Fixup
} }
int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { int PS4_SYSV_ABI sceImeKeyboardOpenInternal() {
@ -144,8 +288,19 @@ int PS4_SYSV_ABI sceImeKeyboardUpdate() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeOpen() { s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "called");
if (!g_ime_handler) {
g_ime_handler = std::make_unique<ImeHandler>(param);
} else {
if (g_ime_handler->IsIme()) {
return ORBIS_IME_ERROR_BUSY;
}
g_ime_handler->Init((void*)param, true);
}
return ORBIS_OK; return ORBIS_OK;
} }
@ -154,9 +309,15 @@ int PS4_SYSV_ABI sceImeOpenInternal() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeParamInit() { void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_INFO(Lib_Ime, "called");
return ORBIS_OK;
if (!param) {
return;
}
memset(param, 0, sizeof(OrbisImeParam));
param->userId = -1;
} }
int PS4_SYSV_ABI sceImeSetCandidateIndex() { int PS4_SYSV_ABI sceImeSetCandidateIndex() {
@ -164,7 +325,7 @@ int PS4_SYSV_ABI sceImeSetCandidateIndex() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeSetCaret() { int PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_ERROR(Lib_Ime, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
} }
@ -179,9 +340,14 @@ int PS4_SYSV_ABI sceImeSetTextGeometry() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceImeUpdate() { s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler) {
LOG_ERROR(Lib_Ime, "(STUBBED) called"); LOG_TRACE(Lib_Ime, "called");
return ORBIS_OK;
if (!g_ime_handler) {
return ORBIS_IME_ERROR_NOT_OPENED;
}
return g_ime_handler->Update(handler);
} }
int PS4_SYSV_ABI sceImeVshClearPreedit() { int PS4_SYSV_ABI sceImeVshClearPreedit() {

View File

@ -5,12 +5,62 @@
#include "common/types.h" #include "common/types.h"
#include "ime_common.h"
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
} }
namespace Libraries::Ime { namespace Libraries::Ime {
constexpr u32 ORBIS_IME_MAX_TEXT_LENGTH = 2048;
enum class OrbisImeKeyboardOption : u32 {
DEFAULT = 0,
REPEAT = 1,
REPEAT_EACH_KEY = 2,
ADD_OSK = 4,
EFFECTIVE_WITH_TIME = 8,
DISABLE_RESUME = 16,
DISABLE_CAPSLOCK_WITHOUT_SHIFT = 32,
};
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeKeyboardOption)
struct OrbisImeKeyboardParam {
OrbisImeKeyboardOption option;
s8 reserved1[4];
void* arg;
OrbisImeEventHandler handler;
s8 reserved2[8];
};
struct OrbisImeParam {
s32 userId;
OrbisImeType type;
u64 supportedLanguages;
OrbisImeEnterLabel enterLabel;
OrbisImeInputMethod inputMethod;
OrbisImeTextFilter filter;
u32 option;
u32 maxTextLength;
char16_t* inputTextBuffer;
float posx;
float posy;
OrbisImeHorizontalAlignment horizontalAlignment;
OrbisImeVerticalAlignment verticalAlignment;
void* work;
void* arg;
OrbisImeEventHandler handler;
s8 reserved[8];
};
struct OrbisImeCaret {
f32 x;
f32 y;
u32 height;
u32 index;
};
int PS4_SYSV_ABI FinalizeImeModule(); int PS4_SYSV_ABI FinalizeImeModule();
int PS4_SYSV_ABI InitializeImeModule(); int PS4_SYSV_ABI InitializeImeModule();
int PS4_SYSV_ABI sceImeCheckFilterText(); int PS4_SYSV_ABI sceImeCheckFilterText();
@ -30,22 +80,22 @@ int PS4_SYSV_ABI sceImeDisableController();
int PS4_SYSV_ABI sceImeFilterText(); int PS4_SYSV_ABI sceImeFilterText();
int PS4_SYSV_ABI sceImeForTestFunction(); int PS4_SYSV_ABI sceImeForTestFunction();
int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeGetPanelPositionAndForm();
int PS4_SYSV_ABI sceImeGetPanelSize(); s32 PS4_SYSV_ABI sceImeGetPanelSize(const OrbisImeParam* param, u32* width, u32* height);
int PS4_SYSV_ABI sceImeKeyboardClose(); s32 PS4_SYSV_ABI sceImeKeyboardClose(s32 userId);
int PS4_SYSV_ABI sceImeKeyboardGetInfo(); int PS4_SYSV_ABI sceImeKeyboardGetInfo();
int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); int PS4_SYSV_ABI sceImeKeyboardGetResourceId();
int PS4_SYSV_ABI sceImeKeyboardOpen(); s32 PS4_SYSV_ABI sceImeKeyboardOpen(s32 userId, const OrbisImeKeyboardParam* param);
int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); int PS4_SYSV_ABI sceImeKeyboardOpenInternal();
int PS4_SYSV_ABI sceImeKeyboardSetMode(); int PS4_SYSV_ABI sceImeKeyboardSetMode();
int PS4_SYSV_ABI sceImeKeyboardUpdate(); int PS4_SYSV_ABI sceImeKeyboardUpdate();
int PS4_SYSV_ABI sceImeOpen(); s32 PS4_SYSV_ABI sceImeOpen(const OrbisImeParam* param, const void* extended);
int PS4_SYSV_ABI sceImeOpenInternal(); int PS4_SYSV_ABI sceImeOpenInternal();
int PS4_SYSV_ABI sceImeParamInit(); void PS4_SYSV_ABI sceImeParamInit(OrbisImeParam* param);
int PS4_SYSV_ABI sceImeSetCandidateIndex(); int PS4_SYSV_ABI sceImeSetCandidateIndex();
int PS4_SYSV_ABI sceImeSetCaret(); s32 PS4_SYSV_ABI sceImeSetCaret(const OrbisImeCaret* caret);
int PS4_SYSV_ABI sceImeSetText(); int PS4_SYSV_ABI sceImeSetText();
int PS4_SYSV_ABI sceImeSetTextGeometry(); int PS4_SYSV_ABI sceImeSetTextGeometry();
int PS4_SYSV_ABI sceImeUpdate(); s32 PS4_SYSV_ABI sceImeUpdate(OrbisImeEventHandler handler);
int PS4_SYSV_ABI sceImeVshClearPreedit(); int PS4_SYSV_ABI sceImeVshClearPreedit();
int PS4_SYSV_ABI sceImeVshClose(); int PS4_SYSV_ABI sceImeVshClose();
int PS4_SYSV_ABI sceImeVshConfirmPreedit(); int PS4_SYSV_ABI sceImeVshConfirmPreedit();

View File

@ -0,0 +1,184 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <sys/types.h>
#include "common/enum.h"
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
enum class OrbisImeType : u32 {
DEFAULT = 0,
BASIC_LATIN = 1,
URL = 2,
MAIL = 3,
NUMBER = 4,
};
enum class OrbisImeHorizontalAlignment : u32 {
LEFT = 0,
CENTER = 1,
RIGHT = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
TOP = 0,
CENTER = 1,
BOTTOM = 2,
};
enum class OrbisImeEnterLabel : u32 {
DEFAULT = 0,
SEND = 1,
SEARCH = 2,
GO = 3,
};
enum class OrbisImeInputMethod : u32 {
DEFAULT = 0,
};
enum class OrbisImeEventId : u32 {
OPEN = 0,
UPDATE_TEXT = 1,
UPDATE_CARET = 2,
PRESS_CLOSE = 4,
PRESS_ENTER = 5,
ABORT = 6,
CANDIDATE_LIST_START = 7,
CANDIDATE_LIST_END = 8,
CANDIDATE_WORD = 9,
CANDIDATE_INDEX = 10,
CANDIDATE_DONE = 11,
CANDIDATE_CANCEL = 12,
CHANGE_DEVICE = 14,
CHANGE_INPUT_METHOD_STATE = 18,
KEYBOARD_OPEN = 256,
KEYBOARD_KEYCODE_DOWN = 257,
KEYBOARD_KEYCODE_UP = 258,
KEYBOARD_KEYCODE_REPEAT = 259,
KEYBOARD_CONNECTION = 260,
KEYBOARD_DISCONNECTION = 261,
KEYBOARD_ABORT = 262,
};
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
enum class OrbisImeDeviceType : u32 {
NONE = 0,
CONTROLLER = 1,
EXT_KEYBOARD = 2,
REMOTE_OSK = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
u32 width;
u32 height;
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
u32 index;
s32 length;
};
struct OrbisImeEditText {
char16_t* str;
u32 caretIndex;
u32 areaNum;
OrbisImeTextAreaProperty textArea[4];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 userId;
u32 resourceId;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[6];
};
enum class OrbisImeCaretMovementDirection : u32 {
STILL = 0,
LEFT = 1,
RIGHT = 2,
UP = 3,
DOWN = 4,
HOME = 5,
END = 6,
PAGE_UP = 7,
PAGE_DOWN = 8,
TOP = 9,
BOTTOM = 10,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
OrbisImeCaretMovementDirection caretMove;
OrbisImeKeycode keycode;
OrbisImeKeyboardResourceIdArray resourceIdArray;
char16_t* candidateWord;
s32 candidateIndex;
OrbisImeDeviceType deviceType;
u32 inputMethodState;
s8 reserved[64];
};
struct OrbisImeEvent {
OrbisImeEventId id;
OrbisImeEventParam param;
};
typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
typedef PS4_SYSV_ABI void (*OrbisImeEventHandler)(void* arg, const OrbisImeEvent* e);

View File

@ -5,6 +5,7 @@
#include "common/enum.h" #include "common/enum.h"
#include "common/types.h" #include "common/types.h"
#include "ime_common.h"
namespace Core::Loader { namespace Core::Loader {
class SymbolsResolver; class SymbolsResolver;
@ -68,21 +69,6 @@ enum class OrbisImeDialogEndStatus : u32 {
ABORTED = 2, ABORTED = 2,
}; };
enum class OrbisImeType : u32 {
DEFAULT = 0,
BASIC_LATIN = 1,
URL = 2,
MAIL = 3,
NUMBER = 4,
};
enum class OrbisImeEnterLabel : u32 {
DEFAULT = 0,
SEND = 1,
SEARCH = 2,
GO = 3,
};
enum class OrbisImeDialogOption : u32 { enum class OrbisImeDialogOption : u32 {
DEFAULT = 0, DEFAULT = 0,
MULTILINE = 1, MULTILINE = 1,
@ -91,25 +77,8 @@ enum class OrbisImeDialogOption : u32 {
// TODO: Document missing options // TODO: Document missing options
LARGE_RESOLUTION = 1024, LARGE_RESOLUTION = 1024,
}; };
DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption)
enum class OrbisImeInputMethod : u32 {
DEFAULT = 0,
};
enum class OrbisImeHorizontalAlignment : u32 {
LEFT = 0,
CENTER = 1,
RIGHT = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
TOP = 0,
CENTER = 1,
BOTTOM = 2,
};
enum class OrbisImePanelPriority : u32 { enum class OrbisImePanelPriority : u32 {
DEFAULT = 0, DEFAULT = 0,
ALPHABET = 1, ALPHABET = 1,
@ -117,47 +86,6 @@ enum class OrbisImePanelPriority : u32 {
ACCENT = 3, ACCENT = 3,
}; };
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
struct OrbisImeColor { struct OrbisImeColor {
u8 r; u8 r;
u8 g; u8 g;
@ -180,9 +108,6 @@ struct OrbisImeKeycode {
u64 timestamp; u64 timestamp;
}; };
typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode,
u16* outKeycode, u32* outStatus, u16* outKeycode, u32* outStatus,
void* reserved); void* reserved);

View File

@ -9,8 +9,8 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/ime/ime_dialog.h"
#include "core/libraries/dialogs/ime_dialog_ui.h" #include "core/libraries/ime/ime_dialog_ui.h"
#include "core/linker.h" #include "core/linker.h"
#include "imgui/imgui_std.h" #include "imgui/imgui_std.h"
@ -22,8 +22,9 @@ namespace Libraries::ImeDialog {
ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param,
const OrbisImeParamExtended* extended) { const OrbisImeParamExtended* extended) {
if (!param) if (!param) {
return; return;
}
userId = param->userId; userId = param->userId;
is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE);
@ -344,7 +345,6 @@ void ImeDialogUi::DrawMultiLineInputText() {
int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData); ImeDialogUi* ui = static_cast<ImeDialogUi*>(data->UserData);
ASSERT(ui); ASSERT(ui);
// Should we filter punctuation? // Should we filter punctuation?

View File

@ -8,7 +8,7 @@
#include <imgui.h> #include <imgui.h>
#include "common/cstring.h" #include "common/cstring.h"
#include "common/types.h" #include "common/types.h"
#include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/ime/ime_dialog.h"
#include "imgui/imgui_layer.h" #include "imgui/imgui_layer.h"
namespace Libraries::ImeDialog { namespace Libraries::ImeDialog {

View File

@ -0,0 +1,252 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ime_ui.h"
#include "imgui/imgui_std.h"
namespace Libraries::Ime {
using namespace ImGui;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImeState::ImeState(const OrbisImeParam* param) {
if (!param) {
return;
}
work_buffer = param->work;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
ORBIS_IME_MAX_TEXT_LENGTH * 4)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
}
}
ImeState::ImeState(ImeState&& other) noexcept
: input_changed(other.input_changed), work_buffer(other.work_buffer),
text_buffer(other.text_buffer), current_text(std::move(other.current_text)),
event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
input_changed = other.input_changed;
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
event_queue = std::move(other.event_queue);
other.text_buffer = nullptr;
}
return *this;
}
void ImeState::SendEvent(OrbisImeEvent* event) {
std::unique_lock lock{queue_mutex};
event_queue.push(*event);
}
void ImeState::SendEnterEvent() {
OrbisImeEvent enterEvent{};
enterEvent.id = OrbisImeEventId::PRESS_ENTER;
SendEvent(&enterEvent);
}
void ImeState::SendCloseEvent() {
OrbisImeEvent closeEvent{};
closeEvent.id = OrbisImeEventId::PRESS_CLOSE;
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
SendEvent(&closeEvent);
}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
return true;
}
bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
return true;
}
ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) {
if (param) {
AddLayer(this);
}
}
ImeUi::~ImeUi() {
std::scoped_lock lock(draw_mutex);
Free();
}
ImeUi& ImeUi::operator=(ImeUi&& other) {
std::scoped_lock lock(draw_mutex, other.draw_mutex);
Free();
state = other.state;
ime_param = other.ime_param;
first_render = other.first_render;
other.state = nullptr;
other.ime_param = nullptr;
AddLayer(this);
return *this;
}
void ImeUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) {
return;
}
const auto& ctx = *GetCurrentContext();
const auto& io = ctx.IO;
// TODO: Figure out how to properly translate the positions -
// for example, if a game wants to center the IME panel,
// we have to translate the panel position in a way that it
// still becomes centered, as the game normally calculates
// the position assuming a it's running on a 1920x1080 screen,
// whereas we are running on a 1280x720 window size (by default).
//
// e.g. Panel position calculation from a game:
// param.posx = (1920 / 2) - (panelWidth / 2);
// param.posy = (1080 / 2) - (panelHeight / 2);
const auto size = GetIO().DisplaySize;
f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x);
f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y);
ImVec2 window_pos = {pos_x, pos_y};
ImVec2 window_size = {500.0f, 100.0f};
// SetNextWindowPos(window_pos);
SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {
SetNextWindowFocus();
}
if (Begin("IME##Ime", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground();
DrawInputText();
SetCursorPosY(GetCursorPosY() + 10.0f);
const char* button_text;
button_text = "Done##ImeDone";
float button_spacing = 10.0f;
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
SetCursorPosX(button_start_pos);
if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) {
state->SendEnterEvent();
}
SameLine(0.0f, button_spacing);
if (Button("Close##ImeClose", BUTTON_SIZE)) {
state->SendCloseEvent();
}
}
End();
first_render = false;
}
void ImeUi::DrawInputText() {
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
SetCursorPosX(20.0f);
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
state->input_changed = true;
}
}
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
} else if (data->CursorPos != lastCaretPos) {
OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::STILL;
if (data->CursorPos < lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::LEFT;
} else if (data->CursorPos > lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::RIGHT;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UPDATE_CARET;
event.param.caretMove = caretDirection;
lastCaretPos = data->CursorPos;
ui->state->SendEvent(&event);
}
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caretIndex = data->CursorPos;
eventParam.areaNum = 1;
eventParam.textArea[0].mode = 1; // Edit mode
eventParam.textArea[0].index = data->CursorPos;
eventParam.textArea[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UPDATE_TEXT;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
return 0;
}
void ImeUi::Free() {
RemoveLayer(this);
}
}; // namespace Libraries::Ime

View File

@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <imgui.h>
#include <queue>
#include "imgui/imgui_layer.h"
#include "common/cstring.h"
#include "common/types.h"
#include "ime.h"
namespace Libraries::Ime {
class ImeHandler;
class ImeUi;
class ImeState {
friend class ImeHandler;
friend class ImeUi;
bool input_changed = false;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
Common::CString<ORBIS_IME_MAX_TEXT_LENGTH * 4> current_text;
std::queue<OrbisImeEvent> event_queue;
std::mutex queue_mutex;
public:
ImeState(const OrbisImeParam* param = nullptr);
ImeState(ImeState&& other) noexcept;
ImeState& operator=(ImeState&& other) noexcept;
void SendEvent(OrbisImeEvent* event);
void SendEnterEvent();
void SendCloseEvent();
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len);
};
class ImeUi : public ImGui::Layer {
ImeState* state{};
const OrbisImeParam* ime_param{};
bool first_render = true;
std::mutex draw_mutex;
public:
explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr);
~ImeUi() override;
ImeUi(const ImeUi& other) = delete;
ImeUi& operator=(ImeUi&& other);
void Draw() override;
private:
void Free();
void DrawInputText();
static int InputTextCallback(ImGuiInputTextCallbackData* data);
};
}; // namespace Libraries::Ime

View File

@ -256,7 +256,7 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) { int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
int version = Common::ElfInfo::Instance().RawFirmwareVer(); int version = Common::ElfInfo::Instance().RawFirmwareVer();
LOG_INFO(Kernel, "returned system version = {:#x}", version); LOG_DEBUG(Kernel, "returned system version = {:#x}", version);
*ver = version; *ver = version;
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
} }

View File

@ -8,12 +8,12 @@
#include "core/libraries/audio/audioout.h" #include "core/libraries/audio/audioout.h"
#include "core/libraries/audio3d/audio3d.h" #include "core/libraries/audio3d/audio3d.h"
#include "core/libraries/avplayer/avplayer.h" #include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/dialogs/error_dialog.h"
#include "core/libraries/dialogs/ime_dialog.h"
#include "core/libraries/disc_map/disc_map.h" #include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h"
#include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/gnmdriver/gnmdriver.h"
#include "core/libraries/ime/error_dialog.h"
#include "core/libraries/ime/ime.h" #include "core/libraries/ime/ime.h"
#include "core/libraries/ime/ime_dialog.h"
#include "core/libraries/kernel/libkernel.h" #include "core/libraries/kernel/libkernel.h"
#include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libc_internal/libc_internal.h"
#include "core/libraries/libpng/pngdec.h" #include "core/libraries/libpng/pngdec.h"
@ -41,6 +41,7 @@
#include "core/libraries/system/systemservice.h" #include "core/libraries/system/systemservice.h"
#include "core/libraries/system/userservice.h" #include "core/libraries/system/userservice.h"
#include "core/libraries/usbd/usbd.h" #include "core/libraries/usbd/usbd.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h" #include "core/libraries/videoout/video_out.h"
namespace Libraries { namespace Libraries {
@ -80,6 +81,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym); Libraries::ErrorDialog::RegisterlibSceErrorDialog(sym);
Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::ImeDialog::RegisterlibSceImeDialog(sym);
Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym);
Libraries::Vdec2::RegisterlibSceVdec2(sym);
Libraries::Audio3d::RegisterlibSceAudio3d(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym);
Libraries::Ime::RegisterlibSceIme(sym); Libraries::Ime::RegisterlibSceIme(sym);
Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym);

View File

@ -0,0 +1,200 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2.h"
#include "videodec2_impl.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
computeMemInfo->cpuGpuMemory = nullptr;
computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
decoderMemInfo->cpuMemory = nullptr;
decoderMemInfo->gpuMemory = nullptr;
decoderMemInfo->cpuGpuMemory = nullptr;
decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
decoderMemInfo->cpuMemorySize = kMinimumMemorySize;
decoderMemInfo->gpuMemorySize = kMinimumMemorySize;
decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize;
decoderMemInfo->frameBufferAlignment = 0x100;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
delete decoder;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Decode(*inputData, *frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Flush(*frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
return decoder->Reset();
}
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfoOut, void* p2ndPictureInfoOut) {
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
UNREACHABLE();
}
return ORBIS_OK;
}
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryComputeMemoryInfo);
LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2AllocateComputeQueue);
LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2ReleaseComputeQueue);
LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryDecoderMemoryInfo);
LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2CreateDecoder);
LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2DeleteDecoder);
LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode);
LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush);
LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset);
LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2GetPictureInfo);
}
} // namespace Libraries::Vdec2

View File

@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "videodec2_avc.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Vdec2 {
class VdecDecoder;
using OrbisVideodec2Decoder = VdecDecoder*;
typedef void* OrbisVideodec2ComputeQueue;
struct OrbisVideodec2DecoderConfigInfo {
u64 thisSize;
u32 resourceType;
u32 codecType;
u32 profile;
u32 maxLevel;
s32 maxFrameWidth;
s32 maxFrameHeight;
s32 maxDpbFrameCount;
u32 decodePipelineDepth;
OrbisVideodec2ComputeQueue computeQueue;
u64 cpuAffinityMask;
s32 cpuThreadPriority;
bool optimizeProgressiveVideo;
bool checkMemoryType;
u8 reserved0;
u8 reserved1;
void* extraConfigInfo;
};
static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48);
struct OrbisVideodec2DecoderMemoryInfo {
u64 thisSize;
u64 cpuMemorySize;
void* cpuMemory;
u64 gpuMemorySize;
void* gpuMemory;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
u64 maxFrameBufferSize;
u32 frameBufferAlignment;
u32 reserved0;
};
static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48);
struct OrbisVideodec2InputData {
u64 thisSize;
void* auData;
u64 auSize;
u64 ptsData;
u64 dtsData;
u64 attachedData;
};
static_assert(sizeof(OrbisVideodec2InputData) == 0x30);
struct OrbisVideodec2OutputInfo {
u64 thisSize;
bool isValid;
bool isErrorFrame;
u8 pictureCount;
u32 codecType;
u32 frameWidth;
u32 framePitch;
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
};
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
void* frameBuffer;
u64 frameBufferSize;
bool isAccepted;
};
static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20);
struct OrbisVideodec2ComputeMemoryInfo {
u64 thisSize;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
};
static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18);
struct OrbisVideodec2ComputeConfigInfo {
u64 thisSize;
u16 computePipeId;
u16 computeQueueId;
bool checkMemoryType;
u8 reserved0;
u16 reserved1;
};
static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10);
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo);
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue);
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue);
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo);
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder);
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfo, void* p2ndPictureInfo);
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Vdec2

View File

@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Vdec2 {
struct OrbisVideodec2AvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
} // namespace Libraries::Vdec2

View File

@ -0,0 +1,228 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2_impl.h"
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
}
VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo) {
ASSERT(configInfo.codecType == 1); /* AVC */
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
ASSERT(codec);
mCodecContext = avcodec_alloc_context3(codec);
ASSERT(mCodecContext);
mCodecContext->width = configInfo.maxFrameWidth;
mCodecContext->height = configInfo.maxFrameHeight;
avcodec_open2(mCodecContext, codec, nullptr);
}
VdecDecoder::~VdecDecoder() {
avcodec_free_context(&mCodecContext);
sws_freeContext(mSwsContext);
gPictureInfos.clear();
}
s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
if (inputData.auSize == 0) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE;
}
AVPacket* packet = av_packet_alloc();
if (!packet) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate packet");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
packet->data = (u8*)inputData.auData;
packet->size = inputData.auSize;
packet->pts = inputData.ptsData;
packet->dts = inputData.dtsData;
int ret = avcodec_send_packet(mCodecContext, packet);
if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret);
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
}
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// FIXME: Should we add picture info here too?
}
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Reset() {
avcodec_flush_buffers(mCodecContext);
gPictureInfos.clear();
return ORBIS_OK;
}
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
AVFrame* nv12_frame = av_frame_alloc();
nv12_frame->pts = frame.pts;
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
nv12_frame->format = AV_PIX_FMT_NV12;
nv12_frame->width = frame.width;
nv12_frame->height = frame.height;
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
nv12_frame->crop_top = frame.crop_top;
nv12_frame->crop_bottom = frame.crop_bottom;
nv12_frame->crop_left = frame.crop_left;
nv12_frame->crop_right = frame.crop_right;
av_frame_get_buffer(nv12_frame, 0);
if (mSwsContext == nullptr) {
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
}
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
nv12_frame->data, nv12_frame->linesize);
if (res < 0) {
LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res));
return nullptr;
}
return nv12_frame;
}
} // namespace Libraries::Vdec2

View File

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec2.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
class VdecDecoder {
public:
VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo);
~VdecDecoder();
s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo);
s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo);
s32 Reset();
private:
AVFrame* ConvertNV12Frame(AVFrame& frame);
private:
AVCodecContext* mCodecContext = nullptr;
SwsContext* mSwsContext = nullptr;
};
} // namespace Libraries::Vdec2

View File

@ -106,13 +106,13 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
} }
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) { s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
LOG_INFO(Lib_VideoOut, "called"); LOG_TRACE(Lib_VideoOut, "called");
driver->GetPort(handle)->flip_rate = rate; driver->GetPort(handle)->flip_rate = rate;
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) { s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
LOG_INFO(Lib_VideoOut, "called"); LOG_TRACE(Lib_VideoOut, "called");
auto* port = driver->GetPort(handle); auto* port = driver->GetPort(handle);
std::unique_lock lock{port->port_mutex}; std::unique_lock lock{port->port_mutex};
s32 pending = port->flip_status.flipPendingNum; s32 pending = port->flip_status.flipPendingNum;

View File

@ -17,31 +17,35 @@
namespace Platform { namespace Platform {
enum class InterruptId : u32 { enum class InterruptId : u32 {
Compute0RelMem = 0u, Compute0RelMem = 0x00,
Compute1RelMem = 1u, Compute1RelMem = 0x01,
Compute2RelMem = 2u, Compute2RelMem = 0x02,
Compute3RelMem = 3u, Compute3RelMem = 0x03,
Compute4RelMem = 4u, Compute4RelMem = 0x04,
Compute5RelMem = 5u, Compute5RelMem = 0x05,
Compute6RelMem = 6u, Compute6RelMem = 0x06,
GfxEop = 7u, GfxEop = 0x40,
GfxFlip = 8u, GfxFlip = 0x08,
GpuIdle = 9u, GpuIdle = 0x09,
InterruptIdMax = 0x40, ///< Max possible value (GfxEop)
}; };
using IrqHandler = std::function<void(InterruptId)>; using IrqHandler = std::function<void(InterruptId)>;
struct IrqController { struct IrqController {
void RegisterOnce(InterruptId irq, IrqHandler handler) { void RegisterOnce(InterruptId irq, IrqHandler handler) {
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number"); ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
auto& ctx = irq_contexts[static_cast<u32>(irq)]; "Invalid IRQ number");
auto& ctx = irq_contexts.try_emplace(irq).first->second;
std::unique_lock lock{ctx.m_lock}; std::unique_lock lock{ctx.m_lock};
ctx.one_time_subscribers.emplace(handler); ctx.one_time_subscribers.emplace(handler);
} }
void Register(InterruptId irq, IrqHandler handler, void* uid) { void Register(InterruptId irq, IrqHandler handler, void* uid) {
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number"); ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
auto& ctx = irq_contexts[static_cast<u32>(irq)]; "Invalid IRQ number");
auto& ctx = irq_contexts.try_emplace(irq).first->second;
std::unique_lock lock{ctx.m_lock}; std::unique_lock lock{ctx.m_lock};
ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(), ASSERT_MSG(ctx.persistent_handlers.find(uid) == ctx.persistent_handlers.cend(),
@ -50,15 +54,17 @@ struct IrqController {
} }
void Unregister(InterruptId irq, void* uid) { void Unregister(InterruptId irq, void* uid) {
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Invalid IRQ number"); ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
auto& ctx = irq_contexts[static_cast<u32>(irq)]; "Invalid IRQ number");
auto& ctx = irq_contexts.try_emplace(irq).first->second;
std::unique_lock lock{ctx.m_lock}; std::unique_lock lock{ctx.m_lock};
ctx.persistent_handlers.erase(uid); ctx.persistent_handlers.erase(uid);
} }
void Signal(InterruptId irq) { void Signal(InterruptId irq) {
ASSERT_MSG(static_cast<u32>(irq) < irq_contexts.size(), "Unexpected IRQ signaled"); ASSERT_MSG(static_cast<u32>(irq) <= static_cast<u32>(InterruptId::InterruptIdMax),
auto& ctx = irq_contexts[static_cast<u32>(irq)]; "Unexpected IRQ signaled");
auto& ctx = irq_contexts.try_emplace(irq).first->second;
std::unique_lock lock{ctx.m_lock}; std::unique_lock lock{ctx.m_lock};
LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq)); LOG_TRACE(Core, "IRQ signaled: {}", magic_enum::enum_name(irq));
@ -81,7 +87,7 @@ private:
std::queue<IrqHandler> one_time_subscribers{}; std::queue<IrqHandler> one_time_subscribers{};
std::mutex m_lock{}; std::mutex m_lock{};
}; };
std::array<IrqContext, magic_enum::enum_count<InterruptId>()> irq_contexts{}; std::unordered_map<InterruptId, IrqContext> irq_contexts{};
}; };
using IrqC = Common::Singleton<IrqController>; using IrqC = Common::Singleton<IrqController>;

View File

@ -229,7 +229,7 @@ void Emulator::Run(const std::filesystem::path& file) {
linker->LoadModule(eboot_path); linker->LoadModule(eboot_path);
// check if we have system modules to load // check if we have system modules to load
LoadSystemModules(eboot_path); LoadSystemModules(eboot_path, game_info.game_serial);
// Load all prx from game's sce_module folder // Load all prx from game's sce_module folder
std::filesystem::path sce_module_folder = file.parent_path() / "sce_module"; std::filesystem::path sce_module_folder = file.parent_path() / "sce_module";
@ -273,8 +273,8 @@ void Emulator::Run(const std::filesystem::path& file) {
std::exit(0); std::exit(0);
} }
void Emulator::LoadSystemModules(const std::filesystem::path& file) { void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) {
constexpr std::array<SysModules, 13> ModulesToLoad{ constexpr std::array<SysModules, 12> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
{"libSceUlt.sprx", nullptr}, {"libSceUlt.sprx", nullptr},
@ -284,7 +284,6 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap}, {"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc}, {"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
{"libSceJpegEnc.sprx", nullptr}, {"libSceJpegEnc.sprx", nullptr},
{"libSceFont.sprx", nullptr},
{"libSceRazorCpu.sprx", nullptr}, {"libSceRazorCpu.sprx", nullptr},
{"libSceCesCs.sprx", nullptr}, {"libSceCesCs.sprx", nullptr},
{"libSceRudp.sprx", nullptr}}}; {"libSceRudp.sprx", nullptr}}};
@ -309,6 +308,14 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) {
LOG_INFO(Loader, "No HLE available for {} module", module_name); LOG_INFO(Loader, "No HLE available for {} module", module_name);
} }
} }
if (std::filesystem::exists(sys_module_path / game_serial)) {
for (const auto& entry :
std::filesystem::directory_iterator(sys_module_path / game_serial)) {
LOG_INFO(Loader, "Loading {} from game serial file {}", entry.path().string(),
game_serial);
linker->LoadModule(entry.path());
}
}
} }
#ifdef ENABLE_QT_GUI #ifdef ENABLE_QT_GUI

View File

@ -29,7 +29,7 @@ public:
void UpdatePlayTime(const std::string& serial); void UpdatePlayTime(const std::string& serial);
private: private:
void LoadSystemModules(const std::filesystem::path& file); void LoadSystemModules(const std::filesystem::path& file, std::string game_serial);
Core::MemoryManager* memory; Core::MemoryManager* memory;
Input::GameController* controller; Input::GameController* controller;

View File

@ -145,7 +145,9 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
return; return;
} }
QString currentRev = QString::fromStdString(Common::g_scm_rev).left(7); QString currentRev = (updateChannel == "Nightly")
? QString::fromStdString(Common::g_scm_rev).left(7)
: "v." + QString::fromStdString(Common::VERSION);
QString currentDate = Common::g_scm_date; QString currentDate = Common::g_scm_date;
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);

View File

@ -18,7 +18,7 @@
<message> <message>
<location filename="../about_dialog.ui" line="78"/> <location filename="../about_dialog.ui" line="78"/>
<source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source> <source>shadPS4 is an experimental open-source emulator for the PlayStation 4.</source>
<translation>shadPS4 è un emulatore sperimentale open source per PlayStation 4.</translation> <translation>shadPS4 è un emulatore sperimentale open-source per PlayStation 4.</translation>
</message> </message>
<message> <message>
<location filename="../about_dialog.ui" line="99"/> <location filename="../about_dialog.ui" line="99"/>
@ -62,7 +62,7 @@
<message> <message>
<location filename="../install_dir_select.cpp" line="37"/> <location filename="../install_dir_select.cpp" line="37"/>
<source>Select which directory you want to install to.</source> <source>Select which directory you want to install to.</source>
<translation>Select which directory you want to install to.</translation> <translation>Seleziona in quale cartella vuoi effettuare l'installazione.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -143,22 +143,22 @@
<message> <message>
<location filename="../gui_context_menus.h" line="72"/> <location filename="../gui_context_menus.h" line="72"/>
<source>Delete...</source> <source>Delete...</source>
<translation>Delete...</translation> <translation>Elimina...</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="73"/> <location filename="../gui_context_menus.h" line="73"/>
<source>Delete Game</source> <source>Delete Game</source>
<translation>Delete Game</translation> <translation>Elimina Gioco</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="74"/> <location filename="../gui_context_menus.h" line="74"/>
<source>Delete Update</source> <source>Delete Update</source>
<translation>Delete Update</translation> <translation>Elimina Aggiornamento</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="75"/> <location filename="../gui_context_menus.h" line="75"/>
<source>Delete DLC</source> <source>Delete DLC</source>
<translation>Delete DLC</translation> <translation>Elimina DLC</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="195"/> <location filename="../gui_context_menus.h" line="195"/>
@ -188,7 +188,7 @@
<message> <message>
<location filename="../gui_context_menus.h" line="299"/> <location filename="../gui_context_menus.h" line="299"/>
<source>Game</source> <source>Game</source>
<translation>Game</translation> <translation>Gioco</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="305"/> <location filename="../gui_context_menus.h" line="305"/>
@ -198,7 +198,7 @@
<message> <message>
<location filename="../gui_context_menus.h" line="312"/> <location filename="../gui_context_menus.h" line="312"/>
<source>This game has no update to delete!</source> <source>This game has no update to delete!</source>
<translation>This game has no update to delete!</translation> <translation>Questo gioco non ha alcun aggiornamento da eliminare!</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="316"/> <location filename="../gui_context_menus.h" line="316"/>
@ -208,7 +208,7 @@
<message> <message>
<location filename="../gui_context_menus.h" line="321"/> <location filename="../gui_context_menus.h" line="321"/>
<source>This game has no DLC to delete!</source> <source>This game has no DLC to delete!</source>
<translation>This game has no DLC to delete!</translation> <translation>Questo gioco non ha alcun DLC da eliminare!</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="325"/> <location filename="../gui_context_menus.h" line="325"/>
@ -218,12 +218,12 @@
<message> <message>
<location filename="../gui_context_menus.h" line="332"/> <location filename="../gui_context_menus.h" line="332"/>
<source>Delete %1</source> <source>Delete %1</source>
<translation>Delete %1</translation> <translation>Elimina %1</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="333"/> <location filename="../gui_context_menus.h" line="333"/>
<source>Are you sure you want to delete %1's %2 directory?</source> <source>Are you sure you want to delete %1's %2 directory?</source>
<translation>Are you sure you want to delete %1's %2 directory?</translation> <translation>Sei sicuro di eliminale la cartella %2 di %1?</translation>
</message> </message>
</context> </context>
<context> <context>
@ -336,7 +336,7 @@
<message> <message>
<location filename="../main_window_ui.h" line="343"/> <location filename="../main_window_ui.h" line="343"/>
<source>Download Cheats/Patches</source> <source>Download Cheats/Patches</source>
<translation>Scarica Trucchi / Patch</translation> <translation>Scarica Trucchi/Patch</translation>
</message> </message>
<message> <message>
<location filename="../main_window_ui.h" line="345"/> <location filename="../main_window_ui.h" line="345"/>
@ -480,7 +480,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="140"/> <location filename="../settings_dialog.ui" line="140"/>
<source>Enable Separate Update Folder</source> <source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation> <translation>Abilità Cartella Aggiornamenti Separata</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="129"/> <location filename="../settings_dialog.ui" line="129"/>
@ -490,7 +490,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="136"/> <location filename="../settings_dialog.ui" line="136"/>
<source>Is PS4 Pro</source> <source>Is PS4 Pro</source>
<translation>Modalità Ps4Pro</translation> <translation>Modalità Ps4 Pro</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="154"/> <location filename="../settings_dialog.ui" line="154"/>
@ -530,12 +530,12 @@
<message> <message>
<location filename="../settings_dialog.ui" line="635"/> <location filename="../settings_dialog.ui" line="635"/>
<source>Hide Cursor</source> <source>Hide Cursor</source>
<translation>Nascondi cursore</translation> <translation>Nascondi Cursore</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="668"/> <location filename="../settings_dialog.ui" line="668"/>
<source>Hide Cursor Idle Timeout</source> <source>Hide Cursor Idle Timeout</source>
<translation>Timeout inattivo per nascondere il cursore</translation> <translation>Timeout inattività per nascondere il cursore</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="767"/> <location filename="../settings_dialog.ui" line="767"/>
@ -555,7 +555,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="282"/> <location filename="../settings_dialog.ui" line="282"/>
<source>Graphics Device</source> <source>Graphics Device</source>
<translation>Adattatore grafico</translation> <translation>Scheda Grafica</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="326"/> <location filename="../settings_dialog.ui" line="326"/>
@ -575,7 +575,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="453"/> <location filename="../settings_dialog.ui" line="453"/>
<source>Advanced</source> <source>Advanced</source>
<translation>Avanzato</translation> <translation>Avanzate</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="462"/> <location filename="../settings_dialog.ui" line="462"/>
@ -614,8 +614,8 @@
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="537"/> <location filename="../settings_dialog.ui" line="537"/>
<source>Enable Debug Dumping</source> <source>Enable</source>
<translation>Abilita Dump Debug</translation> <translation>Abilita Debug Dumping</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="560"/> <location filename="../settings_dialog.ui" line="560"/>
@ -630,7 +630,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="574"/> <location filename="../settings_dialog.ui" line="574"/>
<source>Enable RenderDoc Debugging</source> <source>Enable RenderDoc Debugging</source>
<translation>Abilita Debugging RenderDoc</translation> <translation>Abilita RenderDoc Debugging</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="274"/> <location filename="../settings_dialog.ui" line="274"/>
@ -693,7 +693,7 @@
<message> <message>
<location filename="../main_window.cpp" line="363"/> <location filename="../main_window.cpp" line="363"/>
<source>Download Complete</source> <source>Download Complete</source>
<translation>Scaricamento completato</translation> <translation>Download completato</translation>
</message> </message>
<message> <message>
<location filename="../main_window.cpp" line="364"/> <location filename="../main_window.cpp" line="364"/>
@ -748,7 +748,7 @@
<message> <message>
<location filename="../main_window.cpp" line="646"/> <location filename="../main_window.cpp" line="646"/>
<source>PKG and Game versions match: </source> <source>PKG and Game versions match: </source>
<translation>Le versioni di PKG e del gioco corrispondono: </translation> <translation>Le versioni di PKG e del Gioco corrispondono: </translation>
</message> </message>
<message> <message>
<location filename="../main_window.cpp" line="647"/> <location filename="../main_window.cpp" line="647"/>
@ -1144,7 +1144,7 @@
<message> <message>
<location filename="../settings_dialog.cpp" line="293"/> <location filename="../settings_dialog.cpp" line="293"/>
<source>separateUpdatesCheckBox</source> <source>separateUpdatesCheckBox</source>
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation> <translation>Abilita Cartella Aggiornamenti Separata:\nAbilita l'installazione degli aggiornamenti in una cartella separata per una più facile gestione.</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.cpp" line="295"/> <location filename="../settings_dialog.cpp" line="295"/>
@ -1420,7 +1420,7 @@
<message> <message>
<location filename="../check_update.cpp" line="198"/> <location filename="../check_update.cpp" line="198"/>
<source>Check for Updates at Startup</source> <source>Check for Updates at Startup</source>
<translation>Verifica aggiornamenti allavvio</translation> <translation>Controlla aggiornamenti allavvio</translation>
</message> </message>
<message> <message>
<location filename="../check_update.cpp" line="199"/> <location filename="../check_update.cpp" line="199"/>

View File

@ -143,27 +143,27 @@
<message> <message>
<location filename="../gui_context_menus.h" line="72"/> <location filename="../gui_context_menus.h" line="72"/>
<source>Delete...</source> <source>Delete...</source>
<translation>Delete...</translation> <translation>Fshi...</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="73"/> <location filename="../gui_context_menus.h" line="73"/>
<source>Delete Game</source> <source>Delete Game</source>
<translation>Delete Game</translation> <translation>Fshi lojën</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="74"/> <location filename="../gui_context_menus.h" line="74"/>
<source>Delete Update</source> <source>Delete Update</source>
<translation>Delete Update</translation> <translation>Fshi përditësimin</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="75"/> <location filename="../gui_context_menus.h" line="75"/>
<source>Delete DLC</source> <source>Delete DLC</source>
<translation>Delete DLC</translation> <translation>Fshi DLC-</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="195"/> <location filename="../gui_context_menus.h" line="195"/>
<source>Shortcut creation</source> <source>Shortcut creation</source>
<translation>Krijim i shkurtores</translation> <translation>Krijimi i shkurtores</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="196"/> <location filename="../gui_context_menus.h" line="196"/>
@ -188,27 +188,27 @@
<message> <message>
<location filename="../gui_context_menus.h" line="299"/> <location filename="../gui_context_menus.h" line="299"/>
<source>Game</source> <source>Game</source>
<translation>Game</translation> <translation>Loja</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="305"/> <location filename="../gui_context_menus.h" line="305"/>
<source>requiresEnableSeparateUpdateFolder_MSG</source> <source>requiresEnableSeparateUpdateFolder_MSG</source>
<translation>This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it.</translation> <translation>Kjo veçori kërkon cilësimin 'Aktivizo dosjen e ndarë të përditësimit' për punuar. qoftë se do ta përdorësh këtë veçori, lutem aktivizoje.</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="312"/> <location filename="../gui_context_menus.h" line="312"/>
<source>This game has no update to delete!</source> <source>This game has no update to delete!</source>
<translation>This game has no update to delete!</translation> <translation>Kjo lojë nuk ka përditësim për fshirë!</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="316"/> <location filename="../gui_context_menus.h" line="316"/>
<source>Update</source> <source>Update</source>
<translation>Update</translation> <translation>Përditësim</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="321"/> <location filename="../gui_context_menus.h" line="321"/>
<source>This game has no DLC to delete!</source> <source>This game has no DLC to delete!</source>
<translation>This game has no DLC to delete!</translation> <translation>Kjo lojë nuk ka DLC për fshirë!</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="325"/> <location filename="../gui_context_menus.h" line="325"/>
@ -218,12 +218,12 @@
<message> <message>
<location filename="../gui_context_menus.h" line="332"/> <location filename="../gui_context_menus.h" line="332"/>
<source>Delete %1</source> <source>Delete %1</source>
<translation>Delete %1</translation> <translation>Fshi %1</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="333"/> <location filename="../gui_context_menus.h" line="333"/>
<source>Are you sure you want to delete %1's %2 directory?</source> <source>Are you sure you want to delete %1's %2 directory?</source>
<translation>Are you sure you want to delete %1's %2 directory?</translation> <translation>Je i sigurt do fsish dosjen %2 %1?</translation>
</message> </message>
</context> </context>
<context> <context>
@ -256,7 +256,7 @@
<message> <message>
<location filename="../main_window_ui.h" line="315"/> <location filename="../main_window_ui.h" line="315"/>
<source>Configure...</source> <source>Configure...</source>
<translation>Formëso...</translation> <translation>Konfiguro...</translation>
</message> </message>
<message> <message>
<location filename="../main_window_ui.h" line="318"/> <location filename="../main_window_ui.h" line="318"/>
@ -480,7 +480,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="140"/> <location filename="../settings_dialog.ui" line="140"/>
<source>Enable Separate Update Folder</source> <source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation> <translation>Aktivizo dosjen e ndarë përditësimit</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="129"/> <location filename="../settings_dialog.ui" line="129"/>
@ -1026,7 +1026,7 @@
<message> <message>
<location filename="../cheats_patches.cpp" line="763"/> <location filename="../cheats_patches.cpp" line="763"/>
<source>DownloadComplete_MSG</source> <source>DownloadComplete_MSG</source>
<translation>Arnat u shkarkuan me sukses! gjitha arnat e ofruara për gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse patch-i nuk shfaqet, mund mos ekzistojë për numrin e seri dhe versionin specifik lojës.</translation> <translation>Arnat u shkarkuan me sukses! gjitha arnat e ofruara për gjitha lojërat janë shkarkuar, nuk ka nevojë t'i shkarkosh ato individualisht për secilën lojë siç ndodh me Mashtrimet. Nëse arna nuk shfaqet, mund mos ekzistojë për numrin e serikut dhe versionin specifik lojës.</translation>
</message> </message>
<message> <message>
<location filename="../cheats_patches.cpp" line="773"/> <location filename="../cheats_patches.cpp" line="773"/>
@ -1046,12 +1046,12 @@
<message> <message>
<location filename="../cheats_patches.cpp" line="851"/> <location filename="../cheats_patches.cpp" line="851"/>
<source>The downloaded patch only works on version: %1</source> <source>The downloaded patch only works on version: %1</source>
<translation>Patch-i i shkarkuar funksionon vetëm versionin: %1</translation> <translation>Arna e shkarkuar funksionon vetëm versionin: %1</translation>
</message> </message>
<message> <message>
<location filename="../cheats_patches.cpp" line="856"/> <location filename="../cheats_patches.cpp" line="856"/>
<source>You may need to update your game.</source> <source>You may need to update your game.</source>
<translation>Ju mund duhet përditësoni lojën tuaj.</translation> <translation>Mund duhet përditësosh lojën tënde.</translation>
</message> </message>
<message> <message>
<location filename="../cheats_patches.cpp" line="860"/> <location filename="../cheats_patches.cpp" line="860"/>
@ -1096,7 +1096,7 @@
<message> <message>
<location filename="../cheats_patches.cpp" line="1163"/> <location filename="../cheats_patches.cpp" line="1163"/>
<source>Can't apply cheats before the game is started</source> <source>Can't apply cheats before the game is started</source>
<translation>Nuk mund zbatohen mashtrime para se fillojë loja.</translation> <translation>Nuk mund zbatohen mashtrime para fillimit lojës.</translation>
</message> </message>
</context> </context>
<context> <context>
@ -1144,7 +1144,7 @@
<message> <message>
<location filename="../settings_dialog.cpp" line="293"/> <location filename="../settings_dialog.cpp" line="293"/>
<source>separateUpdatesCheckBox</source> <source>separateUpdatesCheckBox</source>
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation> <translation>Aktivizo dosjen e ndarë përditësimit:\nAktivizon instalimin e përditësimeve lojërave dosje veçanta për menaxhim lehtë.</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.cpp" line="295"/> <location filename="../settings_dialog.cpp" line="295"/>

View File

@ -62,7 +62,7 @@
<message> <message>
<location filename="../install_dir_select.cpp" line="37"/> <location filename="../install_dir_select.cpp" line="37"/>
<source>Select which directory you want to install to.</source> <source>Select which directory you want to install to.</source>
<translation>Select which directory you want to install to.</translation> <translation></translation>
</message> </message>
</context> </context>
<context> <context>
@ -143,22 +143,22 @@
<message> <message>
<location filename="../gui_context_menus.h" line="72"/> <location filename="../gui_context_menus.h" line="72"/>
<source>Delete...</source> <source>Delete...</source>
<translation>Delete...</translation> <translation>...</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="73"/> <location filename="../gui_context_menus.h" line="73"/>
<source>Delete Game</source> <source>Delete Game</source>
<translation>Delete Game</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="74"/> <location filename="../gui_context_menus.h" line="74"/>
<source>Delete Update</source> <source>Delete Update</source>
<translation>Delete Update</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="75"/> <location filename="../gui_context_menus.h" line="75"/>
<source>Delete DLC</source> <source>Delete DLC</source>
<translation>Delete DLC</translation> <translation>DLC</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="195"/> <location filename="../gui_context_menus.h" line="195"/>
@ -188,27 +188,27 @@
<message> <message>
<location filename="../gui_context_menus.h" line="299"/> <location filename="../gui_context_menus.h" line="299"/>
<source>Game</source> <source>Game</source>
<translation>Game</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="305"/> <location filename="../gui_context_menus.h" line="305"/>
<source>requiresEnableSeparateUpdateFolder_MSG</source> <source>requiresEnableSeparateUpdateFolder_MSG</source>
<translation>This feature requires the 'Enable Separate Update Folder' config option to work. If you want to use this feature, please enable it.</translation> <translation>使</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="312"/> <location filename="../gui_context_menus.h" line="312"/>
<source>This game has no update to delete!</source> <source>This game has no update to delete!</source>
<translation>This game has no update to delete!</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="316"/> <location filename="../gui_context_menus.h" line="316"/>
<source>Update</source> <source>Update</source>
<translation>Update</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="321"/> <location filename="../gui_context_menus.h" line="321"/>
<source>This game has no DLC to delete!</source> <source>This game has no DLC to delete!</source>
<translation>This game has no DLC to delete!</translation> <translation>DLC可以删除</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="325"/> <location filename="../gui_context_menus.h" line="325"/>
@ -218,12 +218,12 @@
<message> <message>
<location filename="../gui_context_menus.h" line="332"/> <location filename="../gui_context_menus.h" line="332"/>
<source>Delete %1</source> <source>Delete %1</source>
<translation>Delete %1</translation> <translation> %1</translation>
</message> </message>
<message> <message>
<location filename="../gui_context_menus.h" line="333"/> <location filename="../gui_context_menus.h" line="333"/>
<source>Are you sure you want to delete %1's %2 directory?</source> <source>Are you sure you want to delete %1's %2 directory?</source>
<translation>Are you sure you want to delete %1's %2 directory?</translation> <translation> %1 %2 </translation>
</message> </message>
</context> </context>
<context> <context>
@ -480,7 +480,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="140"/> <location filename="../settings_dialog.ui" line="140"/>
<source>Enable Separate Update Folder</source> <source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="129"/> <location filename="../settings_dialog.ui" line="129"/>
@ -560,7 +560,7 @@
<message> <message>
<location filename="../settings_dialog.ui" line="326"/> <location filename="../settings_dialog.ui" line="326"/>
<source>Width</source> <source>Width</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.ui" line="357"/> <location filename="../settings_dialog.ui" line="357"/>
@ -1144,7 +1144,7 @@
<message> <message>
<location filename="../settings_dialog.cpp" line="293"/> <location filename="../settings_dialog.cpp" line="293"/>
<source>separateUpdatesCheckBox</source> <source>separateUpdatesCheckBox</source>
<translation>Enable Separate Update Folder:\nEnables installing game updates into a separate folder for easy management.</translation> <translation>\n启用安装游戏更新到一个单独的目录中以更便于管理</translation>
</message> </message>
<message> <message>
<location filename="../settings_dialog.cpp" line="295"/> <location filename="../settings_dialog.cpp" line="295"/>

View File

@ -261,6 +261,13 @@ struct Image {
return last_level + 1; return last_level + 1;
} }
u32 NumSamples() const {
if (GetType() == ImageType::Color2DMsaa || GetType() == ImageType::Color2DMsaaArray) {
return 1u << last_level;
}
return 1;
}
ImageType GetType() const noexcept { ImageType GetType() const noexcept {
return static_cast<ImageType>(type); return static_cast<ImageType>(type);
} }

View File

@ -447,6 +447,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
vk::Format::eR8Unorm), vk::Format::eR8Unorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Snorm,
vk::Format::eR8Snorm), vk::Format::eR8Snorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uscaled,
vk::Format::eR8Uscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sscaled,
vk::Format::eR8Sscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Uint,
vk::Format::eR8Uint), vk::Format::eR8Uint),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8, AmdGpu::NumberFormat::Sint,
@ -458,6 +462,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
vk::Format::eR16Unorm), vk::Format::eR16Unorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Snorm,
vk::Format::eR16Snorm), vk::Format::eR16Snorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uscaled,
vk::Format::eR16Uscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sscaled,
vk::Format::eR16Sscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Uint,
vk::Format::eR16Uint), vk::Format::eR16Uint),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format16, AmdGpu::NumberFormat::Sint,
@ -469,6 +477,10 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
vk::Format::eR8G8Unorm), vk::Format::eR8G8Unorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Snorm,
vk::Format::eR8G8Snorm), vk::Format::eR8G8Snorm),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uscaled,
vk::Format::eR8G8Uscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sscaled,
vk::Format::eR8G8Sscaled),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Uint,
vk::Format::eR8G8Uint), vk::Format::eR8G8Uint),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8, AmdGpu::NumberFormat::Sint,
@ -509,6 +521,14 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
vk::Format::eA2B10G10R10UnormPack32), vk::Format::eA2B10G10R10UnormPack32),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Snorm,
vk::Format::eA2B10G10R10SnormPack32), vk::Format::eA2B10G10R10SnormPack32),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uscaled,
vk::Format::eA2B10G10R10UscaledPack32),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sscaled,
vk::Format::eA2B10G10R10SscaledPack32),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Uint,
vk::Format::eA2B10G10R10UintPack32),
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format2_10_10_10, AmdGpu::NumberFormat::Sint,
vk::Format::eA2B10G10R10SintPack32),
// 8_8_8_8 // 8_8_8_8
CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Unorm, CreateSurfaceFormatInfo(AmdGpu::DataFormat::Format8_8_8_8, AmdGpu::NumberFormat::Unorm,
vk::Format::eR8G8B8A8Unorm), vk::Format::eR8G8B8A8Unorm),

View File

@ -127,18 +127,33 @@ bool ComputePipeline::BindResources(VideoCore::BufferCache& buffer_cache,
// we can skip the whole dispatch and update the tracked state instead. Also, it is not // we can skip the whole dispatch and update the tracked state instead. Also, it is not
// intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we // intended to be consumed and in such rare cases (e.g. HTile introspection, CRAA) we
// will need its full emulation anyways. For cases of metadata read a warning will be logged. // will need its full emulation anyways. For cases of metadata read a warning will be logged.
for (const auto& desc : info->texture_buffers) { const auto IsMetaUpdate = [&](const auto& desc) {
const VAddr address = desc.GetSharp(*info).base_address; const VAddr address = desc.GetSharp(*info).base_address;
if (desc.is_written) { if (desc.is_written) {
if (texture_cache.TouchMeta(address, true)) { if (texture_cache.TouchMeta(address, true)) {
LOG_TRACE(Render_Vulkan, "Metadata update skipped"); LOG_TRACE(Render_Vulkan, "Metadata update skipped");
return false; return true;
} }
} else { } else {
if (texture_cache.IsMeta(address)) { if (texture_cache.IsMeta(address)) {
LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)"); LOG_WARNING(Render_Vulkan, "Unexpected metadata read by a CS shader (buffer)");
} }
} }
return false;
};
for (const auto& desc : info->buffers) {
if (desc.is_gds_buffer) {
continue;
}
if (IsMetaUpdate(desc)) {
return false;
}
}
for (const auto& desc : info->texture_buffers) {
if (IsMetaUpdate(desc)) {
return false;
}
} }
BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes, BindBuffers(buffer_cache, texture_cache, *info, binding, push_data, set_writes,

View File

@ -160,22 +160,23 @@ Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index,
// Check and log format support details. // Check and log format support details.
for (const auto& format : LiverpoolToVK::SurfaceFormats()) { for (const auto& format : LiverpoolToVK::SurfaceFormats()) {
if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) { if (!IsFormatSupported(format.vk_format, format.flags)) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Surface format data_format={}, number_format={} is not fully supported " "Surface format data_format={}, number_format={} is not fully supported "
"(vk_format={}, requested flags={})", "(vk_format={}, missing features={})",
static_cast<u32>(format.data_format), static_cast<u32>(format.data_format),
static_cast<u32>(format.number_format), vk::to_string(format.vk_format), static_cast<u32>(format.number_format), vk::to_string(format.vk_format),
vk::to_string(format.flags)); vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format)));
} }
} }
for (const auto& format : LiverpoolToVK::DepthFormats()) { for (const auto& format : LiverpoolToVK::DepthFormats()) {
if (!IsFormatSupported(GetSupportedFormat(format.vk_format, format.flags), format.flags)) { if (!IsFormatSupported(format.vk_format, format.flags)) {
LOG_WARNING(Render_Vulkan, LOG_WARNING(Render_Vulkan,
"Depth format z_format={}, stencil_format={} is not fully supported " "Depth format z_format={}, stencil_format={} is not fully supported "
"(vk_format={}, requested flags={})", "(vk_format={}, missing features={})",
static_cast<u32>(format.z_format), static_cast<u32>(format.stencil_format), static_cast<u32>(format.z_format), static_cast<u32>(format.stencil_format),
vk::to_string(format.vk_format), vk::to_string(format.flags)); vk::to_string(format.vk_format),
vk::to_string(format.flags & ~GetFormatFeatureFlags(format.vk_format)));
} }
} }
} }
@ -546,18 +547,21 @@ void Instance::CollectToolingInfo() {
} }
} }
bool Instance::IsFormatSupported(const vk::Format format, vk::FormatFeatureFlags2 Instance::GetFormatFeatureFlags(vk::Format format) const {
const vk::FormatFeatureFlags2 flags) const {
if (format == vk::Format::eUndefined) [[unlikely]] {
return true;
}
const auto it = format_properties.find(format); const auto it = format_properties.find(format);
if (it == format_properties.end()) { if (it == format_properties.end()) {
UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format)); UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format));
} }
return ((it->second.optimalTilingFeatures | it->second.bufferFeatures) & flags) == flags; return it->second.optimalTilingFeatures | it->second.bufferFeatures;
}
bool Instance::IsFormatSupported(const vk::Format format,
const vk::FormatFeatureFlags2 flags) const {
if (format == vk::Format::eUndefined) [[unlikely]] {
return true;
}
return (GetFormatFeatureFlags(format) & flags) == flags;
} }
static vk::Format GetAlternativeFormat(const vk::Format format) { static vk::Format GetAlternativeFormat(const vk::Format format) {

View File

@ -275,6 +275,9 @@ private:
void CollectDeviceParameters(); void CollectDeviceParameters();
void CollectToolingInfo(); void CollectToolingInfo();
/// Gets the supported feature flags for a format.
[[nodiscard]] vk::FormatFeatureFlags2 GetFormatFeatureFlags(vk::Format format) const;
/// Determines if a format is supported for a set of feature flags. /// Determines if a format is supported for a set of feature flags.
[[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const; [[nodiscard]] bool IsFormatSupported(vk::Format format, vk::FormatFeatureFlags2 flags) const;

View File

@ -181,26 +181,6 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
PipelineCache::~PipelineCache() = default; PipelineCache::~PipelineCache() = default;
const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() { const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
const auto& regs = liverpool->regs;
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
return nullptr;
}
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
// actual draw hence can skip pipeline creation.
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) {
LOG_TRACE(Render_Vulkan, "FCE pass skipped");
return nullptr;
}
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) {
// TODO: check for a valid MRT1 to promote the draw to the resolve pass.
LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped");
return nullptr;
}
if (regs.primitive_type == AmdGpu::PrimitiveType::None) {
LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped");
return nullptr;
}
if (!RefreshGraphicsKey()) { if (!RefreshGraphicsKey()) {
return nullptr; return nullptr;
} }

View File

@ -41,9 +41,43 @@ void Rasterizer::CpSync() {
vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {}); vk::DependencyFlagBits::eByRegion, ib_barrier, {}, {});
} }
bool Rasterizer::FilterDraw() {
const auto& regs = liverpool->regs;
// Tessellation is unsupported so skip the draw to avoid locking up the driver.
if (regs.primitive_type == AmdGpu::PrimitiveType::PatchPrimitive) {
return false;
}
// There are several cases (e.g. FCE, FMask/HTile decompression) where we don't need to do an
// actual draw hence can skip pipeline creation.
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::EliminateFastClear) {
LOG_TRACE(Render_Vulkan, "FCE pass skipped");
return false;
}
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::FmaskDecompress) {
// TODO: check for a valid MRT1 to promote the draw to the resolve pass.
LOG_TRACE(Render_Vulkan, "FMask decompression pass skipped");
return false;
}
if (regs.color_control.mode == Liverpool::ColorControl::OperationMode::Resolve) {
LOG_TRACE(Render_Vulkan, "Resolve pass");
Resolve();
return false;
}
if (regs.primitive_type == AmdGpu::PrimitiveType::None) {
LOG_TRACE(Render_Vulkan, "Primitive type 'None' skipped");
return false;
}
return true;
}
void Rasterizer::Draw(bool is_indexed, u32 index_offset) { void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
RENDERER_TRACE; RENDERER_TRACE;
if (!FilterDraw()) {
return;
}
const auto cmdbuf = scheduler.CommandBuffer(); const auto cmdbuf = scheduler.CommandBuffer();
const auto& regs = liverpool->regs; const auto& regs = liverpool->regs;
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
@ -80,6 +114,10 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) { void Rasterizer::DrawIndirect(bool is_indexed, VAddr address, u32 offset, u32 size) {
RENDERER_TRACE; RENDERER_TRACE;
if (!FilterDraw()) {
return;
}
const auto cmdbuf = scheduler.CommandBuffer(); const auto cmdbuf = scheduler.CommandBuffer();
const auto& regs = liverpool->regs; const auto& regs = liverpool->regs;
const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline(); const GraphicsPipeline* pipeline = pipeline_cache.GetGraphicsPipeline();
@ -258,6 +296,54 @@ void Rasterizer::BeginRendering(const GraphicsPipeline& pipeline) {
scheduler.BeginRendering(state); scheduler.BeginRendering(state);
} }
void Rasterizer::Resolve() {
const auto cmdbuf = scheduler.CommandBuffer();
// Read from MRT0, average all samples, and write to MRT1, which is one-sample
const auto& mrt0_hint = liverpool->last_cb_extent[0];
const auto& mrt1_hint = liverpool->last_cb_extent[1];
VideoCore::ImageInfo mrt0_info{liverpool->regs.color_buffers[0], mrt0_hint};
VideoCore::ImageInfo mrt1_info{liverpool->regs.color_buffers[1], mrt1_hint};
auto& mrt0_image = texture_cache.GetImage(texture_cache.FindImage(mrt0_info));
auto& mrt1_image = texture_cache.GetImage(texture_cache.FindImage(mrt1_info));
VideoCore::SubresourceRange mrt0_range;
mrt0_range.base.layer = liverpool->regs.color_buffers[0].view.slice_start;
mrt0_range.extent.layers = liverpool->regs.color_buffers[0].NumSlices() - mrt0_range.base.layer;
VideoCore::SubresourceRange mrt1_range;
mrt1_range.base.layer = liverpool->regs.color_buffers[1].view.slice_start;
mrt1_range.extent.layers = liverpool->regs.color_buffers[1].NumSlices() - mrt1_range.base.layer;
vk::ImageResolve region = {
.srcSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = mrt0_range.base.layer,
.layerCount = mrt0_range.extent.layers,
},
.srcOffset = {0, 0, 0},
.dstSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = mrt1_range.base.layer,
.layerCount = mrt1_range.extent.layers,
},
.dstOffset = {0, 0, 0},
.extent = {mrt1_image.info.size.width, mrt1_image.info.size.height, 1},
};
mrt0_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead,
mrt0_range);
mrt1_image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
mrt1_range);
cmdbuf.resolveImage(mrt0_image.image, vk::ImageLayout::eTransferSrcOptimal, mrt1_image.image,
vk::ImageLayout::eTransferDstOptimal, region);
}
void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { void Rasterizer::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
buffer_cache.InlineData(address, value, num_bytes, is_gds); buffer_cache.InlineData(address, value, num_bytes, is_gds);
} }

View File

@ -54,11 +54,14 @@ public:
private: private:
void BeginRendering(const GraphicsPipeline& pipeline); void BeginRendering(const GraphicsPipeline& pipeline);
void Resolve();
void UpdateDynamicState(const GraphicsPipeline& pipeline); void UpdateDynamicState(const GraphicsPipeline& pipeline);
void UpdateViewportScissorState(); void UpdateViewportScissorState();
void UpdateDepthStencilState(); void UpdateDepthStencilState();
bool FilterDraw();
private: private:
const Instance& instance; const Instance& instance;
Scheduler& scheduler; Scheduler& scheduler;

View File

@ -219,6 +219,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
pitch = image.Pitch(); pitch = image.Pitch();
resources.levels = image.NumLevels(); resources.levels = image.NumLevels();
resources.layers = image.NumLayers(desc.is_array); resources.layers = image.NumLayers(desc.is_array);
num_samples = image.NumSamples();
num_bits = NumBits(image.GetDataFmt()); num_bits = NumBits(image.GetDataFmt());
usage.texture = true; usage.texture = true;

View File

@ -80,7 +80,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso
} }
range.base.level = image.base_level; range.base.level = image.base_level;
range.base.layer = image.base_array; range.base.layer = image.base_array;
range.extent.levels = image.last_level - image.base_level + 1; if (image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) {
range.extent.levels = 1;
} else {
range.extent.levels = image.last_level - image.base_level + 1;
}
range.extent.layers = image.last_array - image.base_array + 1; range.extent.layers = image.last_array - image.base_array + 1;
type = ConvertImageViewType(image.GetType()); type = ConvertImageViewType(image.GetType());

View File

@ -221,7 +221,8 @@ ImageId TextureCache::FindImage(const ImageInfo& info, FindFlags flags) {
!IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) { !IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format)) {
continue; continue;
} }
ASSERT(cache_image.info.type == info.type || True(flags & FindFlags::RelaxFmt)); ASSERT((cache_image.info.type == info.type || info.size == Extent3D{1, 1, 1} ||
True(flags & FindFlags::RelaxFmt)));
image_id = cache_id; image_id = cache_id;
} }

View File

@ -38,6 +38,10 @@ struct Extent3D {
u32 depth; u32 depth;
auto operator<=>(const Extent3D&) const = default; auto operator<=>(const Extent3D&) const = default;
bool operator==(const Extent3D& other) const {
return width == other.width && height == other.height && depth == other.depth;
}
}; };
struct SubresourceLayers { struct SubresourceLayers {