mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-27 00:20:54 +00:00
commit
156bb3a36d
@ -11,7 +11,7 @@ AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
PackConstructorInitializers: NextLine
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
@ -58,13 +58,11 @@ BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 160
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
SeparateDefinitionBlocks: Always
|
||||
@ -73,40 +71,17 @@ ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
- FOR
|
||||
- FOR_HASH
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"(Kyty)/'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^"(Emulator)/'
|
||||
- Regex: '^<'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^"(gtest)/'
|
||||
Priority: 4
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<Q'
|
||||
Priority: 5
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '"ui_'
|
||||
Priority: 6
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<'
|
||||
Priority: 7
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '$'
|
||||
@ -127,11 +102,6 @@ MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
@ -158,28 +128,21 @@ SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInParensOptions:
|
||||
InEmptyParentheses: false
|
||||
InCStyleCasts: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 4
|
||||
UseCRLF: true
|
||||
LineEnding: LF
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
||||
|
@ -1,2 +1 @@
|
||||
**/third_party/**
|
||||
**/vk_mem_alloc.h
|
||||
**/wepoll.*
|
||||
|
58
.github/workflows/build.yml
vendored
58
.github/workflows/build.yml
vendored
@ -32,15 +32,43 @@ on:
|
||||
|
||||
jobs:
|
||||
formatting-check:
|
||||
permissions:
|
||||
contents: write
|
||||
name: Formatting check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DoozyX/clang-format-lint-action@v0.17
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Test formatting
|
||||
uses: DoozyX/clang-format-lint-action@v0.17
|
||||
with:
|
||||
source: ./
|
||||
extensions: h,hpp,cpp,c,cc
|
||||
clangFormatVersion: 17
|
||||
inplace: true
|
||||
|
||||
- name: Generate patch file
|
||||
run: |
|
||||
git diff > formatting.patch
|
||||
find *.patch -type f -size 0 -delete
|
||||
|
||||
- name: Upload patch
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: code-format
|
||||
path: formatting.patch
|
||||
|
||||
- name: Error if fixed
|
||||
run: |
|
||||
if rm formatting.patch; then
|
||||
echo "# Looks like you messed something up!" >> $GITHUB_STEP_SUMMARY
|
||||
echo "1. Download the \`code-format\` artifact above" >> $GITHUB_STEP_SUMMARY
|
||||
echo "2. Unpack the patch file" >> $GITHUB_STEP_SUMMARY
|
||||
echo "3. Use \`git apply formatting.patch\` command to apply it to your repo" >> $GITHUB_STEP_SUMMARY
|
||||
echo "4. Push the applied changes to your Pull Request" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
build:
|
||||
needs: formatting-check
|
||||
@ -52,19 +80,6 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Restore cached deps
|
||||
uses: actions/cache@v4
|
||||
id: cache-thirdparty
|
||||
with:
|
||||
path: third_party
|
||||
key: ${{ runner.os }}-submodules
|
||||
|
||||
- name: Update submodules
|
||||
if: steps.cache-thirdparty.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Detect MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
@ -79,7 +94,7 @@ jobs:
|
||||
- name: Configure
|
||||
run: |
|
||||
echo "::add-matcher::tools/matchers/cmake.json"
|
||||
cmake.exe -S. -B_build/_Release -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=_build/_Install -DCMAKE_C_COMPILER=clang-cl.exe -DCMAKE_CXX_COMPILER=clang-cl.exe
|
||||
cmake.exe -S. -B_build/_Release -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=_build/_Install -DCMAKE_C_COMPILER=clang-cl.exe -DCMAKE_CXX_COMPILER=clang-cl.exe -DTEST_BENCH=ON -DISACTION=1
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
@ -92,8 +107,15 @@ jobs:
|
||||
echo "::add-matcher::tools/matchers/cmake.json"
|
||||
cmake.exe --install _build/_Release
|
||||
|
||||
- name: Run some tests
|
||||
shell: bash
|
||||
run: echo "`node misc/testRunner`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: psOff-libs
|
||||
path: _build/_Install/*
|
||||
name: psOff-emu
|
||||
path: |
|
||||
_build/_Install/*
|
||||
!_build/_Install/vulkan-1.dll
|
||||
!_build/_Install/*_test.exe
|
||||
|
20
.github/workflows/release_notify.yml
vendored
Normal file
20
.github/workflows/release_notify.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
name: Discord release notify
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
notifier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run things
|
||||
working-directory: misc/discordNotify
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
run: |
|
||||
npm i
|
||||
node .
|
39
.github/workflows/upwiki.yml
vendored
Normal file
39
.github/workflows/upwiki.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: upload wiki
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '.github/workflows/upwiki.yml'
|
||||
- 'docs/wiki/**'
|
||||
|
||||
jobs:
|
||||
update-wiki:
|
||||
name: Update wiki
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Pull main repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: main_repo
|
||||
|
||||
- name: Setup git
|
||||
run: |
|
||||
git config --global user.name github-actions[bot];
|
||||
git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com;
|
||||
|
||||
- name: Pull wiki content
|
||||
run: |
|
||||
mkdir wiki_repo;
|
||||
cd wiki_repo;
|
||||
git init;
|
||||
git pull https://${{ secrets.REPO_FULL_TOKEN }}@github.com/${{ github.repository }}.wiki.git
|
||||
|
||||
- name: Push wiki content
|
||||
run: |
|
||||
rsync -av --delete main_repo/docs/wiki/markdown/ wiki_repo/ --exclude .git;
|
||||
cd wiki_repo;
|
||||
git add .;
|
||||
if git commit -m "Update wiki content"; then
|
||||
git push -f --set-upstream https://${{ secrets.REPO_FULL_TOKEN }}@github.com/${{ github.repository }}.wiki.git master;
|
||||
fi
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ _*/
|
||||
!.vscode
|
||||
!.github
|
||||
.vscode/c_cpp_properties.json
|
||||
core/private
|
13
.gitmodules
vendored
13
.gitmodules
vendored
@ -1,13 +0,0 @@
|
||||
[submodule "third_party/optick"]
|
||||
path = third_party/optick
|
||||
url = https://github.com/bombomby/optick.git
|
||||
[submodule "third_party/magic_enum"]
|
||||
path = third_party/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
[submodule "third_party/boost"]
|
||||
path = third_party/boost
|
||||
url = https://github.com/boostorg/boost.git
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
branch = SDL2
|
13
.vscode/launch.json
vendored
13
.vscode/launch.json
vendored
@ -5,12 +5,17 @@
|
||||
"name": "(Windows) Launch",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${config:psoff.root-path}/emulator.exe",
|
||||
"args": ["--file", "${config:psoff.game-path}/eboot.bin", "--d"],
|
||||
"program": "${workspaceRoot}/_build/_Install/psoff.exe",
|
||||
"args": [
|
||||
"--d",
|
||||
"${config:psoff.add-arg}",
|
||||
"--file=${config:psoff.game-path}/eboot.bin",
|
||||
],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${config:psoff.root-path}",
|
||||
"cwd": "${workspaceRoot}/_build/_Install",
|
||||
"environment": [],
|
||||
"console": "integratedTerminal"
|
||||
"console": "integratedTerminal",
|
||||
"symbolSearchPath": "${workspaceRoot}/_build/_Install/debug"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@ -14,7 +14,7 @@
|
||||
"-fexceptions"
|
||||
],
|
||||
"ccls.index.threads": 0,
|
||||
|
||||
"cmake.parallelJobs": 0,
|
||||
"cmake.generator": "Ninja",
|
||||
"cmake.sourceDirectory": "${workspaceFolder}",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/_build/_Release",
|
||||
@ -24,16 +24,14 @@
|
||||
"CMAKE_C_COMPILER": "clang-cl.exe",
|
||||
"CMAKE_CXX_COMPILER": "clang-cl.exe"
|
||||
},
|
||||
"C_Cpp.formatting": "clangFormat",
|
||||
"C_Cpp.clang_format_style": "file:${workspaceRoot}/.clang-format",
|
||||
"C_Cpp.clang_format_fallbackStyle": "LLVM",
|
||||
"C_Cpp.clang_format_path": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\x64\\bin\\clang-format.exe",
|
||||
"C_Cpp.default.compilerPath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\Llvm\\bin\\clang-cl.exe",
|
||||
"C_Cpp.default.intelliSenseMode": "windows-clang-x64",
|
||||
"C_Cpp.default.cppStandard": "c++20",
|
||||
"C_Cpp.autoAddFileAssociations": false,
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true,
|
||||
"git.ignoredRepositories": [
|
||||
"third_party/boost",
|
||||
"third_party/optick",
|
||||
"third_party/magic_enum",
|
||||
"third_part/SDL2"
|
||||
]
|
||||
}
|
||||
|
4
.vscode/tasks.json
vendored
4
.vscode/tasks.json
vendored
@ -25,6 +25,7 @@
|
||||
"-S${workspaceFolder}",
|
||||
"-B_build/_Release",
|
||||
"-GNinja",
|
||||
//"-DBUILD_CORE=1",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DCMAKE_INSTALL_PREFIX=_build/_Install",
|
||||
"-DCMAKE_C_COMPILER=clang-cl.exe",
|
||||
@ -63,6 +64,9 @@
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"Build"
|
||||
],
|
||||
"presentation": {},
|
||||
},
|
||||
{
|
||||
|
139
CMakeLists.txt
139
CMakeLists.txt
@ -1,4 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
include(ExternalProject)
|
||||
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF)
|
||||
set(CMAKE_WINDOWS_SYMBOL_VISIBILITY_IN_STATIC_LIBRARIES OFF)
|
||||
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
|
||||
|
||||
set(PSOFF_LIB_VERSION v.0.3)
|
||||
set(PSOFF_RENDER_VERSION v.0.5)
|
||||
|
||||
set(ProjectName psOff_${CMAKE_BUILD_TYPE})
|
||||
project(${ProjectName} VERSION 0.0.1)
|
||||
@ -7,19 +18,21 @@ unset(CMAKE_IMPORT_LIBRARY_SUFFIX)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Zi /GS- /GF /Gy /GR /Oi -Ofast -fno-strict-aliasing")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG /OPT:REF,ICF")
|
||||
set(CMAKE_INSTALL_MESSAGE LAZY)
|
||||
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
set(CMAKE_WINDOWS_SYMBOL_VISIBILITY_IN_STATIC_LIBRARIES OFF)
|
||||
if(ISDEBUG)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Zi /GS- /GF /Gy /GR /Oi -O0 -fno-strict-aliasing")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG /OPT:REF,ICF")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MD /GS- /GF /Gy /GR /Oi -Ofast -fno-strict-aliasing")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:REF,ICF")
|
||||
endif()
|
||||
|
||||
if(NOT PRJ_SRC_DIR)
|
||||
set(PRJ_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED IMAGE_BASE)
|
||||
set(IMAGE_BASE 0x10000000)
|
||||
endif()
|
||||
set(IMAGE_BASE 0x10000000)
|
||||
|
||||
# # Gather Infos
|
||||
|
||||
@ -33,6 +46,7 @@ message("Vulkan Path: ${VulkanPath}")
|
||||
|
||||
# # - Gather Infos
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # Move all libs here post-build, except third_party
|
||||
add_definitions(-DPSOFF_RENDER_VERSION="${PSOFF_RENDER_VERSION}")
|
||||
|
||||
#
|
||||
include_directories(BEFORE
|
||||
@ -40,41 +54,130 @@ include_directories(BEFORE
|
||||
${PRJ_SRC_DIR}/modules_include
|
||||
${PRJ_SRC_DIR}/tools/logging
|
||||
${PRJ_SRC_DIR}/tools/config_emu
|
||||
${PRJ_SRC_DIR}/tools/gamereport
|
||||
${PRJ_SRC_DIR}
|
||||
${CMAKE_BINARY_DIR}/third_party/install/include
|
||||
${CMAKE_BINARY_DIR}/third_party/src/third_party/include
|
||||
${CMAKE_BINARY_DIR}/psoff_render/src/psoff_render/include
|
||||
)
|
||||
|
||||
link_directories(BEFORE
|
||||
${CMAKE_INSTALL_PREFIX}/development/lib
|
||||
${CMAKE_BINARY_DIR}/third_party/install/lib
|
||||
${CMAKE_BINARY_DIR}/core
|
||||
${CMAKE_BINARY_DIR}/third_party/src/third_party/lib
|
||||
${CMAKE_BINARY_DIR}/lib
|
||||
${CMAKE_BINARY_DIR}/bin
|
||||
${CMAKE_BINARY_DIR}/psoff_render/src/psoff_render/lib
|
||||
${CMAKE_BINARY_DIR}/core/export/lib
|
||||
)
|
||||
|
||||
add_subdirectory(tools/logging) # include before link_libraries
|
||||
add_subdirectory(tools/config_emu) # include before link_libraries
|
||||
ExternalProject_Add(third_party
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
PREFIX ${CMAKE_BINARY_DIR}/third_party
|
||||
BUILD_IN_SOURCE 1
|
||||
URL https://github.com/SysRay/psOff_thirdParty/releases/download/${PSOFF_LIB_VERSION}/psOff-3rd.zip
|
||||
)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
ExternalProject_Add(psoff_render
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
PREFIX ${CMAKE_BINARY_DIR}/psoff_render
|
||||
BUILD_IN_SOURCE 1
|
||||
URL https://github.com/SysRay/psOff_Renderer/releases/download/${PSOFF_RENDER_VERSION}/psOff-renderer.zip
|
||||
)
|
||||
endif()
|
||||
|
||||
# set(TEST_BENCH OFF CACHE BOOL "Enable testing")
|
||||
|
||||
# if(TEST_BENCH)
|
||||
# add_subdirectory(tests)
|
||||
# endif()
|
||||
|
||||
# include before link_libraries
|
||||
add_subdirectory(tools/logging)
|
||||
add_subdirectory(tools/config_emu)
|
||||
add_subdirectory(tools/dll2Nids)
|
||||
add_subdirectory(asm)
|
||||
|
||||
add_dependencies(dll2Nids third_party)
|
||||
|
||||
# -
|
||||
link_libraries(
|
||||
logging.lib
|
||||
)
|
||||
add_compile_definitions(IMAGE_BASE=${IMAGE_BASE})
|
||||
|
||||
# # Projects
|
||||
include("third_party/third_party.cmake")
|
||||
|
||||
# Internal Projects
|
||||
add_subdirectory(modules)
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(utility)
|
||||
add_subdirectory(tools/gamereport)
|
||||
|
||||
if(BUILD_CORE)
|
||||
add_subdirectory(core)
|
||||
else()
|
||||
add_custom_target(core)
|
||||
endif()
|
||||
|
||||
# #- Projects
|
||||
|
||||
# Executable
|
||||
add_executable(psoff
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_dependencies(psoff logging core psOff_utility gamereport)
|
||||
|
||||
target_link_libraries(psoff PRIVATE
|
||||
core.lib
|
||||
gamereport.lib
|
||||
psOff_utility
|
||||
libboost_filesystem
|
||||
$<TARGET_OBJECTS:asmHelper>
|
||||
)
|
||||
|
||||
target_link_options(psoff PUBLIC /DYNAMICBASE:NO --image-base=${IMAGE_BASE})
|
||||
|
||||
set_property(TARGET psoff PROPERTY VS_DPI_AWARE "PerMonitor") # SDL2 High dpi window
|
||||
|
||||
# # Install
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/third_party/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/third_party/src/third_party/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
FILES_MATCHING PATTERN "*.dll"
|
||||
)
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/third_party/install/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
FILES_MATCHING PATTERN "*.dll"
|
||||
)
|
||||
|
||||
install(DIRECTORY "${CMAKE_BINARY_DIR}/lib/" DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
FILES_MATCHING PATTERN "*.dll"
|
||||
)
|
||||
|
||||
install(DIRECTORY "${PRJ_SRC_DIR}/docs/json/" DESTINATION ${CMAKE_INSTALL_PREFIX}/config/.schemas
|
||||
FILES_MATCHING PATTERN "*.json"
|
||||
)
|
||||
|
||||
if(DEFINED ISACTION)
|
||||
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/bin/vulkan-1.dll CONTENT "") # cmake needs to find the file in order to ignore it
|
||||
endif()
|
||||
|
||||
install(TARGETS psoff
|
||||
COMPONENT psoff
|
||||
RUNTIME_DEPENDENCIES
|
||||
PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-"
|
||||
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" ".*vulkan-1\\.dll"
|
||||
|
||||
DIRECTORIES
|
||||
$<TARGET_FILE_DIR:psoff>
|
||||
${CMAKE_BINARY_DIR}/third_party/src/third_party/bin
|
||||
${CMAKE_BINARY_DIR}/third_party/src/third_party/lib
|
||||
${CMAKE_BINARY_DIR}/bin
|
||||
${CMAKE_BINARY_DIR}/lib
|
||||
${CMAKE_CURRENT_BINARY_DIR}/core
|
||||
${CMAKE_BINARY_DIR}/psoff_render/src/psoff_render/lib
|
||||
${CMAKE_BINARY_DIR}/core/export/lib
|
||||
DESTINATION .
|
||||
)
|
||||
|
||||
install(FILES $<TARGET_PDB_FILE:psoff> DESTINATION ${CMAKE_INSTALL_PREFIX}/debug OPTIONAL)
|
||||
|
4
asm/CMakeLists.txt
Normal file
4
asm/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
enable_language(C ASM_MASM)
|
||||
add_library(asmHelper OBJECT
|
||||
entry.asm
|
||||
)
|
30
asm/entry.asm
Normal file
30
asm/entry.asm
Normal file
@ -0,0 +1,30 @@
|
||||
.CODE
|
||||
|
||||
PUBLIC jmpEntry
|
||||
jmpEntry PROC
|
||||
|
||||
; Input
|
||||
; RDI addr
|
||||
; RSI EntryParams
|
||||
; RDX exitHandler
|
||||
|
||||
mov RAX, RDI
|
||||
|
||||
; align stack
|
||||
SUB RSP, 32
|
||||
AND RSP, -10h
|
||||
|
||||
; copy entry param to stack
|
||||
ADD RSP, 40
|
||||
push [RSI+24]
|
||||
push [RSI+16]
|
||||
push [RSI+8]
|
||||
push [RSI]
|
||||
; -
|
||||
|
||||
lea rdi, [RSP] ; param 1 (EntryParam) is @ rsp
|
||||
mov rsi, RDX ; param2 (atexit)
|
||||
jmp RAX
|
||||
|
||||
jmpEntry ENDP
|
||||
End
|
6
asm/helper.h
Normal file
6
asm/helper.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "utility/utility.h"
|
||||
|
||||
extern "C" {
|
||||
SYSV_ABI void jmpEntry(uint64_t addr, void const* entryParams, void* exitHandler);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
# Place before add_subdir -> define for all childs
|
||||
add_compile_definitions(
|
||||
BOOST_ALL_NO_LIB
|
||||
)
|
||||
|
||||
# Add directories
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(videoout)
|
||||
add_subdirectory(initParams)
|
||||
add_subdirectory(timer)
|
||||
add_subdirectory(systemContent)
|
||||
add_subdirectory(fileManager)
|
||||
add_subdirectory(imports)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(dmem)
|
||||
add_subdirectory(unwinding)
|
||||
|
||||
# Build
|
||||
add_library(core SHARED
|
||||
$<TARGET_OBJECTS:kernel>
|
||||
$<TARGET_OBJECTS:initParams>
|
||||
$<TARGET_OBJECTS:timer>
|
||||
$<TARGET_OBJECTS:systemContent>
|
||||
$<TARGET_OBJECTS:videoout>
|
||||
$<TARGET_OBJECTS:fileManager>
|
||||
$<TARGET_OBJECTS:imports>
|
||||
$<TARGET_OBJECTS:memory>
|
||||
$<TARGET_OBJECTS:dmem>
|
||||
$<TARGET_OBJECTS:unwinding>
|
||||
)
|
||||
|
||||
add_dependencies(core logging boost)
|
||||
target_link_libraries(core PRIVATE
|
||||
libboost_thread
|
||||
libboost_chrono
|
||||
libboost_program_options
|
||||
sdl2
|
||||
OptickCore
|
||||
psOff_utility
|
||||
${Vulkan_LIBRARIES}
|
||||
)
|
||||
|
||||
ADD_CUSTOM_COMMAND(TARGET core POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_CURRENT_BINARY_DIR}/core.lib
|
||||
${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
install(FILES $<TARGET_PDB_FILE:core> DESTINATION debug OPTIONAL)
|
||||
install(TARGETS core LIBRARY DESTINATION .)
|
@ -1,9 +0,0 @@
|
||||
add_library(dmem OBJECT
|
||||
dmem.cpp
|
||||
)
|
||||
|
||||
add_dependencies(dmem third_party boost psOff_utility)
|
||||
|
||||
target_include_directories(dmem PRIVATE
|
||||
${PRJ_SRC_DIR}/third_party/magic_enum/include
|
||||
)
|
@ -1,266 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "dmem.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "core/memory/memory.h"
|
||||
#include "core/videoout/videoout.h"
|
||||
#include "logging.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/thread.hpp>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
LOG_DEFINE_MODULE(MemoryManager);
|
||||
|
||||
namespace {
|
||||
|
||||
struct MemoryInfo {
|
||||
bool isGpu = false;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
struct pImpl {
|
||||
boost::mutex mutex_;
|
||||
|
||||
std::unordered_map<uint64_t, MappingType> mappings_;
|
||||
};
|
||||
|
||||
auto getData() {
|
||||
static pImpl obj;
|
||||
return &obj;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Allocates the sections (memory pool is done internally (in app))
|
||||
class PhysicalMemory: public IPhysicalMemory {
|
||||
std::mutex m_mutex_int;
|
||||
|
||||
std::unordered_map<uint64_t, MemoryInfo> m_objects;
|
||||
|
||||
public:
|
||||
PhysicalMemory() = default;
|
||||
|
||||
virtual ~PhysicalMemory() { deinit(); }
|
||||
|
||||
uint64_t alloc(uint64_t vaddr, size_t len, int memoryTye) final;
|
||||
bool reserve(uint64_t start, size_t len, size_t alignment, uint64_t* outAddr, int memoryType) final;
|
||||
uintptr_t commit(uint64_t base, uint64_t offset, size_t len, size_t alignment, int prot) final;
|
||||
bool Map(uint64_t vaddr, uint64_t physAddr, size_t len, int prot, bool allocFixed, size_t alignment, uint64_t* outAddr) final;
|
||||
bool Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size) final;
|
||||
bool Unmap(uint64_t vaddr, uint64_t size) final;
|
||||
void deinit() final;
|
||||
};
|
||||
|
||||
IPhysicalMemory& accessPysicalMemory() {
|
||||
static PhysicalMemory inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
uint64_t PhysicalMemory::alloc(uint64_t vaddr, size_t len, int prot) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
m_allocSize += len;
|
||||
LOG_DEBUG(L"Alloc: 0x%08llx len:0x%08llx prot:%d curSize=0x%08llx", vaddr, len, prot, m_allocSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::reserve(uint64_t start, size_t len, size_t alignment, uint64_t* outAddr, int memoryType) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
auto const isGpu = memoryType == 3;
|
||||
*outAddr = memory::reserve(start, len, alignment, isGpu);
|
||||
|
||||
{
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
m_availableSize -= len;
|
||||
}
|
||||
|
||||
LOG_DEBUG(L"Reserve| start:0x%08llx size:%llu alignment:%llu memType:%d -> @%08llx", start, len, alignment, memoryType, *outAddr);
|
||||
return *outAddr != 0;
|
||||
}
|
||||
|
||||
uintptr_t PhysicalMemory::commit(uint64_t base, uint64_t vaddr, size_t len, size_t alignment, int prot) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
uintptr_t addr = 0;
|
||||
|
||||
auto [protCPU, protGPU] = util::getMemoryProtection(prot);
|
||||
|
||||
if (protGPU != 0) {
|
||||
addr = memory::allocGPUMemory(base, 0, len, alignment);
|
||||
} else {
|
||||
addr = memory::commit(base, 0, len, alignment, prot);
|
||||
}
|
||||
|
||||
m_objects[addr] = MemoryInfo {.isGpu = protGPU != 0, .size = len};
|
||||
|
||||
if (protGPU != 0) {
|
||||
if (!accessVideoOut().notify_allocHeap(addr, len, prot)) {
|
||||
LOG_ERR(L"Commit| Couldn't allocHeap| base:0x%08llx offset:0x%08llx size:%llu alignment:%llu prot:%d -> @%08llx", base, vaddr, len, alignment, prot,
|
||||
addr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(L"Commit| base:0x%08llx offset:0x%08llx size:%llu alignment:%llu prot:%d -> @%08llx", base, vaddr, len, alignment, prot, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Map(uint64_t vaddr, uint64_t physAddr, size_t len, int prot, bool allocFixed, size_t alignment, uint64_t* outAddr) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
bool mapped = false;
|
||||
*outAddr = 0;
|
||||
{
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
|
||||
auto [protCPU, protGPU] = util::getMemoryProtection(prot);
|
||||
|
||||
if (allocFixed) {
|
||||
if (memory::allocFixed(physAddr, len, prot)) {
|
||||
*outAddr = physAddr;
|
||||
}
|
||||
} else {
|
||||
*outAddr = memory::allocAligned(physAddr, len, prot, alignment);
|
||||
}
|
||||
|
||||
m_objects[*outAddr] = MemoryInfo {.isGpu = protGPU != 0, .size = len};
|
||||
|
||||
if (protGPU != 0) {
|
||||
if (!accessVideoOut().notify_allocHeap(*outAddr, len, prot)) {
|
||||
LOG_ERR(L"Map| Couldn't allocHeap vaddr:0x%08llx physAddr:0x%08llx len:0x%08llx prot:0x%x -> out:0x%08llx", vaddr, physAddr, len, prot, *outAddr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (*outAddr == NULL) {
|
||||
return false;
|
||||
}
|
||||
m_availableSize -= len;
|
||||
}
|
||||
|
||||
LOG_INFO(L"Map| vaddr:0x%08llx physAddr:0x%08llx len:0x%08llx prot:0x%x -> out:0x%08llx", vaddr, physAddr, len, prot, *outAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
m_allocSize -= len;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhysicalMemory::Unmap(uint64_t vaddr, uint64_t size) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
if (auto it = m_objects.find(vaddr); it != m_objects.end()) {
|
||||
if (it->second.isGpu) {
|
||||
// if(isGPU) accessGpuMemory().freeHeap(vaddr); // todo, notify free (should free host memory aswell)
|
||||
memory::free(vaddr);
|
||||
} else {
|
||||
memory::free(vaddr);
|
||||
}
|
||||
} else {
|
||||
LOG_ERR(L"Unmap not in map: vaddr:0x%08llx len:%lld", vaddr, size);
|
||||
memory::free(vaddr);
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
m_availableSize += size;
|
||||
}
|
||||
|
||||
LOG_INFO(L"Unmap: vaddr:0x%08llx len:%lld", vaddr, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PhysicalMemory::deinit() {
|
||||
for (auto& item: m_objects) {
|
||||
if (item.second.isGpu) {
|
||||
// done by gpuMemoryManager
|
||||
} else {
|
||||
memory::free(item.first);
|
||||
}
|
||||
}
|
||||
|
||||
m_objects.clear();
|
||||
}
|
||||
|
||||
class FlexibleMemory: public IFlexibleMemory {
|
||||
uint64_t m_totalAllocated = 0;
|
||||
std::mutex m_mutex_int;
|
||||
|
||||
public:
|
||||
FlexibleMemory() = default;
|
||||
virtual ~FlexibleMemory() = default;
|
||||
|
||||
uint64_t alloc(uint64_t vaddr, size_t len, int prot) final;
|
||||
bool destroy(uint64_t vaddr, uint64_t size) final;
|
||||
|
||||
void release(uint64_t start, size_t len) final;
|
||||
|
||||
uint64_t available() final {
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
return (uint64_t)448 * 1024 * 1024 - m_totalAllocated; // todo get system ram
|
||||
}
|
||||
};
|
||||
|
||||
IFlexibleMemory& accessFlexibleMemory() {
|
||||
static FlexibleMemory inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
uint64_t FlexibleMemory::alloc(uint64_t vaddr, size_t len, int prot) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
m_totalAllocated += len;
|
||||
|
||||
auto const outAddr = memory::alloc(vaddr, len, prot);
|
||||
|
||||
registerMapping(outAddr, MappingType::Flexible);
|
||||
LOG_INFO(L"--> Heap| vaddr:0x%08llx len:%llu prot:0x%x total:0x%08llx -> @0x%08llx", vaddr, len, prot, m_totalAllocated, outAddr);
|
||||
return outAddr;
|
||||
}
|
||||
|
||||
bool FlexibleMemory::destroy(uint64_t vaddr, uint64_t size) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
|
||||
std::unique_lock const lock(m_mutex_int);
|
||||
m_totalAllocated -= size;
|
||||
|
||||
memory::free(vaddr);
|
||||
LOG_INFO(L"<-- Heap| vaddr:0x%08llx len:%lld total:0x%08llx", vaddr, size, m_totalAllocated);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlexibleMemory::release(uint64_t start, size_t len) {
|
||||
LOG_USE_MODULE(MemoryManager);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
}
|
||||
|
||||
void registerMapping(uint64_t vaddr, MappingType type) {
|
||||
auto impl = getData();
|
||||
|
||||
boost::unique_lock lock(impl->mutex_);
|
||||
|
||||
impl->mappings_.emplace(std::make_pair(vaddr, type));
|
||||
}
|
||||
|
||||
MappingType unregisterMapping(uint64_t vaddr) {
|
||||
auto impl = getData();
|
||||
|
||||
boost::unique_lock lock(impl->mutex_);
|
||||
|
||||
if (auto it = impl->mappings_.find((uint64_t)vaddr); it != impl->mappings_.end()) {
|
||||
auto type = it->second;
|
||||
impl->mappings_.erase(it);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return MappingType::None;
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
enum class GpuMemoryMode { NoAccess, Read, Write, ReadWrite };
|
||||
|
||||
enum class MappingType { None, File, Flexible, Fixed };
|
||||
|
||||
class IPhysicalMemory {
|
||||
CLASS_NO_COPY(IPhysicalMemory);
|
||||
|
||||
protected:
|
||||
IPhysicalMemory() = default;
|
||||
uint64_t m_availableSize = 5000000000llu; // todo get from system memory
|
||||
size_t m_allocSize = 0;
|
||||
|
||||
public:
|
||||
virtual ~IPhysicalMemory() = default;
|
||||
|
||||
void getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) {
|
||||
*startOut = size() - m_availableSize;
|
||||
*sizeOut = m_availableSize;
|
||||
}
|
||||
|
||||
virtual uint64_t alloc(uint64_t vaddr, size_t len, int memoryType) = 0;
|
||||
|
||||
virtual bool reserve(uint64_t start, size_t len, size_t alignment, uint64_t* outAddr, int memoryType) = 0;
|
||||
virtual uintptr_t commit(uint64_t base, uint64_t offset, size_t len, size_t alignment, int prot) = 0;
|
||||
|
||||
virtual bool Map(uint64_t vaddr, uint64_t physAddr, size_t len, int prot, bool allocFixed, size_t alignment, uint64_t* outAddr) = 0;
|
||||
virtual bool Release(uint64_t start, size_t len, uint64_t* vaddr, uint64_t* size) = 0;
|
||||
virtual bool Unmap(uint64_t vaddr, uint64_t size) = 0;
|
||||
virtual void deinit() = 0;
|
||||
|
||||
uint64_t const size() const { return m_allocSize; } // use system ram
|
||||
};
|
||||
|
||||
class IFlexibleMemory {
|
||||
CLASS_NO_COPY(IFlexibleMemory);
|
||||
|
||||
uint64_t m_configuresSize = 448 * 1024 * 1024;
|
||||
|
||||
protected:
|
||||
IFlexibleMemory() = default;
|
||||
|
||||
public:
|
||||
virtual ~IFlexibleMemory() = default;
|
||||
|
||||
void setConfiguredSize(uint64_t size) { m_configuresSize = size; }
|
||||
|
||||
uint64_t size() const { return m_configuresSize; }
|
||||
|
||||
virtual uint64_t available() = 0;
|
||||
virtual uint64_t alloc(uint64_t vaddr, size_t len, int prot) = 0;
|
||||
virtual bool destroy(uint64_t vaddr, uint64_t size) = 0;
|
||||
|
||||
virtual void release(uint64_t start, size_t len) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief registers mapping (for mmap, mumap)
|
||||
*
|
||||
* @param vaddr
|
||||
* @param type != None
|
||||
* @return __APICALL
|
||||
*/
|
||||
__APICALL void registerMapping(uint64_t vaddr, MappingType type);
|
||||
|
||||
/**
|
||||
* @brief Unregisters mapping and returns the type of the mappin
|
||||
*
|
||||
* @param vaddr
|
||||
* @return None: Mapping didn't exist
|
||||
*/
|
||||
__APICALL MappingType unregisterMapping(uint64_t vaddr);
|
||||
|
||||
__APICALL IPhysicalMemory& accessPysicalMemory();
|
||||
__APICALL IFlexibleMemory& accessFlexibleMemory();
|
||||
#undef __APICALL
|
@ -1,15 +0,0 @@
|
||||
add_library(fileManager OBJECT
|
||||
fileManager.cpp
|
||||
types/type_in.cpp
|
||||
types/type_out.cpp
|
||||
types/type_zero.cpp
|
||||
types/type_null.cpp
|
||||
types/type_file.cpp
|
||||
types/type_random.cpp
|
||||
)
|
||||
|
||||
add_dependencies(fileManager third_party psOff_utility)
|
||||
|
||||
target_include_directories(fileManager PRIVATE
|
||||
${PRJ_SRC_DIR}/third_party/magic_enum/include
|
||||
)
|
@ -1,272 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "fileManager.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "logging.h"
|
||||
#include "magic_enum/magic_enum.hpp"
|
||||
#include "types/type_in.h"
|
||||
#include "types/type_null.h"
|
||||
#include "types/type_out.h"
|
||||
#include "types/type_zero.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
LOG_DEFINE_MODULE(FileManager);
|
||||
|
||||
struct UniData {
|
||||
CLASS_NO_COPY(UniData);
|
||||
|
||||
enum class Type { File, Dir };
|
||||
Type const m_type;
|
||||
std::filesystem::path const m_path;
|
||||
|
||||
UniData(Type type, std::filesystem::path const& path): m_type(type), m_path(path) {}
|
||||
|
||||
virtual ~UniData() = default;
|
||||
};
|
||||
|
||||
struct FileData: public UniData {
|
||||
std::unique_ptr<IFile> const m_file;
|
||||
|
||||
FileData(std::unique_ptr<IFile>&& file, std::filesystem::path const& path): UniData(UniData::Type::File, path), m_file(std::move(file)) {}
|
||||
|
||||
virtual ~FileData() { m_file->sync(); }
|
||||
};
|
||||
|
||||
struct DirData: public UniData {
|
||||
std::unique_ptr<std::filesystem::directory_iterator> const m_file;
|
||||
uint32_t count = 0;
|
||||
|
||||
DirData(std::unique_ptr<std::filesystem::directory_iterator>&& dirIt, std::filesystem::path const& path)
|
||||
: UniData(UniData::Type::Dir, path), m_file(std::move(dirIt)) {}
|
||||
|
||||
virtual ~DirData() = default;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
int insertItem(std::vector<std::unique_ptr<UniData>>& container, UniData* item) {
|
||||
int index = -1;
|
||||
for (size_t n = 0; n < container.size(); ++n) {
|
||||
if (!container[n]) {
|
||||
container[n] = std::unique_ptr<UniData>(item);
|
||||
index = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
index = container.size();
|
||||
container.push_back(std::unique_ptr<UniData>(item));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class FileManager: public IFileManager {
|
||||
std::vector<std::unique_ptr<UniData>> m_openFiles;
|
||||
std::mutex m_mutext_int;
|
||||
|
||||
/// First: mountpoint Second: RootDir
|
||||
std::unordered_map<MountType, std::unordered_map<std::string, std::filesystem::path>> m_mountPointList;
|
||||
std::filesystem::path m_dirGameFiles;
|
||||
std::unordered_map<std::string, std::filesystem::path> m_mappedPaths;
|
||||
|
||||
bool m_updateSearch = false;
|
||||
|
||||
public:
|
||||
FileManager() { init(); };
|
||||
|
||||
virtual ~FileManager() = default;
|
||||
|
||||
void init() {
|
||||
// Order of the first three files is important, do not change it!
|
||||
assert(addFile(createType_in(), "/dev/stdin") == 0);
|
||||
assert(addFile(createType_out(SCE_TYPEOUT_ERROR), "/dev/stdout") == 1);
|
||||
assert(addFile(createType_out(SCE_TYPEOUT_DEBUG), "/dev/stderr") == 2);
|
||||
addFile(createType_zero(), "/dev/zero");
|
||||
addFile(createType_null(), "/dev/null");
|
||||
}
|
||||
|
||||
void addMountPoint(std::string_view mountPoint, std::filesystem::path const& root, MountType type) final {
|
||||
LOG_USE_MODULE(FileManager);
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
m_mountPointList[type][std::string(mountPoint)] = root;
|
||||
|
||||
// Create root dir
|
||||
std::filesystem::create_directories(root);
|
||||
LOG_INFO(L"+ MountPoint: %S->%s", mountPoint.data(), root.c_str());
|
||||
}
|
||||
|
||||
std::filesystem::path getMountPoint(MountType type, std::string mountPoint) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
if (auto it = m_mountPointList[type].find(mountPoint); it != m_mountPointList[type].end()) return it->second;
|
||||
return {};
|
||||
}
|
||||
|
||||
void clearMountPoint(MountType type, std::string mountPoint) final {
|
||||
LOG_USE_MODULE(FileManager);
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
LOG_INFO(L"- MountPoint for %S", magic_enum::enum_name(type).data());
|
||||
m_mountPointList[type].erase(mountPoint);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> getMappedPath(std::string_view path) final {
|
||||
LOG_USE_MODULE(FileManager);
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
|
||||
// Check Cache
|
||||
{
|
||||
auto it = m_mappedPaths.find(path.data());
|
||||
if (it != m_mappedPaths.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
//-
|
||||
|
||||
// Special case: Mounted Gamefiles
|
||||
if (path[0] != '/') {
|
||||
auto const mapped = m_dirGameFiles / path;
|
||||
LOG_INFO(L"Gamefiles mapped:%s", mapped.c_str());
|
||||
return mapped;
|
||||
}
|
||||
// -
|
||||
|
||||
uint8_t offset = (path[0] == '/' && path[1] == '/') ? 1 : 0; // Path can start with //app0 ?
|
||||
|
||||
for (auto const& mountType: m_mountPointList) {
|
||||
for (auto const& item: mountType.second) {
|
||||
auto const& mountPoint = item.first;
|
||||
auto const& rootDir = item.second;
|
||||
|
||||
if (path.size() >= mountPoint.size() && std::mismatch(mountPoint.begin(), mountPoint.end(), path.begin() + offset).first == mountPoint.end()) {
|
||||
if (path[mountPoint.size() + offset] == '/') ++offset; // path.substr() should return relative path
|
||||
|
||||
if (mountType.first == MountType::App && m_updateSearch) {
|
||||
auto const& updateDir = m_mountPointList[MountType::Update].begin()->second;
|
||||
auto const mapped = updateDir / path.substr(mountPoint.size() + offset);
|
||||
if (std::filesystem::exists(mapped)) {
|
||||
LOG_DEBUG(L"mapped: %s root:%s source:%S", mapped.c_str(), updateDir.c_str(), path.data());
|
||||
m_mappedPaths[path.data()] = mapped;
|
||||
return mapped;
|
||||
}
|
||||
}
|
||||
|
||||
auto const mapped = rootDir / path.substr(mountPoint.size() + offset);
|
||||
LOG_DEBUG(L"mapped: %s root:%s source:%S", mapped.c_str(), rootDir.c_str(), path.data());
|
||||
m_mappedPaths[path.data()] = mapped;
|
||||
return mapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_WARN(L"Unknown map:%S", path.data());
|
||||
return {};
|
||||
}
|
||||
|
||||
void setGameFilesDir(std::filesystem::path const& path) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
m_dirGameFiles = path;
|
||||
}
|
||||
|
||||
std::filesystem::path const& getGameFilesDir() const final { return m_dirGameFiles; }
|
||||
|
||||
int addFile(std::unique_ptr<IFile>&& file, std::filesystem::path const& path) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
|
||||
return insertItem(m_openFiles, std::make_unique<FileData>(std::move(file), path).release());
|
||||
}
|
||||
|
||||
int addDirIterator(std::unique_ptr<std::filesystem::directory_iterator>&& it, std::filesystem::path const& path) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
|
||||
return insertItem(m_openFiles, std::make_unique<DirData>(std::move(it), path).release());
|
||||
}
|
||||
|
||||
void remove(int handle) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
m_openFiles[handle].reset();
|
||||
}
|
||||
|
||||
IFile* accessFile(int handle) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
if (handle < m_openFiles.size() && m_openFiles[handle] && m_openFiles[handle]->m_type == UniData::Type::File) {
|
||||
return static_cast<FileData*>(m_openFiles[handle].get())->m_file.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::filesystem::path getPath(int handle) final {
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
if (handle < m_openFiles.size() && m_openFiles[handle]) {
|
||||
return m_openFiles[handle]->m_path;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
int getDents(int handle, char* buf, int nbytes, int64_t* basep) final {
|
||||
LOG_USE_MODULE(FileManager);
|
||||
|
||||
std::unique_lock const lock(m_mutext_int);
|
||||
|
||||
auto& oFile = m_openFiles[handle];
|
||||
if (oFile->m_type != UniData::Type::Dir) return -1;
|
||||
|
||||
auto dir = static_cast<DirData*>(oFile.get());
|
||||
auto endDir = std::filesystem::directory_iterator();
|
||||
|
||||
if ((*dir->m_file) == endDir) return 0;
|
||||
|
||||
struct DataStruct {
|
||||
uint32_t fileno;
|
||||
uint16_t reclen;
|
||||
uint8_t type;
|
||||
uint8_t namlen;
|
||||
char name[256];
|
||||
};
|
||||
|
||||
auto count = dir->count;
|
||||
int n = 0;
|
||||
|
||||
while (n < nbytes - sizeof(DataStruct)) {
|
||||
auto item = (DataStruct*)(buf + n);
|
||||
|
||||
auto const filename = (*dir->m_file)->path().filename().string();
|
||||
if (sizeof(DataStruct) + std::min(filename.size(), 255llu) >= nbytes) break;
|
||||
|
||||
item->fileno = 0;
|
||||
item->type = ((*dir->m_file)->is_regular_file() ? 8 : 4);
|
||||
item->namlen = filename.copy(item->name, 255);
|
||||
item->name[item->namlen] = '\0';
|
||||
|
||||
n += sizeof(DataStruct);
|
||||
item->reclen = sizeof(DataStruct);
|
||||
|
||||
LOG_DEBUG(L"KernelGetdirentries[%d]: %S %u offset:%u count:%u", handle, item->name, item->type, item->reclen, count);
|
||||
|
||||
std::error_code err;
|
||||
(*dir->m_file).increment(err);
|
||||
count = ++dir->count;
|
||||
|
||||
if ((*dir->m_file) == endDir || err) break;
|
||||
};
|
||||
|
||||
if (basep != nullptr) {
|
||||
*basep = count;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void enableUpdateSearch() final { m_updateSearch = true; }
|
||||
};
|
||||
|
||||
IFileManager& accessFileManager() {
|
||||
static FileManager inst;
|
||||
return inst;
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
#pragma once
|
||||
#include "ifile.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
enum class MountType { App, Update, Save, Temp, Data };
|
||||
constexpr int FILE_DESCRIPTOR_MIN = 3;
|
||||
|
||||
constexpr std::string_view MOUNT_POINT_TEMP = "temp";
|
||||
constexpr std::string_view MOUNT_POINT_DATA = "data";
|
||||
constexpr std::string_view MOUNT_POINT_APP = "/app0/";
|
||||
constexpr std::string_view MOUNT_POINT_UPDATE = "/app1/";
|
||||
constexpr std::string_view SAVE_DATA_POINT = "/savedata";
|
||||
|
||||
class IFileManager {
|
||||
CLASS_NO_COPY(IFileManager);
|
||||
CLASS_NO_MOVE(IFileManager);
|
||||
|
||||
protected:
|
||||
IFileManager() = default;
|
||||
virtual ~IFileManager() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the mapped path. (Resolves linux paths, mounts etc.)
|
||||
*
|
||||
* @param path
|
||||
* @return std::optional<std::filesystem::path> on success, the mapped path
|
||||
*/
|
||||
virtual std::optional<std::filesystem::path> getMappedPath(std::string_view path) = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds a fake mount point. getMappedPath() uses those to resolve to the correct path.
|
||||
* Just replaces both strings
|
||||
*
|
||||
* @param mountPoint e.g. /savedata
|
||||
* @param root actual path e.g. C:/savedir
|
||||
* @param type Used to group the mounts. normaly only one mount per type is used. (savedata uses multiple ...)
|
||||
*/
|
||||
virtual void addMountPoint(std::string_view mountPoint, std::filesystem::path const& root, MountType type) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the actual path for a mount point
|
||||
*
|
||||
* @param type
|
||||
* @param mountPoint e.g. /savedata
|
||||
* @return std::filesystem::path empty on error
|
||||
*/
|
||||
virtual std::filesystem::path getMountPoint(MountType type, std::string mountPoint) = 0;
|
||||
|
||||
/**
|
||||
* @brief Removes the mountpoint
|
||||
*
|
||||
* @param type
|
||||
* @param mountPoint
|
||||
*/
|
||||
virtual void clearMountPoint(MountType type, std::string mountPoint) = 0;
|
||||
|
||||
/**
|
||||
* @brief Sets the path where all games files should be located (savegames etc).
|
||||
* Used by main to setup the emulator
|
||||
* @param path
|
||||
*/
|
||||
virtual void setGameFilesDir(std::filesystem::path const& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the Game Files Dir
|
||||
*
|
||||
* @return std::filesystem::path const&
|
||||
*/
|
||||
virtual std::filesystem::path const& getGameFilesDir() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Add a open file.
|
||||
*
|
||||
* @param file
|
||||
* @param path the mapped path to the file/folder
|
||||
* @return int handle of the fstream. used by getFile() etc.
|
||||
*/
|
||||
virtual int addFile(std::unique_ptr<IFile>&& file, std::filesystem::path const& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief Add a directory_iterator
|
||||
*
|
||||
* @param it
|
||||
* @param path the mapped path to the folder
|
||||
* @return int handle to the directory_iterator
|
||||
*/
|
||||
virtual int addDirIterator(std::unique_ptr<std::filesystem::directory_iterator>&& it, std::filesystem::path const& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief remove the file/folder with the associated handle.
|
||||
* Only the internal mapping is removed!
|
||||
*
|
||||
* @param handle
|
||||
*/
|
||||
virtual void remove(int handle) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get access to the File
|
||||
*
|
||||
* @param handle
|
||||
* @return std::fstream*
|
||||
*/
|
||||
virtual IFile* accessFile(int handle) = 0;
|
||||
|
||||
virtual int getDents(int handle, char* buf, int nbytes, int64_t* basep) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the mapped path of a open file/folder
|
||||
*
|
||||
* @param handle
|
||||
* @return std::filesystem::path
|
||||
*/
|
||||
virtual std::filesystem::path getPath(int handle) = 0;
|
||||
|
||||
virtual void enableUpdateSearch() = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL IFileManager& accessFileManager();
|
||||
#undef __APICALL
|
@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum class FileType {
|
||||
File,
|
||||
Device,
|
||||
};
|
||||
|
||||
enum class SceWhence : int {
|
||||
beg,
|
||||
cur,
|
||||
end,
|
||||
};
|
||||
|
||||
class IFile {
|
||||
CLASS_NO_COPY(IFile);
|
||||
CLASS_NO_MOVE(IFile);
|
||||
FileType const m_type;
|
||||
|
||||
protected:
|
||||
int32_t m_errCode = 0;
|
||||
|
||||
IFile(FileType type): m_type(type) {}
|
||||
|
||||
public:
|
||||
virtual ~IFile() = default;
|
||||
|
||||
auto getType() const { return m_type; }
|
||||
|
||||
auto getErr() const { return m_errCode; }
|
||||
|
||||
void clearError() { m_errCode = 0; }
|
||||
|
||||
/**
|
||||
* @brief read n bytes from file to (uint8_t)buf[nbytes]
|
||||
*
|
||||
* @param buf
|
||||
* @param nbytes
|
||||
* @return size_t number of bytes read, 0:empty
|
||||
*/
|
||||
virtual size_t read(void* buf, size_t nbytes) = 0;
|
||||
|
||||
/**
|
||||
* @brief write n bytes from (uint8_t)buf[nbytes] to file
|
||||
*
|
||||
* @param buf
|
||||
* @param nbytes
|
||||
* @return size_t number of bytes written
|
||||
*/
|
||||
virtual size_t write(void* buf, size_t nbytes) = 0;
|
||||
|
||||
virtual void sync() = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the read/write position
|
||||
*
|
||||
* @param offset
|
||||
* @param whence
|
||||
* @return int64_t
|
||||
*/
|
||||
virtual int64_t lseek(int64_t offset, SceWhence whence) = 0;
|
||||
|
||||
virtual void* getNative() = 0;
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
## FileManager
|
||||
|
||||
Manages all Linux files (mounts etc.), converts paths to Host paths and is the owner of opened files.
|
||||
|
||||
<div align="center">
|
||||
|
||||
![](../../out/docs/uml/modules/fileManager.svg)
|
||||
</div>
|
@ -1,147 +0,0 @@
|
||||
#include "type_file.h"
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <windows.h>
|
||||
|
||||
LOG_DEFINE_MODULE(File);
|
||||
|
||||
namespace {
|
||||
typedef HANDLE Handle_t;
|
||||
|
||||
std::pair<Handle_t, int32_t> open(std::filesystem::path path, filesystem::SceOpen mode) {
|
||||
LOG_USE_MODULE(File);
|
||||
|
||||
DWORD shareMode = 0;
|
||||
DWORD access = 0;
|
||||
|
||||
// Set acces and shareMode;
|
||||
switch (mode.mode) {
|
||||
case filesystem::SceOpenMode::RDONLY: {
|
||||
access = GENERIC_READ;
|
||||
if (mode.shlock) shareMode = FILE_SHARE_READ;
|
||||
} break;
|
||||
case filesystem::SceOpenMode::WRONLY: {
|
||||
access = GENERIC_WRITE;
|
||||
if (mode.shlock) shareMode = FILE_SHARE_WRITE;
|
||||
} break;
|
||||
case filesystem::SceOpenMode::RDWR: {
|
||||
access = GENERIC_READ | GENERIC_WRITE;
|
||||
if (mode.shlock) shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
} break;
|
||||
}
|
||||
// -
|
||||
|
||||
DWORD flags = OPEN_EXISTING;
|
||||
if (mode.trunc) flags = CREATE_ALWAYS;
|
||||
// Create new if it doesn't exist
|
||||
else if (mode.create) {
|
||||
flags = CREATE_NEW;
|
||||
}
|
||||
|
||||
auto pathString = path.wstring();
|
||||
std::replace(pathString.begin(), pathString.end(), L'/', L'\\');
|
||||
HANDLE file = CreateFileW(pathString.c_str(), access, shareMode, 0, flags, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
auto const lastErr = GetLastError();
|
||||
LOG_WARN(L"open file: %s flags:0x%lx access:0x%lx shareMode:0x%lx mode:0x%llx(%u) error:0x%lx ", pathString.c_str(), flags, access, shareMode,
|
||||
*(uint32_t*)(&mode), mode.mode, lastErr);
|
||||
if (lastErr == 0x57) return {nullptr, (int)ErrCode::_EINVAL};
|
||||
if (lastErr == 0x20) return {nullptr, (int)ErrCode::_EALREADY};
|
||||
return {nullptr, Ok};
|
||||
}
|
||||
|
||||
LOG_DEBUG(L"[%lld] opened file: %s flags:0x%lx access:0x%lx shareMode:0x%lx mode:0x%lx(%u)", file, pathString.c_str(), flags, access, shareMode,
|
||||
*(uint32_t*)(&mode), mode.mode);
|
||||
return {file, Ok};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class TypeFile: public IFile {
|
||||
Handle_t m_file = INVALID_HANDLE_VALUE;
|
||||
|
||||
filesystem::SceOpen const m_mode;
|
||||
|
||||
bool m_isAppend = false;
|
||||
|
||||
public:
|
||||
TypeFile(std::filesystem::path path, filesystem::SceOpen mode): IFile(FileType::File), m_mode(mode) {
|
||||
auto [file, err] = open(path, mode);
|
||||
m_file = file;
|
||||
|
||||
m_errCode = err;
|
||||
|
||||
m_isAppend = mode.append;
|
||||
}
|
||||
|
||||
virtual ~TypeFile() {
|
||||
if (m_file != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(m_file);
|
||||
sync();
|
||||
}
|
||||
}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
void sync() final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
|
||||
virtual void* getNative() final { return m_file; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_file(std::filesystem::path path, filesystem::SceOpen mode) {
|
||||
return std::make_unique<TypeFile>(path, mode);
|
||||
}
|
||||
|
||||
size_t TypeFile::read(void* buf, size_t nbytes) {
|
||||
DWORD numRead = 0;
|
||||
if (!ReadFile(m_file, buf, nbytes, &numRead, nullptr)) {
|
||||
auto const lastError = GetLastError();
|
||||
if (lastError != ERROR_IO_PENDING) {
|
||||
LOG_USE_MODULE(File);
|
||||
LOG_WARN(L"Read error: 0x%lx", GetLastError());
|
||||
m_errCode = (int)ErrCode::_EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return numRead;
|
||||
}
|
||||
|
||||
size_t TypeFile::write(void* buf, size_t nbytes) {
|
||||
// The file is opened in append mode. Before each write(2),
|
||||
// the file offset is positioned at the end of the file
|
||||
if (m_isAppend) {
|
||||
lseek(0, SceWhence::end);
|
||||
}
|
||||
// -
|
||||
|
||||
DWORD numWrite = 0;
|
||||
if (!WriteFile(m_file, buf, nbytes, &numWrite, nullptr)) {
|
||||
LOG_USE_MODULE(File);
|
||||
LOG_WARN(L"Write error: 0x%lx", GetLastError());
|
||||
m_errCode = (int)ErrCode::_EIO;
|
||||
return 0;
|
||||
}
|
||||
return numWrite;
|
||||
}
|
||||
|
||||
void TypeFile::sync() {
|
||||
FlushFileBuffers(m_file);
|
||||
}
|
||||
|
||||
int64_t TypeFile::lseek(int64_t offset, SceWhence whence) {
|
||||
static int _whence[] = {
|
||||
FILE_BEGIN,
|
||||
FILE_CURRENT,
|
||||
FILE_END,
|
||||
};
|
||||
|
||||
LONG upperHalf = offset >> 32;
|
||||
DWORD lowerHalf = SetFilePointer(m_file, (uint32_t)offset, &upperHalf, _whence[(int)whence]);
|
||||
|
||||
return ((uint64_t)upperHalf << 32) | lowerHalf;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
#include "core/kernel/filesystem.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_file(std::filesystem::path path, filesystem::SceOpen mode);
|
@ -1,41 +0,0 @@
|
||||
#include "type_in.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
LOG_DEFINE_MODULE(filesystem);
|
||||
|
||||
class TypeIn: public IFile {
|
||||
public:
|
||||
TypeIn(): IFile(FileType::Device) {}
|
||||
|
||||
virtual ~TypeIn() {}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
void sync() final;
|
||||
|
||||
void* getNative() final { return nullptr; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_in() {
|
||||
return std::make_unique<TypeIn>();
|
||||
}
|
||||
|
||||
size_t TypeIn::read(void* buf, size_t nbytes) {
|
||||
if (nbytes == 0) return 0;
|
||||
printf("Emulator awaits your input: ");
|
||||
return std::fread(buf, 1, nbytes, stdin);
|
||||
}
|
||||
|
||||
size_t TypeIn::write(void* buf, size_t nbytes) {
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int64_t TypeIn::lseek(int64_t offset, SceWhence whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TypeIn::sync() {}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_in();
|
@ -1,36 +0,0 @@
|
||||
#include "type_null.h"
|
||||
|
||||
#include "modules_include/common.h"
|
||||
|
||||
class TypeNull: public IFile {
|
||||
public:
|
||||
TypeNull(): IFile(FileType::Device) {}
|
||||
|
||||
virtual ~TypeNull() {}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
void sync() final;
|
||||
|
||||
void* getNative() final { return nullptr; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_null() {
|
||||
return std::make_unique<TypeNull>();
|
||||
}
|
||||
|
||||
size_t TypeNull::read(void* buf, size_t nbytes) {
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
size_t TypeNull::write(void* buf, size_t nbytes) {
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int64_t TypeNull::lseek(int64_t offset, SceWhence whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TypeNull::sync() {}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_null();
|
@ -1,58 +0,0 @@
|
||||
#include "type_out.h"
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
LOG_DEFINE_MODULE(filesystem);
|
||||
|
||||
class TypeOut: public IFile {
|
||||
const SceFileOutChannel m_channel = SCE_TYPEOUT_ERROR;
|
||||
|
||||
public:
|
||||
TypeOut(SceFileOutChannel ch): IFile(FileType::Device), m_channel(ch) {}
|
||||
|
||||
virtual ~TypeOut() {}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
void sync() final;
|
||||
|
||||
void* getNative() final { return nullptr; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_out(SceFileOutChannel ch) {
|
||||
return std::make_unique<TypeOut>(ch);
|
||||
}
|
||||
|
||||
size_t TypeOut::read(void* buf, size_t nbytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t TypeOut::write(void* buf, size_t nbytes) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
std::string str((const char*)buf, nbytes);
|
||||
while (str.back() == '\n')
|
||||
str.pop_back();
|
||||
|
||||
switch (m_channel) {
|
||||
case SCE_TYPEOUT_ERROR: {
|
||||
printf("Console:%s\n", str.c_str());
|
||||
LOG_DEBUG(L"%S", str.c_str());
|
||||
|
||||
} break;
|
||||
|
||||
case SCE_TYPEOUT_DEBUG: LOG_DEBUG(L"%S", str.c_str()); break;
|
||||
|
||||
default: LOG_CRIT(L"Unknown channel: %d", (uint32_t)m_channel); break;
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int64_t TypeOut::lseek(int64_t offset, SceWhence whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TypeOut::sync() {}
|
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
enum SceFileOutChannel {
|
||||
SCE_TYPEOUT_ERROR,
|
||||
SCE_TYPEOUT_DEBUG,
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_out(SceFileOutChannel ch);
|
@ -1,40 +0,0 @@
|
||||
#include "type_random.h"
|
||||
|
||||
class TypeRandom: public IFile {
|
||||
public:
|
||||
TypeRandom(): IFile(FileType::Device) {}
|
||||
|
||||
virtual ~TypeRandom() {}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
void sync() final;
|
||||
|
||||
void* getNative() final { return nullptr; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_random() {
|
||||
std::srand(std::time(nullptr));
|
||||
return std::make_unique<TypeRandom>();
|
||||
}
|
||||
|
||||
size_t TypeRandom::read(void* buf, size_t nbytes) {
|
||||
auto cbuf = static_cast<char*>(buf);
|
||||
|
||||
for (size_t i = 0; i < nbytes; i++)
|
||||
cbuf[i] = std::rand() & 0xFF;
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
size_t TypeRandom::write(void* buf, size_t nbytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t TypeRandom::lseek(int64_t offset, SceWhence whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TypeRandom::sync() {}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_random();
|
@ -1,38 +0,0 @@
|
||||
#include "type_zero.h"
|
||||
|
||||
class TypeZero: public IFile {
|
||||
public:
|
||||
TypeZero(): IFile(FileType::Device) {}
|
||||
|
||||
virtual ~TypeZero() {}
|
||||
|
||||
// ### Interface
|
||||
size_t read(void* buf, size_t nbytes) final;
|
||||
size_t write(void* buf, size_t nbytes) final;
|
||||
int64_t lseek(int64_t offset, SceWhence whence) final;
|
||||
void sync() final;
|
||||
|
||||
void* getNative() final { return nullptr; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IFile> createType_zero() {
|
||||
return std::make_unique<TypeZero>();
|
||||
}
|
||||
|
||||
size_t TypeZero::read(void* buf, size_t nbytes) {
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
((char*)buf)[i] = '\0';
|
||||
}
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
size_t TypeZero::write(void* buf, size_t nbytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t TypeZero::lseek(int64_t offset, SceWhence whence) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TypeZero::sync() {}
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_zero();
|
@ -1,11 +0,0 @@
|
||||
add_library(imports OBJECT
|
||||
imports.cpp
|
||||
exit.cpp
|
||||
)
|
||||
|
||||
add_dependencies(imports third_party psOff_utility initParams)
|
||||
target_include_directories(imports PRIVATE
|
||||
${Vulkan_INCLUDE_DIRS}
|
||||
${PRJ_SRC_DIR}/third_party/optick/src
|
||||
${PRJ_SRC_DIR}
|
||||
)
|
@ -1,14 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "imports_exit.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "core/dmem/dmem.h"
|
||||
|
||||
/**
|
||||
* @brief Exit handling of the core lib
|
||||
*
|
||||
*/
|
||||
|
||||
void core_exit() {
|
||||
// signal handler context
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
#pragma once
|
||||
#include "core/kernel/eventqueue_types.h"
|
||||
#include "utility/utility.h"
|
||||
#include "vulkan/vulkan_core.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
enum class GRAPHICS_EVENTS : int {
|
||||
Compute0RelMem = 0,
|
||||
Compute1RelMem,
|
||||
Compute2RelMem,
|
||||
Compute3RelMem,
|
||||
Compute4RelMem,
|
||||
Compute5RelMem,
|
||||
Compute6RelMem,
|
||||
EOP = 0x40,
|
||||
};
|
||||
|
||||
namespace vulkan {
|
||||
struct DeviceInfo;
|
||||
|
||||
enum class QueueType : uint8_t;
|
||||
|
||||
struct QueueInfo;
|
||||
} // namespace vulkan
|
||||
|
||||
class IEventsGraphics {
|
||||
public:
|
||||
virtual void eventDoFlip(int handle, int index, int64_t flipArg, VkSemaphore waitSema, size_t waitValue) = 0;
|
||||
|
||||
virtual vulkan::DeviceInfo* getDeviceInfo() = 0;
|
||||
|
||||
virtual vulkan::QueueInfo* getQueue(vulkan::QueueType type) = 0;
|
||||
};
|
||||
|
||||
class IGraphics {
|
||||
CLASS_NO_COPY(IGraphics);
|
||||
CLASS_NO_MOVE(IGraphics);
|
||||
|
||||
protected:
|
||||
IGraphics() = default;
|
||||
|
||||
public:
|
||||
virtual ~IGraphics() = default;
|
||||
|
||||
/**
|
||||
* @brief Adds a GRAPHICS_EVENTS event
|
||||
*
|
||||
* @param event
|
||||
* @param eq
|
||||
* @return int result
|
||||
*/
|
||||
virtual int addEvent(Kernel::EventQueue::KernelEqueueEvent& event, Kernel::EventQueue::IKernelEqueue_t eq) = 0;
|
||||
|
||||
/**
|
||||
* @brief Removes a GRAPHICS_EVENTS event
|
||||
*
|
||||
* @param eq
|
||||
* @param ident
|
||||
*/
|
||||
virtual void removeEvent(Kernel::EventQueue::IKernelEqueue_t eq, int const ident) = 0;
|
||||
|
||||
/**
|
||||
* @brief Add commandbuffer for preparsing.
|
||||
* unregistered on submit
|
||||
*
|
||||
* @param cmdBufferClass
|
||||
*/
|
||||
virtual void registerCommandBuffer(void* cmdBufferClass) = 0;
|
||||
|
||||
/**
|
||||
* @brief submits the command buffer to the command processor
|
||||
*
|
||||
* @param count
|
||||
* @param drawBuffers
|
||||
* @param numDrawDw
|
||||
* @param constBuffers
|
||||
* @param numConstDw
|
||||
* @param handle
|
||||
* @param index
|
||||
* @param flipMode
|
||||
* @param flipArg
|
||||
* @param flip
|
||||
*/
|
||||
virtual void submitCmdBuffer(uint32_t count, uint32_t const* drawBuffers[], uint32_t const numDrawDw[], uint32_t const* constBuffers[],
|
||||
uint32_t const numConstDw[], int const handle, int const index, int const flipMode, int64_t const flipArg, bool flip) = 0;
|
||||
|
||||
/**
|
||||
* @brief Wait for all submits (and currently flips todo: check if needed)
|
||||
*
|
||||
*/
|
||||
virtual void waitSubmitDone() = 0;
|
||||
|
||||
/**
|
||||
* @brief Increase the counter in waitSubmitDone(). Adds a wait condition.
|
||||
*
|
||||
*/
|
||||
virtual void submited() = 0;
|
||||
|
||||
/**
|
||||
* @brief Notify waitSubmitDone(). Decreases the counter
|
||||
*
|
||||
*/
|
||||
virtual void submitDone() = 0;
|
||||
|
||||
/**
|
||||
* @brief Check if command processor is running (handling submits)
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool isRunning() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Pre deletion call
|
||||
*
|
||||
*/
|
||||
virtual void deinit() = 0;
|
||||
|
||||
/**
|
||||
* @brief Call before deinit, stops all incoming requests
|
||||
*
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/**
|
||||
* @brief Register a display buffer
|
||||
*
|
||||
* @param vaddr
|
||||
* @param extent
|
||||
* @param pitch
|
||||
* @param format
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool registerDisplayBuffer(uint64_t vaddr, VkExtent2D extent, uint32_t pitch, VkFormat format) = 0;
|
||||
|
||||
/**
|
||||
* @brief Notify a gpu visible memory range
|
||||
*
|
||||
* @return true: Memory has been allocated successfully
|
||||
*/
|
||||
virtual bool notify_allocHeap(uint64_t vaddr, uint64_t size, int memoryProtection) = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if the vaddr is gpu memory (prev notify_allocHeap)
|
||||
*
|
||||
* @param vaddr
|
||||
* @return true is gpu local memory
|
||||
* @return false
|
||||
*/
|
||||
virtual bool isGPULocal(uint64_t vaddr) = 0;
|
||||
|
||||
/**
|
||||
* @brief Records the copy commands for copying the displayBuffer to the dstImage
|
||||
*
|
||||
* @param vaddr
|
||||
* @param cmdBuffer
|
||||
* @param dstImage
|
||||
* @param dstExtent
|
||||
* @return true success
|
||||
*/
|
||||
virtual bool copyDisplayBuffer(uint64_t vaddr, VkCommandBuffer cmdBuffer, VkImage dstImage, VkExtent2D dstExtent) = 0;
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Pm4 {
|
||||
|
||||
#define PM4_GET(u, r, f) (((u) >> Pm4::r##_##f##_SHIFT) & Pm4::r##_##f##_MASK)
|
||||
|
||||
enum class Custom : uint32_t {
|
||||
R_VS,
|
||||
R_PS,
|
||||
R_DRAW_INDEX,
|
||||
R_DRAW_INDEX_AUTO,
|
||||
R_DRAW_INDEX_OFFSET,
|
||||
R_DRAW_RESET,
|
||||
R_WAIT_FLIP_DONE,
|
||||
R_CS,
|
||||
R_DISPATCH_DIRECT,
|
||||
R_DISPATCH_RESET,
|
||||
R_DISPATCH_WAIT_MEM,
|
||||
R_PUSH_MARKER,
|
||||
R_POP_MARKER,
|
||||
R_VS_EMBEDDED,
|
||||
R_PS_EMBEDDED,
|
||||
R_VS_UPDATE,
|
||||
R_PS_UPDATE,
|
||||
R_VGT_CONTROL,
|
||||
R_NUM_COMMANDS, // used for array size
|
||||
};
|
||||
|
||||
constexpr uint32_t create(uint16_t len, Custom baseIndex) {
|
||||
return 0x40000000u | (((static_cast<uint16_t>(len) - 2u) & 0x3fffu) << 16u) | (uint32_t)baseIndex;
|
||||
}
|
||||
|
||||
} // namespace Pm4
|
@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
struct SceKernelMemParam {
|
||||
uint64_t* sceKernelExtendedPageTable;
|
||||
uint64_t* sceKernelFlexibleMemorySize;
|
||||
uint8_t* sceKernelExtendedMemory1;
|
||||
uint64_t const* sceKernelExtendedGpuPageTable;
|
||||
uint8_t const* sceKernelExtendedMemory2;
|
||||
uint64_t const* sceKernelExtendedCpuPageTable;
|
||||
};
|
||||
|
||||
struct SceKernelFsParam {
|
||||
uint64_t size;
|
||||
uint64_t* sceKernelFsDupDent;
|
||||
void* sceWorkspaceUpdateMode;
|
||||
void* sceTraceAprNameMode;
|
||||
};
|
||||
|
||||
struct SceMallocReplace {
|
||||
uint64_t size = sizeof(SceMallocReplace);
|
||||
uint64_t unknown1 = 1;
|
||||
void* malloc_init = nullptr;
|
||||
void* malloc_finalize = nullptr;
|
||||
void* malloc = nullptr;
|
||||
void* free = nullptr;
|
||||
void* calloc = nullptr;
|
||||
void* realloc = nullptr;
|
||||
void* memalign = nullptr;
|
||||
void* reallocalign = nullptr;
|
||||
void* posix_memalign = nullptr;
|
||||
void* malloc_stats = nullptr;
|
||||
void* malloc_stats_fast = nullptr;
|
||||
void* malloc_usable_size = nullptr;
|
||||
};
|
||||
|
||||
struct SceLibcNewReplace {
|
||||
uint64_t Size = sizeof(SceLibcNewReplace);
|
||||
uint64_t Unknown1 = 0x2;
|
||||
void* user_new = nullptr;
|
||||
void* user_new_nothrow = nullptr;
|
||||
void* user_new_array = nullptr;
|
||||
void* user_new_array_nothrow = nullptr;
|
||||
void* user_delete = nullptr;
|
||||
void* user_delete_nothrow = nullptr;
|
||||
void* user_delete_array = nullptr;
|
||||
void* user_delete_array_nothrow = nullptr;
|
||||
// optional
|
||||
// void* user_delete_3= nullptr;
|
||||
// void* user_delete_4= nullptr;
|
||||
// void* user_delete_array_3= nullptr;
|
||||
// void* user_delete_array_4= nullptr;
|
||||
};
|
||||
|
||||
struct SceLibcParam0 {
|
||||
uint64_t Size;
|
||||
uint32_t entry_count;
|
||||
};
|
||||
|
||||
struct SceLibcParam1: SceLibcParam0 {
|
||||
uint32_t SceLibcInternalHeap; //(entry_count > 1)
|
||||
uint32_t* sceLibcHeapSize;
|
||||
uint32_t* sceLibcHeapDelayedAlloc;
|
||||
uint32_t* sceLibcHeapExtendedAlloc;
|
||||
uint32_t* sceLibcHeapInitialSize;
|
||||
SceMallocReplace* _sceLibcMallocReplace;
|
||||
SceLibcNewReplace* _sceLibcNewReplace;
|
||||
};
|
||||
|
||||
struct SceLibcParam2: SceLibcParam1 {
|
||||
uint64_t* sceLibcHeapHighAddressAlloc; //(entry_count > 2)
|
||||
uint32_t* Need_sceLibc;
|
||||
};
|
||||
|
||||
struct SceLibcParam3: SceLibcParam2 {
|
||||
uint64_t* sceLibcHeapMemoryLock; //(entry_count > 4)
|
||||
uint64_t* sceKernelInternalMemorySize;
|
||||
uint64_t* _sceLibcMallocReplaceForTls;
|
||||
};
|
||||
|
||||
struct SceLibcParam4: SceLibcParam3 {
|
||||
uint64_t* sceLibcMaxSystemSize; //(entry_count > 7)
|
||||
};
|
||||
|
||||
struct SceLibcParam5: SceLibcParam4 {
|
||||
uint64_t* sceLibcHeapDebugFlags; //(entry_count > 8)
|
||||
uint32_t* sceLibcStdThreadStackSize;
|
||||
void* Unknown3;
|
||||
uint32_t* sceKernelInternalMemoryDebugFlags;
|
||||
uint32_t* sceLibcWorkerThreadNum;
|
||||
uint32_t* sceLibcWorkerThreadPriority;
|
||||
uint32_t* sceLibcThreadUnnamedObjects;
|
||||
};
|
||||
|
||||
struct ProcParam {
|
||||
struct {
|
||||
uint64_t size; /// 0x50
|
||||
uint32_t magic; /// "orbi" (0x4942524F)
|
||||
uint32_t entryCount; /// >=1
|
||||
uint64_t sdkVersion; /// 0x4508101
|
||||
} header;
|
||||
|
||||
char const* sceProcessName;
|
||||
char const* sceUserMainThreadName;
|
||||
uint32_t const* sceUserMainThreadPriority;
|
||||
uint32_t const* sceUserMainThreadStackSize;
|
||||
SceLibcParam0* PSceLibcParam;
|
||||
SceKernelMemParam* _sceKernelMemParam;
|
||||
SceKernelFsParam* _sceKernelFsParam;
|
||||
uint32_t* sceProcessPreloadEnabled;
|
||||
uint64_t* unknown;
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
## Exports
|
||||
|
||||
Exports currently provided by the emulator itself.
|
@ -1,125 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdint.h>
|
||||
|
||||
using cxa_destructor_func_t = void (*)(void*);
|
||||
|
||||
struct CxaDestructor {
|
||||
cxa_destructor_func_t destructor_func;
|
||||
void* destructor_object;
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct alignas(32) EntryParams {
|
||||
int argc = 0;
|
||||
uint32_t pad = 0;
|
||||
const char* argv[3] = {0, 0, 0};
|
||||
};
|
||||
|
||||
struct ModulInfo {
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
uint64_t procParamAddr;
|
||||
};
|
||||
|
||||
struct SceKernelModuleSegmentInfo {
|
||||
uint64_t address;
|
||||
uint32_t size;
|
||||
int prot;
|
||||
};
|
||||
|
||||
struct SceKernelModuleInfo {
|
||||
uint64_t size = sizeof(SceKernelModuleInfo);
|
||||
char name[255];
|
||||
SceKernelModuleSegmentInfo segments[3];
|
||||
uint32_t segment_count;
|
||||
uint32_t ref_count;
|
||||
uint8_t fingerprint[20];
|
||||
};
|
||||
|
||||
struct SceKernelModuleInfoEx {
|
||||
uint64_t size = sizeof(SceKernelModuleInfoEx);
|
||||
char name[255];
|
||||
int id;
|
||||
uint32_t tls_index;
|
||||
void* tls_init_addr;
|
||||
uint32_t tls_init_size;
|
||||
uint32_t tls_size;
|
||||
uint32_t tls_offset;
|
||||
uint32_t tls_align;
|
||||
uint64_t init_proc_addr;
|
||||
uint64_t fini_proc_addr;
|
||||
uint64_t reserved1 = 0;
|
||||
uint64_t reserved2 = 0;
|
||||
void* eh_frame_hdr_addr;
|
||||
void* eh_frame_addr;
|
||||
uint32_t eh_frame_hdr_size;
|
||||
uint32_t eh_frame_size;
|
||||
|
||||
SceKernelModuleSegmentInfo segments[3];
|
||||
uint32_t segment_count;
|
||||
uint32_t ref_count;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
class IRuntimeExport {
|
||||
CLASS_NO_COPY(IRuntimeExport);
|
||||
CLASS_NO_MOVE(IRuntimeExport);
|
||||
|
||||
protected:
|
||||
IRuntimeExport() = default;
|
||||
|
||||
public:
|
||||
virtual ~IRuntimeExport() = default;
|
||||
|
||||
virtual int loadStartModule(std::filesystem::path const& path, size_t args, const void* argp, int* pRes) = 0;
|
||||
|
||||
virtual void initTLS(uint8_t* obj) = 0;
|
||||
|
||||
virtual uint64_t getTLSStaticBlockSize() const = 0;
|
||||
|
||||
virtual EntryParams const* getEntryParams() const = 0;
|
||||
|
||||
virtual void cxa_add_atexit(CxaDestructor&&, int moduleId) = 0;
|
||||
virtual void cxa_finalize(int moduleId) = 0;
|
||||
|
||||
virtual ModulInfo mainModuleInfo() const = 0;
|
||||
|
||||
virtual SceKernelModuleInfoEx const* getModuleInfoEx(uint64_t vaddr) const = 0;
|
||||
|
||||
virtual std::vector<int> getModules() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the symbols address
|
||||
*
|
||||
* @param symName nid
|
||||
* @param libName
|
||||
* @param modName
|
||||
* @return void* address
|
||||
*/
|
||||
virtual void* getSymbol(std::string_view symName, std::string_view libName, std::string_view modName) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Searches the module for the symbol
|
||||
*
|
||||
* @param module moduleId
|
||||
* @param symName
|
||||
* @param isNid
|
||||
* @return void*
|
||||
*/
|
||||
virtual void* getSymbol(int module, std::string_view symName, bool isNid) const = 0;
|
||||
|
||||
// ### THREAD LOCAL STORAGE
|
||||
virtual void* getTLSAddr(uint32_t index, uint64_t offset) = 0;
|
||||
virtual void setTLSKey(uint32_t key, const void* value) = 0;
|
||||
virtual void* getTLSKey(uint32_t key) = 0;
|
||||
virtual uint32_t createTLSKey(void* destructor) = 0;
|
||||
virtual void deleteTLSKey(uint32_t key) = 0;
|
||||
virtual void destroyTLSKeys(uint8_t* obj) = 0;
|
||||
// - ### TLS
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "imports.h"
|
||||
|
||||
#include "imports_func.h"
|
||||
#include "imports_runtime.h"
|
||||
|
||||
#undef __APICALL_EXTERN
|
||||
#include "core/imports/exports/runtimeExport.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
// Change if called from construct is needed
|
||||
static createGraphics_t g_createGraphics = nullptr;
|
||||
static runtimeExport_t g_runtimeExport = nullptr;
|
||||
|
||||
std::unique_ptr<IGraphics> createGraphics(IEventsGraphics& listener, VkDevice device, VkPhysicalDevice physDev, VkInstance instance) {
|
||||
assert(g_createGraphics != nullptr);
|
||||
return g_createGraphics(listener, device, physDev, instance);
|
||||
}
|
||||
|
||||
IRuntimeExport* accessRuntimeExport() {
|
||||
assert(g_runtimeExport != nullptr);
|
||||
return g_runtimeExport;
|
||||
}
|
||||
|
||||
void setCallback_createGraphics(createGraphics_t cb) {
|
||||
g_createGraphics = cb;
|
||||
}
|
||||
|
||||
void setCallback_accessRuntimeExport(runtimeExport_t cb) {
|
||||
g_runtimeExport = cb;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "exports/graphics.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
using createGraphics_t = std::unique_ptr<IGraphics> (*)(IEventsGraphics& listener, VkDevice device, VkPhysicalDevice physDev, VkInstance instance);
|
||||
|
||||
using runtimeExport_t = class IRuntimeExport*;
|
||||
|
||||
__APICALL void setCallback_createGraphics(createGraphics_t);
|
||||
|
||||
__APICALL void setCallback_accessRuntimeExport(runtimeExport_t);
|
||||
|
||||
#undef __APICALL
|
@ -1,16 +0,0 @@
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Called at exit
|
||||
*
|
||||
*/
|
||||
__APICALL void core_exit();
|
||||
|
||||
#undef __APICALL
|
@ -1,28 +0,0 @@
|
||||
#include "exports/graphics.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
// All communication strictly goes through the core library. (May change later)
|
||||
|
||||
/**
|
||||
* @brief Create a Graphics object.
|
||||
*
|
||||
* @param listener
|
||||
* @param device
|
||||
* @param physDev
|
||||
* @param instance
|
||||
* @return object
|
||||
*/
|
||||
__APICALL std::unique_ptr<IGraphics> createGraphics(IEventsGraphics& listener, VkDevice device, VkPhysicalDevice physDev, VkInstance instance);
|
||||
|
||||
#undef __APICALL
|
@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "exports/runtimeExport.h"
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL IRuntimeExport* accessRuntimeExport();
|
||||
|
||||
#undef __APICALL
|
@ -1,5 +0,0 @@
|
||||
add_library(initParams OBJECT
|
||||
initParams.cpp
|
||||
)
|
||||
|
||||
add_dependencies(initParams third_party boost)
|
@ -1,80 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "initParams.h"
|
||||
#undef __APICALL_EXTERN
|
||||
#include <boost/program_options.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
struct InitParamsPimpl {
|
||||
boost::program_options::variables_map m_vm;
|
||||
InitParamsPimpl() = default;
|
||||
};
|
||||
|
||||
InitParams::InitParams() {
|
||||
_pImpl = std::make_unique<InitParamsPimpl>().release();
|
||||
}
|
||||
|
||||
InitParams::~InitParams() {
|
||||
delete _pImpl;
|
||||
}
|
||||
|
||||
bool InitParams::init(int argc, char** argv) {
|
||||
namespace po = boost::program_options;
|
||||
|
||||
po::options_description desc("Allowed options");
|
||||
desc.add_options()
|
||||
// clang-format off
|
||||
("help,h", "Help")
|
||||
("d", "Wait for debugger")
|
||||
("vkValidation", "Enable vulkan validation layers")
|
||||
("vsync", po::value<bool>()->default_value(true), "Enable vulkan validation layers")
|
||||
("file", po::value<std::string>(), "fullpath to applications binary")
|
||||
("root", po::value<std::string>(), "Applications root")
|
||||
("update", po::value<std::string>(), "update files folder")
|
||||
// clang-format on
|
||||
;
|
||||
|
||||
auto& vm = _pImpl->m_vm;
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
po::notify(vm);
|
||||
|
||||
if (vm.count("help")) {
|
||||
std::cout << desc << '\n';
|
||||
return false;
|
||||
}
|
||||
if (!vm.count("file")) {
|
||||
std::cout << "--file missing\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
InitParams* accessInitParams() {
|
||||
static InitParams obj;
|
||||
return &obj;
|
||||
}
|
||||
|
||||
bool InitParams::isDebug() {
|
||||
return _pImpl->m_vm.count("d");
|
||||
}
|
||||
|
||||
std::string InitParams::getApplicationPath() {
|
||||
return _pImpl->m_vm.count("file") ? _pImpl->m_vm["file"].as<std::string>() : std::string();
|
||||
}
|
||||
|
||||
std::string InitParams::getApplicationRoot() {
|
||||
return _pImpl->m_vm.count("root") ? _pImpl->m_vm["root"].as<std::string>() : std::string();
|
||||
}
|
||||
|
||||
std::string InitParams::getUpdateRoot() {
|
||||
return _pImpl->m_vm.count("update") ? _pImpl->m_vm["update"].as<std::string>() : std::string();
|
||||
}
|
||||
|
||||
bool InitParams::enableValidation() {
|
||||
return _pImpl->m_vm.count("vkValidation");
|
||||
}
|
||||
|
||||
bool InitParams::useVSYNC() {
|
||||
return _pImpl->m_vm["vsync"].as<bool>();
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
struct InitParamsPimpl;
|
||||
|
||||
class InitParams {
|
||||
CLASS_NO_COPY(InitParams);
|
||||
CLASS_NO_MOVE(InitParams);
|
||||
|
||||
InitParamsPimpl* _pImpl = nullptr;
|
||||
|
||||
public:
|
||||
InitParams();
|
||||
|
||||
bool init(int argc, char** argv);
|
||||
|
||||
// Functions
|
||||
bool isDebug();
|
||||
|
||||
std::string getApplicationPath();
|
||||
std::string getApplicationRoot();
|
||||
std::string getUpdateRoot();
|
||||
|
||||
bool enableValidation();
|
||||
bool useVSYNC();
|
||||
~InitParams();
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL InitParams* accessInitParams();
|
||||
#undef __APICALL
|
@ -1,10 +0,0 @@
|
||||
add_library(kernel OBJECT
|
||||
eventqueue.cpp
|
||||
eventflag.cpp
|
||||
errors.cpp
|
||||
filesystem.cpp
|
||||
pthread.cpp
|
||||
semaphore.cpp
|
||||
)
|
||||
|
||||
add_dependencies(kernel third_party boost)
|
@ -1,13 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "errors.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
int* getError_pthread() {
|
||||
thread_local int g_errno = 0;
|
||||
return &g_errno;
|
||||
}
|
||||
|
||||
void setError_pthread(int error) {
|
||||
// todo tracing stacktrace?
|
||||
*getError_pthread() = error;
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#include "modules_include/common.h"
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL int* getError_pthread();
|
||||
__APICALL void setError_pthread(int);
|
||||
|
||||
/**
|
||||
* @brief Takes Kernel errors from gerErr(), converts and saves it
|
||||
*
|
||||
* @param result from gerErr()
|
||||
* @return int32_t
|
||||
*/
|
||||
static int32_t POSIX_CALL(int32_t result) {
|
||||
if (result >= 0) return result;
|
||||
|
||||
setError_pthread(result - (int32_t)0x80020000);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int32_t POSIX_SET(ErrCode error) {
|
||||
setError_pthread((int32_t)error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef __APICALL
|
@ -1,176 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "eventflag.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <memory>
|
||||
|
||||
LOG_DEFINE_MODULE(KernelEventFlag);
|
||||
|
||||
namespace Kernel::EventFlag {
|
||||
|
||||
class KernelEventFlag: public IKernelEventFlag {
|
||||
CLASS_NO_COPY(KernelEventFlag);
|
||||
|
||||
public:
|
||||
KernelEventFlag(std::string const& name, bool single, bool /*fifo*/, uint64_t bits): m_singleThread(single), m_bits(bits), m_name(name) {};
|
||||
virtual ~KernelEventFlag();
|
||||
|
||||
void set(uint64_t bits) final;
|
||||
void clear(uint64_t bits) final;
|
||||
void cancel(uint64_t bits, int* num_waiting_threads) final;
|
||||
|
||||
int wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) final;
|
||||
|
||||
int poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result) final {
|
||||
uint32_t micros = 0;
|
||||
return wait(bits, wait_mode, clear_mode, result, µs);
|
||||
}
|
||||
|
||||
private:
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
void setStatus(Status status) {
|
||||
m_status = status;
|
||||
if (status == Status::Set) m_cond_var_status.notify_all();
|
||||
}
|
||||
|
||||
boost::mutex m_mutex_cond;
|
||||
boost::condition_variable m_cond_var;
|
||||
boost::condition_variable m_cond_var_status;
|
||||
|
||||
Status m_status = Status::Set;
|
||||
|
||||
int m_waitingThreads = 0;
|
||||
bool m_singleThread = false;
|
||||
|
||||
std::string m_name;
|
||||
|
||||
uint64_t m_bits = 0;
|
||||
};
|
||||
|
||||
KernelEventFlag::~KernelEventFlag() {
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
|
||||
|
||||
m_status = Status::Deleted;
|
||||
m_cond_var.notify_all();
|
||||
|
||||
m_cond_var_status.wait(lock, [this] { return m_waitingThreads == 0; });
|
||||
}
|
||||
|
||||
void KernelEventFlag::set(uint64_t bits) {
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
|
||||
m_bits |= bits;
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
void KernelEventFlag::clear(uint64_t bits) {
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
|
||||
m_bits &= bits;
|
||||
}
|
||||
|
||||
void KernelEventFlag::cancel(uint64_t bits, int* num_waiting_threads) {
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
|
||||
if (num_waiting_threads != nullptr) {
|
||||
*num_waiting_threads = m_waitingThreads;
|
||||
}
|
||||
|
||||
setStatus(Status::Canceled);
|
||||
m_bits = bits;
|
||||
|
||||
m_cond_var.notify_all();
|
||||
m_cond_var_status.wait(lock, [this] { return m_waitingThreads == 0; });
|
||||
setStatus(Status::Set);
|
||||
}
|
||||
|
||||
int KernelEventFlag::wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) {
|
||||
LOG_USE_MODULE(KernelEventFlag);
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
if (m_singleThread && m_waitingThreads > 0) {
|
||||
return getErr(ErrCode::_EPERM);
|
||||
}
|
||||
|
||||
uint32_t micros = 0;
|
||||
bool infinitely = true;
|
||||
if (ptr_micros != nullptr) {
|
||||
micros = *ptr_micros;
|
||||
infinitely = false;
|
||||
}
|
||||
|
||||
auto const start = boost::chrono::system_clock::now();
|
||||
++m_waitingThreads;
|
||||
auto waitFunc = [this, wait_mode, bits] {
|
||||
return (m_status == Status::Canceled || m_status == Status::Deleted || (wait_mode == WaitMode::And && (m_bits & bits) == bits) ||
|
||||
(wait_mode == WaitMode::Or && (m_bits & bits) != 0));
|
||||
};
|
||||
if (infinitely) {
|
||||
m_cond_var.wait(lock, waitFunc);
|
||||
} else {
|
||||
if (!m_cond_var.wait_for(lock, boost::chrono::microseconds(micros), waitFunc)) {
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
*ptr_micros = 0;
|
||||
--m_waitingThreads;
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
--m_waitingThreads;
|
||||
m_cond_var_status.notify_all();
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
auto elapsed = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::system_clock::now() - start).count();
|
||||
if (result != nullptr) {
|
||||
*result = m_bits;
|
||||
}
|
||||
|
||||
if (ptr_micros != nullptr) {
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
// LOG_TRACE(L"wait %llu us -> %llu us", micros, elapsed);
|
||||
}
|
||||
|
||||
// Aborted
|
||||
if (m_status == Status::Canceled) {
|
||||
return getErr(ErrCode::_ECANCELED);
|
||||
} else if (m_status == Status::Deleted) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
// -
|
||||
|
||||
if (clear_mode == ClearMode::All) {
|
||||
m_bits = 0;
|
||||
} else if (clear_mode == ClearMode::Bits) {
|
||||
m_bits &= ~bits;
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
IKernelEventFlag_t createEventFlag(std::string const& name, bool single, bool fifo, uint64_t bits) {
|
||||
return std::make_unique<KernelEventFlag>(std::string(name), single, fifo, bits).release();
|
||||
}
|
||||
|
||||
int deleteEventFlag(IKernelEventFlag_t ef) {
|
||||
LOG_USE_MODULE(KernelEventFlag);
|
||||
if (ef == nullptr) {
|
||||
return getErr(ErrCode::_ESRCH);
|
||||
}
|
||||
LOG_INFO(L"Deleted ptr:0x%08llx", (uint64_t)ef);
|
||||
delete ef;
|
||||
return Ok;
|
||||
}
|
||||
} // namespace Kernel::EventFlag
|
@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
namespace Kernel::EventFlag {
|
||||
|
||||
enum class ClearMode { None, All, Bits };
|
||||
|
||||
enum class WaitMode { And, Or };
|
||||
|
||||
class IKernelEventFlag {
|
||||
CLASS_NO_COPY(IKernelEventFlag);
|
||||
CLASS_NO_MOVE(IKernelEventFlag);
|
||||
|
||||
protected:
|
||||
IKernelEventFlag() = default;
|
||||
|
||||
public:
|
||||
virtual ~IKernelEventFlag() = default;
|
||||
|
||||
virtual void set(uint64_t bits) = 0;
|
||||
virtual void clear(uint64_t bits) = 0;
|
||||
virtual void cancel(uint64_t bits, int* num_waiting_threads) = 0;
|
||||
|
||||
virtual int wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) = 0;
|
||||
|
||||
virtual int poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result) = 0;
|
||||
};
|
||||
|
||||
using IKernelEventFlag_t = IKernelEventFlag*;
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL IKernelEventFlag_t createEventFlag(std::string const& name, bool single, bool fifo, uint64_t bits);
|
||||
__APICALL int deleteEventFlag(IKernelEventFlag_t ef);
|
||||
|
||||
#undef __APICALL
|
||||
} // namespace Kernel::EventFlag
|
@ -1,296 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "eventqueue.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Kernel {
|
||||
namespace EventQueue {
|
||||
LOG_DEFINE_MODULE(EventQueue);
|
||||
|
||||
class KernelEqueue: public IKernelEqueue {
|
||||
public:
|
||||
KernelEqueue() = default;
|
||||
virtual ~KernelEqueue();
|
||||
|
||||
[[nodiscard]] std::string const& getName() const { return m_name; }
|
||||
|
||||
void setName(std::string&& name) { m_name.swap(name); }
|
||||
|
||||
int waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros);
|
||||
|
||||
int pollEvents(KernelEvent_t ev, int num) {
|
||||
std::unique_lock lock(m_mutex_cond);
|
||||
auto ret = getTriggeredEvents(ev, num);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) final;
|
||||
|
||||
int addUserEvent(int ident) final;
|
||||
int addUserEventEdge(int ident) final;
|
||||
int triggerUserEvent(uintptr_t ident, void* udata) final;
|
||||
|
||||
int addEvent(const KernelEqueueEvent& event) final;
|
||||
int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) final;
|
||||
int deleteEvent(uintptr_t ident, int16_t filter) final;
|
||||
|
||||
private:
|
||||
int getTriggeredEvents(KernelEvent_t ev, int num);
|
||||
|
||||
std::list<KernelEqueueEvent> m_events; // We dont ecpext many events ~5
|
||||
|
||||
boost::mutex m_mutex_cond;
|
||||
std::mutex m_mutex_int;
|
||||
boost::condition_variable m_cond_var;
|
||||
|
||||
std::string m_name;
|
||||
bool m_closed = false;
|
||||
};
|
||||
|
||||
KernelEqueue::~KernelEqueue() {
|
||||
std::unique_lock const lock(m_mutex_cond);
|
||||
/*
|
||||
for (auto& ev: m_events) {
|
||||
if (ev.filter.delete_event_func != nullptr) {
|
||||
ev.filter.delete_event_func(this, &ev);
|
||||
}
|
||||
}*/
|
||||
m_closed = true;
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
int KernelEqueue::waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
// LOG_TRACE(L"->waitForEvents: ident:0x%08llx num:%d", ev->ident, num);
|
||||
|
||||
boost::unique_lock lock(m_mutex_cond);
|
||||
|
||||
int ret = 0;
|
||||
if (micros != nullptr && *micros > 0) {
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
if (m_cond_var.wait_for(lock, boost::chrono::microseconds(*(decltype(micros))micros), [&] {
|
||||
ret = getTriggeredEvents(ev, num);
|
||||
return (ret > 0) | m_closed;
|
||||
}) == 0) {
|
||||
// LOG_TRACE(L"waitForEvents timeout");
|
||||
}
|
||||
} else {
|
||||
m_cond_var.wait(lock, [&] {
|
||||
ret = getTriggeredEvents(ev, num);
|
||||
return (ret > 0) | m_closed;
|
||||
});
|
||||
}
|
||||
// LOG_TRACE(L"<-waitForEvents: ident:0x%08llx ret:%d", ev->ident, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KernelEqueue::getTriggeredEvents(KernelEvent_t eventList, int num) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
|
||||
int countTriggered = 0;
|
||||
for (auto& ev: m_events) {
|
||||
if (ev.triggered) {
|
||||
eventList[countTriggered++] = ev.event;
|
||||
|
||||
if ((((uint32_t)ev.event.flags & (uint32_t)EventFlags::EV_CLEAR) != 0) && ev.filter.reset_func != nullptr) {
|
||||
ev.filter.reset_func(&ev);
|
||||
}
|
||||
LOG_TRACE(L"Event Triggered: ident:0x%08llx, filter:%d", (uint64_t)ev.event.ident, ev.event.filter);
|
||||
if (countTriggered >= num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return countTriggered;
|
||||
}
|
||||
|
||||
void event_trigger_func(Kernel::EventQueue::KernelEqueueEvent* event, void* trigger_data) {
|
||||
event->triggered = true;
|
||||
event->event.fflags++;
|
||||
event->event.udata = trigger_data;
|
||||
}
|
||||
|
||||
void event_reset_func(Kernel::EventQueue::KernelEqueueEvent* event) {
|
||||
event->triggered = false;
|
||||
event->event.fflags = 0;
|
||||
event->event.udata = 0;
|
||||
}
|
||||
|
||||
void event_delete_func(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEqueueEvent* event) {}
|
||||
|
||||
int createEqueue(IKernelEqueue_t* eq, const char* name) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
if (eq == nullptr || name == nullptr) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
auto item = std::make_unique<KernelEqueue>();
|
||||
item->setName(std::string(name));
|
||||
|
||||
LOG_INFO(L"+EventQueue (%S)", item->getName().c_str());
|
||||
*eq = item.release();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int deleteEqueue(IKernelEqueue_t eq) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
|
||||
if (eq == nullptr) {
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
LOG_INFO(L"-EventQueue (%S)", ((KernelEqueue*)eq)->getName().c_str());
|
||||
delete (eq);
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int KernelEqueue::wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) {
|
||||
if (ev == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
if (num < 1) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
if (time == nullptr) {
|
||||
*out = waitForEvents(ev, num, nullptr);
|
||||
if (*out == 0) {
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
} else {
|
||||
*out = waitForEvents(ev, num, time);
|
||||
if (*out == 0 && *time > 0) {
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int KernelEqueue::addUserEvent(int ident) {
|
||||
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
|
||||
.event =
|
||||
{
|
||||
.ident = ident,
|
||||
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
|
||||
.flags = Kernel::EventQueue::EventFlags::EV_NONE,
|
||||
.fflags = 0,
|
||||
.data = 0,
|
||||
.udata = 0,
|
||||
|
||||
},
|
||||
.filter {
|
||||
.data = nullptr,
|
||||
.trigger_func = event_trigger_func,
|
||||
.reset_func = event_reset_func,
|
||||
.delete_event_func = event_delete_func,
|
||||
}});
|
||||
}
|
||||
|
||||
int KernelEqueue::addUserEventEdge(int ident) {
|
||||
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
|
||||
.event =
|
||||
{
|
||||
.ident = ident,
|
||||
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
|
||||
.flags = Kernel::EventQueue::EventFlags::EV_CLEAR,
|
||||
.fflags = 0,
|
||||
.data = 0,
|
||||
.udata = 0,
|
||||
|
||||
},
|
||||
.filter {
|
||||
.data = nullptr,
|
||||
.trigger_func = event_trigger_func,
|
||||
.reset_func = event_reset_func,
|
||||
.delete_event_func = event_delete_func,
|
||||
}});
|
||||
}
|
||||
|
||||
int KernelEqueue::triggerUserEvent(uintptr_t ident, void* udata) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
LOG_TRACE(L"triggerUserEvent: ident:0x%08llx", (uint64_t)ident);
|
||||
|
||||
return triggerEvent(ident, Kernel::EventQueue::KERNEL_EVFILT_USER, udata);
|
||||
}
|
||||
|
||||
int KernelEqueue::addEvent(const KernelEqueueEvent& event) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
LOG_INFO(L"(%S) Add Event: ident:0x%08llx, filter:%d", m_name.c_str(), (uint64_t)event.event.ident, event.event.filter);
|
||||
|
||||
std::unique_lock const lock(m_mutex_cond);
|
||||
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(),
|
||||
[=](KernelEqueueEvent const& ev) { return ev.event.ident == event.event.ident && ev.event.filter == event.event.filter; });
|
||||
|
||||
if (it != m_events.end()) {
|
||||
*it = event;
|
||||
} else {
|
||||
m_events.push_back(event);
|
||||
}
|
||||
|
||||
if (event.triggered) {
|
||||
m_cond_var.notify_all();
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int KernelEqueue::triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
LOG_TRACE(L"triggerEvent: ident:0x%08llx, filter:%d", (uint64_t)ident, filter);
|
||||
|
||||
std::unique_lock const lock(m_mutex_cond);
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
|
||||
|
||||
if (it != m_events.end()) {
|
||||
if (it->filter.trigger_func != nullptr) {
|
||||
it->filter.trigger_func(&(*it), trigger_data);
|
||||
} else {
|
||||
it->triggered = true;
|
||||
}
|
||||
m_cond_var.notify_all();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
return getErr(ErrCode::_ENOENT);
|
||||
}
|
||||
|
||||
int KernelEqueue::deleteEvent(uintptr_t ident, int16_t filter) {
|
||||
LOG_USE_MODULE(EventQueue);
|
||||
LOG_INFO(L"deleteEvent: ident:0x%08llx, filter:%llu", (uint64_t)ident, filter);
|
||||
|
||||
std::unique_lock const lock(m_mutex_cond);
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
|
||||
|
||||
if (it != m_events.end()) {
|
||||
if (it->filter.delete_event_func != nullptr) {
|
||||
it->filter.delete_event_func(this, &(*it));
|
||||
}
|
||||
|
||||
m_events.erase(it);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
return getErr(ErrCode::_ENOENT);
|
||||
}
|
||||
} // namespace EventQueue
|
||||
} // namespace Kernel
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
#include "eventqueue_types.h"
|
||||
#include "modules_include/common.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
namespace Kernel::EventQueue {
|
||||
class IKernelEqueue {
|
||||
CLASS_NO_COPY(IKernelEqueue);
|
||||
CLASS_NO_MOVE(IKernelEqueue);
|
||||
|
||||
protected:
|
||||
IKernelEqueue() = default;
|
||||
|
||||
public:
|
||||
virtual ~IKernelEqueue() = default;
|
||||
|
||||
virtual int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* timo) = 0;
|
||||
|
||||
virtual int addUserEvent(int ident) = 0;
|
||||
virtual int addUserEventEdge(int ident) = 0;
|
||||
virtual int triggerUserEvent(uintptr_t ident, void* udata) = 0;
|
||||
|
||||
virtual int addEvent(const KernelEqueueEvent& event) = 0;
|
||||
virtual int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) = 0;
|
||||
virtual int deleteEvent(uintptr_t ident, int16_t filter) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL int createEqueue(IKernelEqueue_t* eq, const char* name);
|
||||
__APICALL int deleteEqueue(IKernelEqueue_t eq);
|
||||
|
||||
#undef __APICALL
|
||||
|
||||
} // namespace Kernel::EventQueue
|
@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Kernel::EventQueue {
|
||||
constexpr int16_t KERNEL_EVFILT_TIMER = -7;
|
||||
constexpr int16_t KERNEL_EVFILT_READ = -1;
|
||||
constexpr int16_t KERNEL_EVFILT_WRITE = -2;
|
||||
constexpr int16_t KERNEL_EVFILT_USER = -11;
|
||||
constexpr int16_t KERNEL_EVFILT_FILE = -4;
|
||||
constexpr int16_t KERNEL_EVFILT_GRAPHICS = -14;
|
||||
constexpr int16_t KERNEL_EVFILT_VIDEO_OUT = -13;
|
||||
constexpr int16_t KERNEL_EVFILT_HRTIMER = -15;
|
||||
|
||||
enum class EventFlags : uint32_t {
|
||||
EV_NONE = 0,
|
||||
EV_ONESHOT = 0x10, // only report one occurrence
|
||||
EV_CLEAR = 0x20, // clear event state after reporting
|
||||
EV_RECEIPT = 0x40, // force EV_ERROR on success, data=0
|
||||
EV_DISPATCH = 0x80, // disable event after reporting
|
||||
|
||||
EV_SYSFLAGS = 0xF000, // reserved by system
|
||||
EV_FLAG1 = 0x2000, // filter-specific flag
|
||||
};
|
||||
|
||||
struct KernelEvent {
|
||||
int ident = 0;
|
||||
int16_t filter = 0;
|
||||
EventFlags flags = EventFlags::EV_NONE;
|
||||
uint32_t fflags = 0;
|
||||
intptr_t data = 0;
|
||||
void* udata = nullptr;
|
||||
};
|
||||
struct KernelEqueueEvent;
|
||||
class IKernelEqueue;
|
||||
using IKernelEqueue_t = IKernelEqueue*;
|
||||
using KernelEvent_t = KernelEvent*;
|
||||
|
||||
using trigger_func_t = void (*)(KernelEqueueEvent* event, void* trigger_data);
|
||||
using reset_func_t = void (*)(KernelEqueueEvent* event);
|
||||
using delete_func_t = void (*)(IKernelEqueue_t eq, KernelEqueueEvent* event);
|
||||
|
||||
struct KernelFilter {
|
||||
void* data = nullptr;
|
||||
trigger_func_t trigger_func = nullptr;
|
||||
reset_func_t reset_func = nullptr;
|
||||
delete_func_t delete_event_func = nullptr;
|
||||
};
|
||||
|
||||
struct KernelEqueueEvent {
|
||||
bool triggered = false;
|
||||
KernelEvent event;
|
||||
KernelFilter filter;
|
||||
};
|
||||
|
||||
} // namespace Kernel::EventQueue
|
@ -1,689 +0,0 @@
|
||||
#include "filesystem.h"
|
||||
|
||||
#include "core/dmem/dmem.h"
|
||||
#include "core/fileManager/fileManager.h"
|
||||
#include "core/fileManager/types/type_file.h"
|
||||
#include "core/fileManager/types/type_random.h"
|
||||
#include "core/memory/memory.h"
|
||||
#include "logging.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <unordered_map>
|
||||
#include <windows.h>
|
||||
|
||||
LOG_DEFINE_MODULE(filesystem);
|
||||
|
||||
namespace {
|
||||
std::pair<uint32_t, uint32_t> convProtection(int prot) {
|
||||
switch (prot & 0xf) {
|
||||
case 0: return {PAGE_NOACCESS, 0};
|
||||
case 1: return {PAGE_READONLY, FILE_MAP_READ};
|
||||
case 2:
|
||||
case 3: return {PAGE_READWRITE, FILE_MAP_ALL_ACCESS};
|
||||
case 4: return {PAGE_EXECUTE, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE};
|
||||
case 5: return {PAGE_EXECUTE_READ, FILE_MAP_EXECUTE | FILE_MAP_READ};
|
||||
case 6:
|
||||
case 7: return {PAGE_EXECUTE_READWRITE, FILE_MAP_ALL_ACCESS};
|
||||
}
|
||||
|
||||
return {PAGE_NOACCESS, 0};
|
||||
}
|
||||
|
||||
std::unique_ptr<IFile> createType_dev(std::filesystem::path path, std::ios_base::openmode mode) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
// todo: /dev/rng? No ioctl for now
|
||||
if (path == "/dev/urandom" || path == "/dev/urandom") {
|
||||
return createType_random();
|
||||
} else { // todo: other devices
|
||||
LOG_CRIT(L"%S: unknown device!", path.c_str());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int open_dev(const char* path, filesystem::SceOpen flags, filesystem::SceKernelMode kernelMode) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
std::ios_base::openmode mode = std::ios::binary;
|
||||
|
||||
switch (flags.mode) {
|
||||
case filesystem::SceOpenMode::RDONLY: mode |= std::ios::in; break;
|
||||
case filesystem::SceOpenMode::WRONLY: mode |= std::ios::out; break;
|
||||
case filesystem::SceOpenMode::RDWR: mode |= std::ios::out | std::ios::in; break;
|
||||
}
|
||||
|
||||
auto file = createType_dev(path, mode);
|
||||
int const handle = accessFileManager().addFile(std::move(file), path);
|
||||
LOG_INFO(L"OpenFile[%d]: %s mode:0x%lx(0x%lx)", handle, path, mode, kernelMode);
|
||||
return handle;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace filesystem {
|
||||
int mmap(void* addr, size_t len, int prot, SceMap flags, int fd, int64_t offset, void** res) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
// Mapping cases
|
||||
if (flags.mode == SceMapMode::FIXED) {
|
||||
LOG_ERR(L"todo: Mmap fixed 0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld", addr, len, prot, flags, fd, offset);
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
if (flags.mode == SceMapMode::VOID_) {
|
||||
// reserve memory
|
||||
LOG_ERR(L"todo: Mmap void 0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld", addr, len, prot, flags, fd, offset);
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
if (flags.type == SceMapType::ANON) {
|
||||
*res = (void*)accessFlexibleMemory().alloc((uint64_t)addr, len, prot); // registers mapping (flexible)
|
||||
|
||||
LOG_DEBUG(L"Mmap anon addr:0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld -> out:0x%08llx", addr, len, prot, flags, fd, offset, *res);
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
// -
|
||||
|
||||
// Do file mapping
|
||||
if (fd < FILE_DESCRIPTOR_MIN) {
|
||||
LOG_DEBUG(L"Mmap error addr:0x%08llx len:0x%08llx prot:%d flags:%08llx fd:%d offset:%lld -> out:0x%08llx", addr, len, prot, flags, fd, offset, *res);
|
||||
return getErr(ErrCode::_EPERM);
|
||||
}
|
||||
|
||||
auto const [fileProt, viewProt] = convProtection(prot);
|
||||
|
||||
auto file = accessFileManager().accessFile(fd);
|
||||
|
||||
if (file->getNative() == nullptr) {
|
||||
LOG_ERR(L"Mmap fd:%d no nativeHandle", fd);
|
||||
return getErr(ErrCode::_EMFILE);
|
||||
}
|
||||
|
||||
uint64_t mappingLength = (fileProt == PAGE_READONLY) || (fileProt == PAGE_EXECUTE_READ) ? 0 : len;
|
||||
auto lenSplit = std::bit_cast<std::array<uint32_t, 2>>(mappingLength);
|
||||
|
||||
HANDLE hFileMapping = CreateFileMapping(file->getNative(),
|
||||
NULL, // default security
|
||||
fileProt,
|
||||
lenSplit[1], // maximum object size (high-order DWORD)
|
||||
lenSplit[0], // maximum object size (low-order DWORD)
|
||||
NULL); // name of mapping object
|
||||
|
||||
*res = nullptr;
|
||||
|
||||
if (hFileMapping == NULL) {
|
||||
LOG_ERR(L"Mmap CreateFileMapping == NULL for| 0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld err:%04x", addr, mappingLength, prot, flags, fd,
|
||||
offset, GetLastError());
|
||||
} else {
|
||||
auto offsetSplit = std::bit_cast<std::array<uint32_t, 2>>(offset);
|
||||
*res = MapViewOfFile(hFileMapping, // handle to file mapping object
|
||||
viewProt, // read/write permission
|
||||
offsetSplit[1], // high offset
|
||||
offsetSplit[0], // low offset
|
||||
mappingLength); // number of bytes to map
|
||||
if (*res == NULL) {
|
||||
LOG_ERR(L"Mmap MapViewOfFile == NULL for| 0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld err:%04x", addr, mappingLength, prot, flags, fd,
|
||||
offset, GetLastError());
|
||||
} else {
|
||||
LOG_DEBUG(L"Mmap addr:0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld -> out:0x%08llx", addr, mappingLength, prot, flags, fd, offset, *res);
|
||||
registerMapping((uint64_t)*res, MappingType::File);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hFileMapping); // is kept open internally until mapView is unmapped
|
||||
|
||||
if (*res == nullptr) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int munmap(void* addr, size_t len) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
// unregister and then delete
|
||||
auto type = unregisterMapping((uint64_t)addr);
|
||||
|
||||
switch (type) {
|
||||
case MappingType::File: {
|
||||
return UnmapViewOfFile(addr) != 0 ? Ok : -1;
|
||||
} break;
|
||||
|
||||
case MappingType::Flexible: {
|
||||
return accessFlexibleMemory().destroy((uint64_t)addr, len) != 0 ? Ok : -1;
|
||||
} break;
|
||||
|
||||
case MappingType::Fixed: {
|
||||
LOG_ERR(L"tod munmap Fixed 0x%08llx 0x%08llx", addr, len);
|
||||
} break;
|
||||
|
||||
case MappingType::None: {
|
||||
LOG_ERR(L"munmap unkown 0x%08llx 0x%08llx", addr, len);
|
||||
} break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t read(int handle, void* buf, size_t nbytes) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
auto const count = file->read((char*)buf, nbytes);
|
||||
LOG_TRACE(L"KernelRead[%d]: 0x%08llx:%llu read(%lld)", handle, (uint64_t)buf, nbytes, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int64_t write(int handle, const void* buf, size_t nbytes) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"KernelWrite[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
size_t const count = file->write((char*)buf, nbytes);
|
||||
|
||||
LOG_TRACE(L"KernelWrite[%d]: 0x%08llx:%llu count:%llu", handle, (uint64_t)buf, nbytes, count);
|
||||
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int open(const char* path, SceOpen flags, SceKernelMode kernelMode) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
if (path == nullptr) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
if (std::string_view(path).starts_with("/dev")) {
|
||||
return open_dev(path, flags, kernelMode);
|
||||
}
|
||||
|
||||
assert(!flags.fsync && !flags.excl && !flags.dsync && !flags.direct);
|
||||
|
||||
auto mapped = accessFileManager().getMappedPath(path);
|
||||
if (!mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto const mappedPath = mapped.value();
|
||||
|
||||
// handle excl (file already exists)
|
||||
if (flags.excl && flags.create && std::filesystem::exists(path)) {
|
||||
return getErr(ErrCode::_EEXIST);
|
||||
}
|
||||
// -
|
||||
|
||||
bool isDir = std::filesystem::is_directory(mappedPath);
|
||||
|
||||
if (flags.directory || isDir) {
|
||||
if (!isDir) {
|
||||
LOG_WARN(L"Directory doesn't exist: %s", mappedPath.c_str());
|
||||
return getErr(ErrCode::_ENOTDIR);
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(mappedPath.parent_path())) std::filesystem::create_directories(mappedPath);
|
||||
|
||||
auto dirIt = std::make_unique<std::filesystem::directory_iterator>(mappedPath);
|
||||
int const handle = accessFileManager().addDirIterator(std::move(dirIt), mappedPath);
|
||||
LOG_INFO(L"OpenDir [%d]: %S (%s)", handle, path, mappedPath.c_str());
|
||||
|
||||
return handle;
|
||||
} else {
|
||||
// error if read only input file does not exist
|
||||
if (flags.mode == SceOpenMode::RDONLY && !std::filesystem::exists(mappedPath)) {
|
||||
LOG_WARN(L"File doesn't exist: %s mode:0x%lx", mappedPath.c_str(), flags.mode);
|
||||
return getErr(ErrCode::_ENOENT);
|
||||
}
|
||||
|
||||
auto file = createType_file(mappedPath, flags);
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
int const handle = accessFileManager().addFile(std::move(file), mappedPath);
|
||||
LOG_INFO(L"OpenFile[%d]: %s mode:0x%lx(0x%lx)", handle, mappedPath.c_str(), *(int*)&flags, kernelMode);
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
int close(int handle) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_INFO(L"Closed[%d]", handle);
|
||||
if (handle < FILE_DESCRIPTOR_MIN) {
|
||||
return getErr(ErrCode::_EPERM);
|
||||
}
|
||||
|
||||
accessFileManager().remove(handle);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int unlink(const char* path) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
if (path == nullptr) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
auto const _mapped = accessFileManager().getMappedPath(path);
|
||||
if (!_mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto const mapped = _mapped.value();
|
||||
|
||||
if (std::filesystem::is_directory(mapped)) {
|
||||
return getErr(ErrCode::_EPERM);
|
||||
}
|
||||
|
||||
if (std::filesystem::remove(mapped)) {
|
||||
LOG_INFO(L"Deleted: %S", path);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
return getErr(ErrCode::_ENOENT);
|
||||
}
|
||||
|
||||
int chmod(const char* path, SceKernelMode mode) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"TODO %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int checkReachability(const char* path) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S %S", __FUNCTION__, path);
|
||||
auto mapped = accessFileManager().getMappedPath(path);
|
||||
if (!mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto const mappedPath = mapped.value();
|
||||
|
||||
if (std::filesystem::exists(mappedPath.parent_path())) return Ok;
|
||||
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
void sync(void) {
|
||||
// todo: sync all open files?
|
||||
}
|
||||
|
||||
int fsync(int handle) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"KernelFsync[%d]", handle);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
file->sync();
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int fdatasync(int fd) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S %d", __FUNCTION__, fd);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int fcntl(int fd, int cmd, va_list args) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S %d", __FUNCTION__, fd);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
size_t readv(int handle, const SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
// todo: move it to IFile?
|
||||
size_t count = 0;
|
||||
|
||||
for (int n = 0; n < iovcnt; ++n) {
|
||||
auto* item = &iov[n];
|
||||
|
||||
if (item->iov_base == nullptr || item->iov_len == 0) continue;
|
||||
|
||||
auto const countTemp = file->read((char*)item->iov_base, item->iov_len);
|
||||
|
||||
LOG_TRACE(L"KernelRead[%d]: 0x%08llx:%llu read(%lld)", handle, (uint64_t)item->iov_base, item->iov_len, countTemp);
|
||||
|
||||
count += countTemp;
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
LOG_TRACE(L"file end");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t writev(int handle, const SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
// todo: move it to IFile?
|
||||
size_t count = 0;
|
||||
|
||||
for (int n = 0; n < iovcnt; n++) {
|
||||
auto* item = &iov[n];
|
||||
|
||||
if (item->iov_base == nullptr || item->iov_len == 0) continue;
|
||||
|
||||
auto const countTemp = file->write(item->iov_base, item->iov_len);
|
||||
|
||||
LOG_TRACE(L"KernelWrite[%d]: 0x%08llx:%llu write(%lld)", handle, (uint64_t)item->iov_base, item->iov_len, countTemp);
|
||||
|
||||
count += countTemp;
|
||||
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
LOG_TRACE(L"file end");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int fchmod(int fd, SceKernelMode mode) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int rename(const char* from, const char* to) {
|
||||
auto mapped1 = accessFileManager().getMappedPath(from);
|
||||
if (!mapped1) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto mapped2 = accessFileManager().getMappedPath(to);
|
||||
if (!mapped2) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
std::filesystem::rename(*mapped1, *mapped2);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int mkdir(const char* path, SceKernelMode mode) {
|
||||
auto _mapped = accessFileManager().getMappedPath(path);
|
||||
if (!_mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto const mapped = _mapped.value();
|
||||
|
||||
if (!std::filesystem::create_directory(mapped)) {
|
||||
return getErr(ErrCode::_EIO);
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int rmdir(const char* path) {
|
||||
auto mapped = accessFileManager().getMappedPath(path);
|
||||
if (!mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
std::filesystem::remove_all(*mapped);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int utimes(const char* path, const SceKernelTimeval* times) {
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int stat(const char* path, SceKernelStat* sb) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
|
||||
LOG_TRACE(L"KernelStat: %S", path);
|
||||
|
||||
memset(sb, 0, sizeof(SceKernelStat));
|
||||
auto _mapped = accessFileManager().getMappedPath(path);
|
||||
if (!_mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
auto const mapped = _mapped.value();
|
||||
|
||||
if (!std::filesystem::exists(mapped)) {
|
||||
return getErr(ErrCode::_ENOENT);
|
||||
}
|
||||
|
||||
bool const isDir = std::filesystem::is_directory(mapped);
|
||||
sb->mode = 0000777u | (isDir ? 0040000u : 0100000u);
|
||||
|
||||
if (isDir) {
|
||||
sb->size = 0;
|
||||
sb->blksize = 512;
|
||||
sb->blocks = 0;
|
||||
} else {
|
||||
sb->size = (int64_t)std::filesystem::file_size(mapped);
|
||||
sb->blksize = 512;
|
||||
sb->blocks = (sb->size + 511) / 512;
|
||||
|
||||
auto const lastW = std::filesystem::last_write_time(mapped);
|
||||
|
||||
auto const time = std::chrono::time_point_cast<std::chrono::nanoseconds>(lastW).time_since_epoch().count();
|
||||
|
||||
ns2timespec(&sb->aTime, time);
|
||||
ns2timespec(&sb->mTime, time);
|
||||
}
|
||||
|
||||
sb->cTime = sb->aTime;
|
||||
sb->birthtime = sb->mTime;
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int fstat(int fd, SceKernelStat* sb) {
|
||||
auto mapped = accessFileManager().getPath(fd);
|
||||
if (mapped.empty()) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
return stat(mapped.string().c_str(), sb);
|
||||
}
|
||||
|
||||
int futimes(int fd, const SceKernelTimeval* times) {
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int getdirentries(int fd, char* buf, int nbytes, long* basep) {
|
||||
if (fd < FILE_DESCRIPTOR_MIN) {
|
||||
return getErr(ErrCode::_EPERM);
|
||||
}
|
||||
if (buf == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
auto count = accessFileManager().getDents(fd, buf, nbytes, (int64_t*)basep);
|
||||
if (count < 0) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int getdents(int fd, char* buf, int nbytes) {
|
||||
return getdirentries(fd, buf, nbytes, nullptr);
|
||||
}
|
||||
|
||||
size_t preadv(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_TRACE(L"preadv [%d]", handle);
|
||||
|
||||
if (offset < 0) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"preadv[%d] file==nullptr", handle);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
file->clearError();
|
||||
file->lseek(offset, SceWhence::beg);
|
||||
|
||||
auto const count = readv(handle, iov, iovcnt);
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t pwritev(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_TRACE(L"pwritev [%d]", handle);
|
||||
|
||||
if (offset < 0) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"pwritev[%d] file==nullptr", handle);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
file->clearError();
|
||||
file->lseek(offset, SceWhence::beg);
|
||||
|
||||
auto const count = writev(handle, iov, iovcnt);
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t pread(int handle, void* buf, size_t nbytes, int64_t offset) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_TRACE(L"pread [%d]", handle);
|
||||
|
||||
if (buf == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"pread[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
file->clearError();
|
||||
file->lseek(offset, SceWhence::beg);
|
||||
|
||||
auto const count = file->read((char*)buf, nbytes);
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
|
||||
LOG_TRACE(L"pread[%d]: 0x%08llx:%llu read(%lld) offset:0x%08llx", handle, (uint64_t)buf, nbytes, count, offset);
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t pwrite(int handle, const void* buf, size_t nbytes, int64_t offset) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_TRACE(L"pwrite[%d]: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
if (file == nullptr) {
|
||||
LOG_ERR(L"write[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
|
||||
return getErr(ErrCode::_EBADF);
|
||||
}
|
||||
|
||||
file->clearError();
|
||||
file->lseek(offset, SceWhence::beg);
|
||||
|
||||
auto const count = file->write((char*)buf, nbytes);
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int64_t lseek(int handle, int64_t offset, int whence) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_TRACE(L"lseek [%d] 0x%08llx %d", handle, offset, whence);
|
||||
|
||||
if (whence > 2) return getErr(ErrCode::_EINVAL);
|
||||
|
||||
auto file = accessFileManager().accessFile(handle);
|
||||
file->clearError();
|
||||
|
||||
auto const pos = file->lseek(offset, (SceWhence)whence);
|
||||
|
||||
if (auto err = file->getErr(); err != Ok) {
|
||||
return getErr((ErrCode)err);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
int truncate(const char* path, int64_t length) {
|
||||
auto mapped = accessFileManager().getMappedPath(path);
|
||||
if (!mapped) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
std::filesystem::resize_file(*mapped, length);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int ftruncate(int fd, int64_t length) {
|
||||
auto mapped = accessFileManager().getPath(fd);
|
||||
if (mapped.empty()) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
return truncate(mapped.string().c_str(), length);
|
||||
}
|
||||
|
||||
int setCompressionAttribute(int fd, int flag) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int lwfsSetAttribute(int fd, int flags) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int lwfsAllocateBlock(int fd, int64_t size) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int lwfsTrimBlock(int fd, int64_t size) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"todo %S", __FUNCTION__);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int64_t lwfsLseek(int fd, int64_t offset, int whence) {
|
||||
LOG_USE_MODULE(filesystem);
|
||||
LOG_ERR(L"seek [%d]", fd);
|
||||
return Ok;
|
||||
}
|
||||
|
||||
size_t lwfsWrite(int fd, const void* buf, size_t nbytes) {
|
||||
return Ok;
|
||||
}
|
||||
} // namespace filesystem
|
@ -1,137 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filesystem {
|
||||
|
||||
enum class SceMapMode : uint16_t {
|
||||
SHARED = 0x0001,
|
||||
PRIVATE = 0x0002,
|
||||
FIXED = 0x0010,
|
||||
NO_OVERWRITE = 0x0080,
|
||||
VOID_ = 0x0100,
|
||||
};
|
||||
|
||||
enum class SceMapType : uint16_t {
|
||||
FILE = 0x0,
|
||||
ANON = 0x1,
|
||||
SYSTEM = 0x2,
|
||||
ALLAVAILABLE = 0x4,
|
||||
};
|
||||
|
||||
struct SceMap {
|
||||
SceMapMode mode : 12;
|
||||
SceMapType type : 4;
|
||||
};
|
||||
|
||||
enum class SceOpenMode : uint32_t {
|
||||
RDONLY = 0,
|
||||
WRONLY = 1,
|
||||
RDWR = 2,
|
||||
};
|
||||
|
||||
struct SceOpen {
|
||||
SceOpenMode mode : 2;
|
||||
uint32_t nonblock : 1;
|
||||
uint32_t append : 1;
|
||||
uint32_t shlock : 1;
|
||||
uint32_t exlock : 1;
|
||||
uint32_t async : 1;
|
||||
uint32_t fsync : 1;
|
||||
uint32_t nofollow : 1;
|
||||
uint32_t create : 1;
|
||||
uint32_t trunc : 1;
|
||||
uint32_t excl : 1;
|
||||
uint32_t dsync : 1;
|
||||
uint32_t dummy0 : 1;
|
||||
uint32_t fhaslock : 1;
|
||||
uint32_t noctty : 1;
|
||||
uint32_t direct : 1;
|
||||
uint32_t directory : 1;
|
||||
uint32_t exec : 1;
|
||||
uint32_t tty_init : 1;
|
||||
uint32_t cloexec : 1;
|
||||
};
|
||||
|
||||
struct SceKernelStat {
|
||||
uint32_t dev;
|
||||
uint32_t ino;
|
||||
uint16_t mode;
|
||||
uint16_t nlink;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint32_t rdev;
|
||||
SceKernelTimespec aTime;
|
||||
SceKernelTimespec mTime;
|
||||
SceKernelTimespec cTime;
|
||||
int64_t size;
|
||||
int64_t blocks;
|
||||
uint32_t blksize;
|
||||
uint32_t flags;
|
||||
uint32_t gen;
|
||||
int32_t lspare;
|
||||
SceKernelTimespec birthtime;
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(SceKernelTimespec)));
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(SceKernelTimespec)));
|
||||
};
|
||||
|
||||
struct Sce_iovec {
|
||||
void* iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
|
||||
typedef uint16_t SceKernelMode; // todo needed?
|
||||
|
||||
typedef struct Sce_iovec SceKernelIovec;
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL int mmap(void* addr, size_t len, int prot, SceMap flags, int fd, int64_t offset, void** res);
|
||||
__APICALL int munmap(void* address, size_t len);
|
||||
__APICALL size_t read(int handle, void* buf, size_t nbytes);
|
||||
__APICALL int64_t write(int handle, const void* buf, size_t nbytes);
|
||||
__APICALL int open(const char* path, SceOpen flags, SceKernelMode kernelMode);
|
||||
__APICALL int close(int handle);
|
||||
__APICALL int unlink(const char* path);
|
||||
__APICALL int chmod(const char* path, SceKernelMode mode);
|
||||
__APICALL int checkReachability(const char* path);
|
||||
__APICALL void sync(void);
|
||||
__APICALL int fsync(int handle);
|
||||
__APICALL int fdatasync(int fd);
|
||||
__APICALL int fcntl(int fd, int cmd, va_list args);
|
||||
__APICALL size_t readv(int handle, const SceKernelIovec* iov, int iovcnt);
|
||||
__APICALL size_t writev(int handle, const SceKernelIovec* iov, int iovcnt);
|
||||
__APICALL int fchmod(int fd, SceKernelMode mode);
|
||||
__APICALL int rename(const char* from, const char* to);
|
||||
__APICALL int mkdir(const char* path, SceKernelMode mode);
|
||||
__APICALL int rmdir(const char* path);
|
||||
__APICALL int utimes(const char* path, const SceKernelTimeval* times);
|
||||
__APICALL int stat(const char* path, SceKernelStat* sb);
|
||||
__APICALL int fstat(int fd, SceKernelStat* sb);
|
||||
__APICALL int futimes(int fd, const SceKernelTimeval* times);
|
||||
__APICALL int getdirentries(int fd, char* buf, int nbytes, long* basep);
|
||||
__APICALL int getdents(int fd, char* buf, int nbytes);
|
||||
__APICALL size_t preadv(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset);
|
||||
__APICALL size_t pwritev(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset);
|
||||
__APICALL size_t pread(int handle, void* buf, size_t nbytes, int64_t offset);
|
||||
__APICALL size_t pwrite(int handle, const void* buf, size_t nbytes, int64_t offset);
|
||||
__APICALL int64_t lseek(int handle, int64_t offset, int whence);
|
||||
__APICALL int truncate(const char* path, int64_t length);
|
||||
__APICALL int ftruncate(int fd, int64_t length);
|
||||
__APICALL int setCompressionAttribute(int fd, int flag);
|
||||
__APICALL int lwfsSetAttribute(int fd, int flags);
|
||||
__APICALL int lwfsAllocateBlock(int fd, int64_t size);
|
||||
__APICALL int lwfsTrimBlock(int fd, int64_t size);
|
||||
__APICALL int64_t lwfsLseek(int fd, int64_t offset, int whence);
|
||||
__APICALL size_t lwfsWrite(int fd, const void* buf, size_t nbytes);
|
||||
|
||||
#undef __APICALL
|
||||
}; // namespace filesystem
|
File diff suppressed because it is too large
Load Diff
@ -1,193 +0,0 @@
|
||||
#pragma once
|
||||
#include "pthread_types.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <modules_include/common.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct PthreadAttrPrivate;
|
||||
struct PthreadBarrierPrivate;
|
||||
struct PthreadBarrierattrPrivate;
|
||||
struct PthreadCondattrPrivate;
|
||||
struct PthreadCondPrivate;
|
||||
struct PthreadMutexPrivate;
|
||||
struct PthreadMutexattrPrivate;
|
||||
struct PthreadRwlockPrivate;
|
||||
struct PthreadRwlockattrPrivate;
|
||||
struct PthreadPrivate;
|
||||
struct PthreadOnce;
|
||||
|
||||
using ScePthread_obj = uint8_t*;
|
||||
using ScePthreadAttr = PthreadAttrPrivate*;
|
||||
using ScePthreadBarrier = PthreadBarrierPrivate*;
|
||||
using ScePthreadBarrierattr = PthreadBarrierattrPrivate*;
|
||||
using ScePthreadCondattr = PthreadCondattrPrivate*;
|
||||
using ScePthreadCond = PthreadCondPrivate*;
|
||||
using ScePthreadMutex = PthreadMutexPrivate*;
|
||||
using ScePthreadMutexattr = PthreadMutexattrPrivate*;
|
||||
using ScePthreadRwlock = PthreadRwlockPrivate*;
|
||||
using ScePthreadRwlockattr = PthreadRwlockattrPrivate*;
|
||||
using ScePthread = PthreadPrivate*;
|
||||
using ScePthreadOnce = PthreadOnce*;
|
||||
using ScePthreadKey = int;
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
struct DTVKey;
|
||||
|
||||
namespace pthread {
|
||||
|
||||
__APICALL uint8_t* getTLSStaticBlock(ScePthread_obj obj);
|
||||
__APICALL uint64_t* getDTV(ScePthread_obj obj);
|
||||
|
||||
__APICALL int getThreadId();
|
||||
__APICALL int getThreadId(ScePthread_obj obj);
|
||||
|
||||
__APICALL ScePthread_obj& getSelf();
|
||||
|
||||
__APICALL int attrDestroy(ScePthreadAttr* attr);
|
||||
__APICALL int attrGetstack(const ScePthreadAttr* attr, void** stackAddr, size_t* stackSize);
|
||||
__APICALL int attrGetstacksize(const ScePthreadAttr* attr, size_t* stackSize);
|
||||
__APICALL int attrGetguardsize(const ScePthreadAttr* attr, size_t* guardSize);
|
||||
__APICALL int attrGetstackaddr(const ScePthreadAttr* attr, void** stackAddr);
|
||||
__APICALL int attrGetdetachstate(const ScePthreadAttr* attr, SceDetachState* state);
|
||||
__APICALL int attrSetstacksize(ScePthreadAttr* attr, size_t stackSize);
|
||||
__APICALL int attrSetguardsize(ScePthreadAttr* attr, size_t guardSize);
|
||||
__APICALL int attrSetstack(ScePthreadAttr* attr, void* addr, size_t size);
|
||||
__APICALL int attrSetstackaddr(ScePthreadAttr* attr, void* addr);
|
||||
__APICALL int attrSetdetachstate(ScePthreadAttr* attr, SceDetachState state);
|
||||
|
||||
__APICALL int barrierDestroy(ScePthreadBarrier* barrier);
|
||||
__APICALL int barrierInit(ScePthreadBarrier* barrier, const ScePthreadBarrierattr* attr, unsigned count, const char* name);
|
||||
__APICALL int barrierWait(ScePthreadBarrier* barrier);
|
||||
|
||||
__APICALL int barrierattrDestroy(ScePthreadBarrierattr* barrier);
|
||||
__APICALL int barrierattrInit(ScePthreadBarrierattr* barrier);
|
||||
__APICALL int barrierattrGetpshared(ScePthreadBarrierattr* barrier, int* pshared);
|
||||
__APICALL int barrierattrSetpshared(ScePthreadBarrierattr* barrier, int pshared);
|
||||
|
||||
__APICALL int condattrDestroy(ScePthreadCondattr* attr);
|
||||
__APICALL int condattrInit(ScePthreadCondattr* attr);
|
||||
|
||||
__APICALL int condInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, const char* name);
|
||||
__APICALL int condBroadcast(ScePthreadCond* cond);
|
||||
__APICALL int condDestroy(ScePthreadCond* cond);
|
||||
__APICALL int condSignal(ScePthreadCond* cond);
|
||||
__APICALL int condSignalto(ScePthreadCond* cond, ScePthread_obj obj);
|
||||
__APICALL int condTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, SceKernelUseconds usec);
|
||||
__APICALL int condTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, const SceKernelTimespec* t);
|
||||
__APICALL int condWait(ScePthreadCond* cond, ScePthreadMutex* mutex);
|
||||
|
||||
__APICALL int detach(ScePthread_obj obj);
|
||||
__APICALL int equal(ScePthread_obj thread1, ScePthread_obj thread2);
|
||||
|
||||
__APICALL int getcpuclockid(ScePthread_obj thread, int* clock);
|
||||
|
||||
__APICALL int join(ScePthread_obj obj, void** value);
|
||||
|
||||
__APICALL int keyDelete(ScePthreadKey key);
|
||||
__APICALL int keyCreate(ScePthreadKey* key, pthread_key_destructor_func_t destructor);
|
||||
|
||||
__APICALL int mutexattrInit(ScePthreadMutexattr* attr);
|
||||
__APICALL int mutexattrDestroy(ScePthreadMutexattr* attr);
|
||||
__APICALL int mutexattrGettype(ScePthreadMutexattr* attr, int* type);
|
||||
__APICALL int mutexattrSettype(ScePthreadMutexattr* attr, int type);
|
||||
|
||||
__APICALL int mutexDestroy(ScePthreadMutex* mutex);
|
||||
__APICALL int mutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, const char* name);
|
||||
__APICALL int mutexLock(ScePthreadMutex* mutex);
|
||||
__APICALL int mutexTrylock(ScePthreadMutex* mutex);
|
||||
__APICALL int mutexTimedlock(ScePthreadMutex* mutex, SceKernelUseconds usec);
|
||||
__APICALL int mutexTimedlock(ScePthreadMutex* mutex, const SceKernelTimespec* t);
|
||||
__APICALL int mutexUnlock(ScePthreadMutex* mutex);
|
||||
|
||||
__APICALL int rwlockDestroy(ScePthreadRwlock* rwlock);
|
||||
__APICALL int rwlockInit(ScePthreadRwlock* rwlock, const ScePthreadRwlockattr* attr, const char* name);
|
||||
|
||||
__APICALL int rwlockRdlock(ScePthreadRwlock* rwlock);
|
||||
__APICALL int rwlockTimedrdlock(ScePthreadRwlock* rwlock, SceKernelUseconds usec);
|
||||
__APICALL int rwlockTimedrdlock(ScePthreadRwlock* rwlock, const SceKernelTimespec* t);
|
||||
__APICALL int rwlockTimedwrlock(ScePthreadRwlock* rwlock, SceKernelUseconds usec);
|
||||
__APICALL int rwlockTimedwrlock(ScePthreadRwlock* rwlock, const SceKernelTimespec* t);
|
||||
__APICALL int rwlockTryrdlock(ScePthreadRwlock* rwlock);
|
||||
__APICALL int rwlockTrywrlock(ScePthreadRwlock* rwlock);
|
||||
__APICALL int rwlockUnlock(ScePthreadRwlock* rwlock);
|
||||
__APICALL int rwlockWrlock(ScePthreadRwlock* rwlock);
|
||||
|
||||
__APICALL int rwlockattrDestroy(ScePthreadRwlockattr* attr);
|
||||
__APICALL int rwlockattrSettype(ScePthreadRwlockattr* attr, int type);
|
||||
__APICALL int rwlockattrInit(ScePthreadRwlockattr* attr);
|
||||
__APICALL int rwlockattrGettype(ScePthreadRwlockattr* attr, int* type);
|
||||
|
||||
__APICALL int setspecific(ScePthreadKey key, const void* value);
|
||||
__APICALL void* getspecific(ScePthreadKey key);
|
||||
|
||||
__APICALL int cancel(ScePthread_obj obj);
|
||||
|
||||
__APICALL int setcancelstate(int state, int* oldState);
|
||||
__APICALL int setcanceltype(int type, int* oldType);
|
||||
|
||||
__APICALL void testCancel(void);
|
||||
|
||||
__APICALL int getprio(ScePthread_obj obj, int* prio);
|
||||
__APICALL int setprio(ScePthread_obj obj, int prio);
|
||||
|
||||
__APICALL void yield(void);
|
||||
|
||||
__APICALL int mutexattrGetprioceiling(ScePthreadMutexattr* attr, int* prio);
|
||||
__APICALL int mutexattrSetprioceiling(ScePthreadMutexattr* attr, int prio);
|
||||
__APICALL int mutexGetprioceiling(ScePthreadMutex* mutex, int* prio);
|
||||
__APICALL int mutexSetprioceiling(ScePthreadMutex* mutex, int prio, int* oldPrio);
|
||||
__APICALL int mutexattrGetprotocol(ScePthreadMutexattr* attr, int* protocol);
|
||||
__APICALL int mutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
|
||||
|
||||
__APICALL int attrGetinheritsched(const ScePthreadAttr* attr, SceInheritShed* inheritSched);
|
||||
__APICALL int attrGetschedparam(const ScePthreadAttr* attr, SceSchedParam* param);
|
||||
__APICALL int attrGetschedpolicy(const ScePthreadAttr* attr, SceShedPolicy* policy);
|
||||
__APICALL int attrSetinheritsched(ScePthreadAttr* attr, SceInheritShed inheritSched);
|
||||
__APICALL int attrSetschedparam(ScePthreadAttr* attr, const SceSchedParam* param);
|
||||
__APICALL int attrSetschedpolicy(ScePthreadAttr* attr, SceShedPolicy policy);
|
||||
|
||||
__APICALL int getschedparam(ScePthread_obj obj, int* policy, SceSchedParam* param);
|
||||
__APICALL int setschedparam(ScePthread_obj obj, int policy, const SceSchedParam* param);
|
||||
|
||||
__APICALL int attrGet(ScePthread_obj obj, ScePthreadAttr* attr);
|
||||
|
||||
__APICALL int attrGetaffinity(const ScePthreadAttr* attr, SceKernelCpumask* mask);
|
||||
__APICALL int attrSetaffinity(ScePthreadAttr* attr, const SceKernelCpumask mask);
|
||||
|
||||
__APICALL int getaffinity(ScePthread_obj obj, SceKernelCpumask* mask);
|
||||
__APICALL int setaffinity(ScePthread_obj obj, const SceKernelCpumask mask);
|
||||
|
||||
__APICALL int getthreadid(void);
|
||||
__APICALL int once(ScePthreadOnce once_control, pthread_once_init init_routine);
|
||||
|
||||
__APICALL int rename(ScePthread_obj obj, const char* name);
|
||||
__APICALL int getName(ScePthread_obj obj, char* name);
|
||||
|
||||
__APICALL void setThreadDtors(thread_dtors_func_t dtors);
|
||||
|
||||
__APICALL int attrInit(ScePthreadAttr* attr);
|
||||
|
||||
__APICALL void exit(void* value);
|
||||
__APICALL void raise(ScePthread_obj obj, void* callback, int signo);
|
||||
|
||||
__APICALL int create(ScePthread_obj* obj, const ScePthreadAttr* attr, pthread_entry_func_t entry, void* arg, const char* name);
|
||||
|
||||
__APICALL int attrSetscope(ScePthreadAttr* attr, int flag);
|
||||
__APICALL int attrGetscope(ScePthreadAttr* attr, int* flag);
|
||||
|
||||
__APICALL void cxa_finalize(void* /*p*/);
|
||||
|
||||
__APICALL void cleanup_push(thread_clean_func_t func, void* arg);
|
||||
__APICALL void cleanup_pop(int execute);
|
||||
|
||||
} // namespace pthread
|
||||
|
||||
#undef __APICALL
|
@ -1,168 +0,0 @@
|
||||
#pragma once
|
||||
#include "core/unwinding/unwind.h"
|
||||
#include "modules_include/common.h"
|
||||
#include "pthread_types.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
constexpr size_t DEFAULT_STACKSIZE = 16 * 1024 * 1024;
|
||||
constexpr uint64_t XSAVE_CHK_GUARD = 0xDeadBeef5533CCAAu;
|
||||
|
||||
constexpr int KEYS_MAX = 256;
|
||||
constexpr int DESTRUCTOR_ITERATIONS = 4;
|
||||
|
||||
constexpr size_t DTV_MAX_KEYS = 256;
|
||||
|
||||
struct PthreadAttrPrivate {
|
||||
private:
|
||||
size_t stackSize = DEFAULT_STACKSIZE;
|
||||
void* stackAddr = 0; /// if != 0 use the custum stack
|
||||
|
||||
SceSchedParam shedParam = {.sched_priority = 700};
|
||||
SceKernelCpumask affinity = 0x7f;
|
||||
size_t guardSize = 0x1000;
|
||||
SceShedPolicy policy = SceShedPolicy::FIFO;
|
||||
int scope = 0;
|
||||
SceDetachState detachState = SceDetachState::JOINABLE;
|
||||
|
||||
SceInheritShed inheritShed = SceInheritShed::INHERIT;
|
||||
|
||||
public:
|
||||
PthreadAttrPrivate() = default;
|
||||
|
||||
auto getStackAddr() const noexcept { return stackAddr; }
|
||||
|
||||
void setStackAddr(decltype(PthreadAttrPrivate::stackAddr) param) noexcept { stackAddr = param; }
|
||||
|
||||
auto getStackSize() const noexcept { return stackSize; }
|
||||
|
||||
void setStackSize(decltype(PthreadAttrPrivate::stackSize) param) noexcept { stackSize = param; }
|
||||
|
||||
auto getGuardSize() const noexcept { return guardSize; }
|
||||
|
||||
void setGuardSize(decltype(PthreadAttrPrivate::guardSize) param) noexcept { guardSize = param; }
|
||||
|
||||
auto getAffinity() const noexcept { return affinity; }
|
||||
|
||||
void setAffinity(decltype(PthreadAttrPrivate::affinity) param) noexcept { affinity = param; }
|
||||
|
||||
auto getPolicy() const noexcept { return policy; }
|
||||
|
||||
void setPolicy(decltype(PthreadAttrPrivate::policy) param) noexcept { policy = param; }
|
||||
|
||||
auto getScope() const noexcept { return scope; }
|
||||
|
||||
void setScope(decltype(PthreadAttrPrivate::scope) param) noexcept { scope = param; }
|
||||
|
||||
auto getShedParam() const noexcept { return shedParam; }
|
||||
|
||||
void setShedParam(decltype(PthreadAttrPrivate::shedParam) param) noexcept { shedParam = param; }
|
||||
|
||||
auto getDetachState() const noexcept { return detachState; }
|
||||
|
||||
void setDetachState(decltype(PthreadAttrPrivate::detachState) param) noexcept { detachState = param; }
|
||||
|
||||
auto getInheritShed() const noexcept { return inheritShed; }
|
||||
|
||||
void setInheritShed(decltype(PthreadAttrPrivate::inheritShed) param) noexcept { inheritShed = param; }
|
||||
};
|
||||
|
||||
struct PthreadMutexPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[256]; // type is bigger on ps than here
|
||||
boost::recursive_mutex p;
|
||||
std::string name;
|
||||
size_t id;
|
||||
int prioCeiling;
|
||||
SceMutexType type = SceMutexType::DEFAULT;
|
||||
|
||||
~PthreadMutexPrivate() {}
|
||||
};
|
||||
|
||||
struct PthreadMutexattrPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[64]; // type is bigger on ps than here
|
||||
|
||||
SceMutexType type = SceMutexType::DEFAULT;
|
||||
SceMutexProtocol pprotocol = SceMutexProtocol::PRIO_NONE;
|
||||
int prioCeiling;
|
||||
};
|
||||
|
||||
struct DTVKey {
|
||||
bool used = false;
|
||||
|
||||
pthread_key_destructor_func_t destructor = nullptr;
|
||||
};
|
||||
|
||||
constexpr size_t DTV_SIZE = DTV_MAX_KEYS;
|
||||
|
||||
struct PthreadPrivate {
|
||||
|
||||
uint64_t dtv[DTV_SIZE];
|
||||
std::string name;
|
||||
|
||||
boost::thread p;
|
||||
|
||||
PthreadAttrPrivate attr = {};
|
||||
pthread_entry_func_t entry = nullptr;
|
||||
void* arg = nullptr;
|
||||
int unique_id = 0;
|
||||
std::atomic_bool started = false;
|
||||
int policy = 0;
|
||||
|
||||
std::vector<std::pair<thread_clean_func_t, void*>> cleanupFuncs;
|
||||
|
||||
bool detached = false; // fake detach
|
||||
|
||||
unwinding_jmp_buf _unwinding;
|
||||
~PthreadPrivate() = default;
|
||||
};
|
||||
|
||||
struct PthreadRwlockPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[256]; // type is bigger on ps than here
|
||||
|
||||
boost::shared_mutex p;
|
||||
std::string name;
|
||||
size_t id;
|
||||
|
||||
bool isWrite = false;
|
||||
|
||||
~PthreadRwlockPrivate() {}
|
||||
};
|
||||
|
||||
struct PthreadBarrierPrivate {};
|
||||
|
||||
struct PthreadBarrierattrPrivate {
|
||||
int pshared;
|
||||
};
|
||||
|
||||
struct PthreadCondattrPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[64]; // type is bigger on ps than here
|
||||
};
|
||||
|
||||
struct PthreadRwlockattrPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[64]; // type is bigger on ps than here
|
||||
int type;
|
||||
};
|
||||
|
||||
struct PthreadCondPrivate {
|
||||
uint64_t initialized = 1;
|
||||
uint8_t reserved[64]; // type is bigger on ps than here
|
||||
|
||||
boost::condition_variable p;
|
||||
std::string name;
|
||||
|
||||
~PthreadCondPrivate() {}
|
||||
};
|
||||
|
||||
struct PthreadOnce {
|
||||
int32_t isInit; // 0: no, 1: yes
|
||||
int32_t _align;
|
||||
|
||||
PthreadMutexPrivate* mutex;
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
#include "utility/utility.h"
|
||||
|
||||
using pthread_entry_func_t = SYSV_ABI void* (*)(void*);
|
||||
using pthread_key_destructor_func_t = SYSV_ABI void (*)(void*);
|
||||
|
||||
using thread_dtors_func_t = SYSV_ABI void (*)();
|
||||
using thread_clean_func_t = SYSV_ABI void (*)(void*);
|
||||
|
||||
using pthread_once_init = SYSV_ABI void (*)();
|
||||
|
||||
struct SceSchedParam {
|
||||
int sched_priority;
|
||||
};
|
||||
|
||||
enum class SceMutexProtocol { PRIO_NONE, PRIO_INHERIT, PRIO_PROTECT };
|
||||
enum class SceMutexType { DEFAULT, ERRORCHECK, RECURSIVE, NORMAL, ADAPTIVE_NP };
|
||||
|
||||
enum class SceDetachState {
|
||||
JOINABLE,
|
||||
DETACHED,
|
||||
};
|
||||
|
||||
enum class SceShedPolicy {
|
||||
OTHER,
|
||||
FIFO,
|
||||
RR,
|
||||
};
|
||||
enum class SceInheritShed {
|
||||
EXPLICIT,
|
||||
INHERIT = 4,
|
||||
};
|
||||
|
||||
enum class SceCancelState {
|
||||
ENABLE,
|
||||
DISABLE,
|
||||
};
|
||||
enum class SceCancelType {
|
||||
DEFERRED,
|
||||
ASYNC = 4,
|
||||
};
|
||||
|
||||
struct SceCleanInfo {
|
||||
SceCleanInfo* prev;
|
||||
thread_clean_func_t func;
|
||||
void* arg;
|
||||
int onHeap;
|
||||
};
|
@ -1,189 +0,0 @@
|
||||
#include "semaphore.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "modules_include/common.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
LOG_DEFINE_MODULE(Semaphore)
|
||||
|
||||
namespace {
|
||||
size_t getUniqueId() {
|
||||
static size_t count = 0;
|
||||
return count++;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class Semaphore: public ISemaphore {
|
||||
|
||||
enum class Status { Set, Canceled, Deleted };
|
||||
|
||||
boost::mutex m_mutex;
|
||||
boost::condition_variable m_condState;
|
||||
|
||||
std::queue<std::shared_ptr<boost::condition_variable>> m_condQueue; // todo: optimze
|
||||
|
||||
std::string m_name;
|
||||
size_t m_waitCounter = 0;
|
||||
size_t const m_id = getUniqueId();
|
||||
int m_countThreads = 0;
|
||||
|
||||
Status m_state = Status::Set;
|
||||
bool m_fifo;
|
||||
int m_count;
|
||||
int m_max_count;
|
||||
|
||||
public:
|
||||
Semaphore(const std::string& name, bool fifo, int init_count, int max_count): m_name(name), m_fifo(fifo), m_count(init_count), m_max_count(max_count) {
|
||||
if (!fifo) m_condQueue.push(std::make_shared<boost::condition_variable>());
|
||||
};
|
||||
|
||||
virtual ~Semaphore();
|
||||
|
||||
int cancel(int setCount, int* numWaitingThreads) final;
|
||||
int signal(int signalCount) final;
|
||||
int wait(int needcount, uint32_t* pMicros) final;
|
||||
int try_wait(int needcount, uint32_t* pMicros) final;
|
||||
|
||||
std::string_view const getName() const final { return m_name; }
|
||||
|
||||
size_t getId() const final { return m_id; }
|
||||
|
||||
private:
|
||||
int wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock);
|
||||
};
|
||||
|
||||
std::unique_ptr<ISemaphore> createSemaphore(const char* name, bool fifo, int initCount, int maxCount) {
|
||||
return std::make_unique<Semaphore>(name == nullptr ? "" : name, fifo, initCount, maxCount);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_state = Status::Deleted;
|
||||
|
||||
while (!m_condQueue.empty()) {
|
||||
m_condQueue.front()->notify_all();
|
||||
m_condQueue.pop();
|
||||
}
|
||||
|
||||
// Wait for Threads to leave wait
|
||||
m_condState.wait(lock, [this] { return m_countThreads == 0; });
|
||||
}
|
||||
|
||||
int Semaphore::cancel(int setCount, int* numWaitingThreads) {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
|
||||
m_state = Status::Canceled;
|
||||
if (numWaitingThreads != nullptr) *numWaitingThreads = m_countThreads;
|
||||
|
||||
if (m_fifo) {
|
||||
while (!m_condQueue.empty()) {
|
||||
m_condQueue.front()->notify_one();
|
||||
m_condQueue.pop();
|
||||
}
|
||||
} else {
|
||||
m_condQueue.front()->notify_all();
|
||||
}
|
||||
|
||||
// Wait for Threads to leave wait
|
||||
m_condState.wait(lock, [this] { return m_countThreads == 0; });
|
||||
m_state = Status::Set;
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::signal(int signalCount) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
|
||||
boost::unique_lock lock(m_mutex);
|
||||
LOG_TRACE(L"KernelSema(%llu) name:%S signal:%d count:%d", m_id, m_name.c_str(), signalCount, m_count);
|
||||
|
||||
m_condState.wait(lock, [this] { return m_state != Status::Canceled; });
|
||||
if (m_state == Status::Deleted) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
if (m_count + signalCount > m_max_count) {
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
if (signalCount > 0) {
|
||||
m_count += signalCount;
|
||||
if (!m_condQueue.empty()) m_condQueue.front()->notify_one();
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock) {
|
||||
LOG_USE_MODULE(Semaphore);
|
||||
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
|
||||
uint32_t const micros = pMicros != nullptr ? *pMicros : 0;
|
||||
size_t waitCount = 0;
|
||||
{
|
||||
|
||||
if (m_state == Status::Deleted) return getErr(ErrCode::_EACCES);
|
||||
|
||||
std::shared_ptr<boost::condition_variable> condVar;
|
||||
if (m_fifo) {
|
||||
condVar = std::make_shared<boost::condition_variable>();
|
||||
m_condQueue.push(condVar);
|
||||
} else {
|
||||
condVar = m_condQueue.front();
|
||||
}
|
||||
|
||||
waitCount = m_waitCounter++;
|
||||
LOG_TRACE(L"-> KernelSema(%llu) name:%S waitCount:%llu need:%d count:%d time:%u us", m_id, m_name.c_str(), waitCount, needCount, m_count, micros);
|
||||
|
||||
m_countThreads++;
|
||||
if (pMicros != nullptr) {
|
||||
if (!condVar->wait_for(lock, boost::chrono::microseconds(micros),
|
||||
[this, condVar, needCount] { return m_state != Status::Set || (m_condQueue.front() == condVar && m_count >= needCount); })) {
|
||||
LOG_WARN(L"<- KernelSema(%llu) name:%S waitCount:%llu timeout", m_id, m_name.c_str(), waitCount);
|
||||
*pMicros = 0;
|
||||
m_countThreads--;
|
||||
return getErr(ErrCode::_ETIMEDOUT);
|
||||
}
|
||||
} else {
|
||||
condVar->wait(lock, [this, condVar, needCount] { return m_state != Status::Set || (m_condQueue.front() == condVar && m_count >= needCount); });
|
||||
}
|
||||
|
||||
if (m_fifo) m_condQueue.pop();
|
||||
m_count -= needCount;
|
||||
m_countThreads--;
|
||||
|
||||
m_condState.notify_all();
|
||||
|
||||
if (m_count >= 0 && !m_condQueue.empty()) m_condQueue.front()->notify_one(); // notify next in queue if count is != 0
|
||||
}
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - start).count();
|
||||
LOG_TRACE(L"<- KernelSema(%llu) name:%S waitCount:%llu count:%d waiting:%llu", m_id, m_name.c_str(), waitCount, m_count, m_condQueue.size());
|
||||
|
||||
if (pMicros != nullptr) {
|
||||
*pMicros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
if (m_state == Status::Canceled) {
|
||||
return getErr(ErrCode::_ECANCELED);
|
||||
}
|
||||
|
||||
if (m_state == Status::Deleted) {
|
||||
return getErr(ErrCode::_EACCES);
|
||||
}
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
int Semaphore::wait(int needCount, uint32_t* pMicros) {
|
||||
|
||||
boost::unique_lock lock(m_mutex);
|
||||
return wait_internal(needCount, pMicros, lock);
|
||||
}
|
||||
|
||||
int Semaphore::try_wait(int needCount, uint32_t* pMicros) {
|
||||
boost::unique_lock lock(m_mutex);
|
||||
if (m_countThreads > 0) return getErr(ErrCode::_EAGAIN);
|
||||
|
||||
return wait_internal(needCount, pMicros, lock);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class ISemaphore {
|
||||
CLASS_NO_COPY(ISemaphore);
|
||||
CLASS_NO_MOVE(ISemaphore);
|
||||
|
||||
protected:
|
||||
ISemaphore() = default;
|
||||
|
||||
public:
|
||||
virtual ~ISemaphore() = default;
|
||||
|
||||
virtual int cancel(int setCount, int* numWaitingThreads) = 0;
|
||||
virtual int signal(int signalCount) = 0;
|
||||
virtual int wait(int needcount, uint32_t* pMicros) = 0;
|
||||
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
|
||||
|
||||
int poll(int needCount) {
|
||||
uint32_t micros = 0;
|
||||
return wait(needCount, µs);
|
||||
}
|
||||
|
||||
virtual std::string_view const getName() const = 0;
|
||||
|
||||
virtual size_t getId() const = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
__APICALL std::unique_ptr<ISemaphore> createSemaphore(const char* name, bool fifo, int initCount, int maxCount);
|
||||
|
||||
#undef __APICALL
|
@ -1,5 +0,0 @@
|
||||
add_library(memory OBJECT
|
||||
memory.cpp
|
||||
)
|
||||
|
||||
add_dependencies(memory third_party psOff_utility)
|
@ -1,305 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "memory.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "logging.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
LOG_DEFINE_MODULE(memory);
|
||||
|
||||
namespace {
|
||||
bool checkIsGPU(int protection) {
|
||||
return (protection & 0xf0) > 0;
|
||||
}
|
||||
|
||||
DWORD convProtection(int prot) {
|
||||
switch (prot & 0xf) {
|
||||
case 0: return PAGE_NOACCESS;
|
||||
case 1: return PAGE_READONLY;
|
||||
case 2:
|
||||
case 3: return PAGE_READWRITE;
|
||||
case 4: return PAGE_EXECUTE;
|
||||
case 5: return PAGE_EXECUTE_READ;
|
||||
case 6:
|
||||
case 7: return PAGE_EXECUTE_READWRITE;
|
||||
}
|
||||
|
||||
if (checkIsGPU(prot)) {
|
||||
return PAGE_READWRITE; // special case: cpu read/writes gpumemory on host memory
|
||||
}
|
||||
|
||||
return PAGE_NOACCESS;
|
||||
}
|
||||
|
||||
int convProtection(DWORD prot) {
|
||||
switch (prot) {
|
||||
case PAGE_NOACCESS: return 0;
|
||||
case PAGE_READONLY: return SceProtRead;
|
||||
case PAGE_READWRITE: return SceProtRead | SceProtWrite;
|
||||
case PAGE_EXECUTE: return SceProtExecute;
|
||||
case PAGE_EXECUTE_READ: return SceProtExecute | SceProtRead;
|
||||
case PAGE_EXECUTE_READWRITE: return SceProtRead | SceProtWrite | SceProtExecute;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
using VirtualAlloc2_func_t = /*WINBASEAPI*/ PVOID WINAPI (*)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
|
||||
|
||||
VirtualAlloc2_func_t getVirtualAlloc2() {
|
||||
static auto funcVirtualAlloc2 = [] {
|
||||
LOG_USE_MODULE(memory);
|
||||
HMODULE h = GetModuleHandle("KernelBase");
|
||||
if (h != nullptr) {
|
||||
auto const addr = reinterpret_cast<VirtualAlloc2_func_t>(GetProcAddress(h, "VirtualAlloc2"));
|
||||
if (addr == nullptr) {
|
||||
LOG_CRIT(L"virtual_alloc2 == nullptr");
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
LOG_CRIT(L"KernelBase not found");
|
||||
return (VirtualAlloc2_func_t) nullptr;
|
||||
}();
|
||||
|
||||
return funcVirtualAlloc2;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace memory {
|
||||
int getpagesize(void) {
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
}
|
||||
|
||||
uint64_t getTotalSystemMemory() {
|
||||
MEMORYSTATUSEX status;
|
||||
status.dwLength = sizeof(status);
|
||||
GlobalMemoryStatusEx(&status);
|
||||
|
||||
return status.ullTotalPhys;
|
||||
}
|
||||
|
||||
uintptr_t reserve(uint64_t start, uint64_t size, uint64_t alignment, bool isGpu) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
MEM_ADDRESS_REQUIREMENTS requirements {
|
||||
.LowestStartingAddress = (PVOID)start,
|
||||
.HighestEndingAddress = (PVOID)0,
|
||||
.Alignment = alignment,
|
||||
};
|
||||
|
||||
MEM_EXTENDED_PARAMETER param {
|
||||
.Type = MemExtendedParameterAddressRequirements,
|
||||
.Pointer = &requirements,
|
||||
};
|
||||
|
||||
auto ptr = (uintptr_t)getVirtualAlloc2()(GetCurrentProcess(), nullptr, size, isGpu ? MEM_RESERVE | MEM_WRITE_WATCH : MEM_RESERVE, PAGE_NOACCESS, ¶m, 1);
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
if (err != ERROR_INVALID_PARAMETER) {
|
||||
LOG_ERR(L"reserve failed err:0x%08x| start:0x%08llx size:%llu alignment:%llu isGpu:%d", err, start, size, alignment, isGpu);
|
||||
} else {
|
||||
return reserve(start, size, alignment << 1, isGpu);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint64_t commit(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment, int prot) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
auto ptr = (uintptr_t)VirtualAlloc((LPVOID)(baseAddr + offset), size, MEM_COMMIT, convProtection(prot));
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
LOG_ERR(L"commit failed err:0x%0x| base:0x%08llx offset:0x%08llx size:%llu alignment:%llu prot:%d", err, baseAddr, offset, size, alignment, prot);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint64_t allocGPUMemory(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
MEMORY_BASIC_INFORMATION im;
|
||||
VirtualQuery((LPVOID)baseAddr, &im, sizeof(im));
|
||||
|
||||
VirtualFree(im.AllocationBase, 0, MEM_RELEASE);
|
||||
|
||||
auto ptrBase = reserve((uint64_t)im.AllocationBase, im.RegionSize, alignment, true);
|
||||
if (ptrBase == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
LOG_ERR(L"allocGPUMemory failed err:0x%0x| addr:0x%08llx size:%llu", err, baseAddr, im.RegionSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ptr = (uintptr_t)VirtualAlloc((LPVOID)(baseAddr + offset), size, MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE);
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
LOG_ERR(L"allocGPUMemory failed err:0x%0x| addr:0x%08llx size:%llu", err, baseAddr, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DEBUG(L"allocGPUMemory| base:0x%08llx(0x%08llx) addr:0x%08llx", ptrBase, baseAddr, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int64_t queryAlloc(uintptr_t addr, uintptr_t* start, uintptr_t* end, int* prot) {
|
||||
MEMORY_BASIC_INFORMATION im;
|
||||
if (VirtualQuery((LPVOID)addr, &im, sizeof(im)) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (start != nullptr) *start = (uintptr_t)im.BaseAddress;
|
||||
if (end != nullptr) *end = *start + im.RegionSize;
|
||||
if (prot != nullptr) {
|
||||
*prot = convProtection(im.Protect);
|
||||
}
|
||||
return (int64_t)im.AllocationBase;
|
||||
}
|
||||
|
||||
uint64_t allocAligned(uint64_t address, uint64_t size, int prot, uint64_t alignment) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
constexpr uint64_t USER_MIN = DIRECTMEM_START;
|
||||
constexpr uint64_t USER_MAX = 0xFBFFFFFFFFu;
|
||||
|
||||
MEM_ADDRESS_REQUIREMENTS req2 {};
|
||||
MEM_EXTENDED_PARAMETER param2 {};
|
||||
req2.LowestStartingAddress = (address == 0 ? reinterpret_cast<PVOID>(USER_MIN) : reinterpret_cast<PVOID>(address));
|
||||
req2.HighestEndingAddress = reinterpret_cast<PVOID>(USER_MAX);
|
||||
req2.Alignment = alignment;
|
||||
param2.Type = MemExtendedParameterAddressRequirements;
|
||||
param2.Pointer = &req2;
|
||||
|
||||
static auto virtual_alloc2 = getVirtualAlloc2();
|
||||
|
||||
if (virtual_alloc2 == nullptr) {
|
||||
LOG_CRIT(L"virtual_alloc2 == nullptr");
|
||||
return 0;
|
||||
}
|
||||
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
|
||||
if (checkIsGPU(prot)) flags |= MEM_WRITE_WATCH;
|
||||
auto ptr = reinterpret_cast<uintptr_t>(virtual_alloc2(0, nullptr, size, flags, convProtection(prot), ¶m2, 1));
|
||||
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
if (err != ERROR_INVALID_PARAMETER) {
|
||||
LOG_ERR(L"VirtualAlloc2(alignment = 0x%08llx failed: 0x%04x", alignment, err);
|
||||
} else {
|
||||
return alloc(0, size, prot);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint64_t alloc(uint64_t address, uint64_t size, int prot) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
|
||||
if (checkIsGPU(prot)) flags |= MEM_WRITE_WATCH;
|
||||
|
||||
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, flags, convProtection(prot)));
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
|
||||
if (err != ERROR_INVALID_ADDRESS) {
|
||||
LOG_ERR(L"VirtualAlloc() failed @0x%08llx:0x%08llx protection:0x%x, err:0x%04x", address, size, prot, err);
|
||||
} else {
|
||||
return allocAligned(address, size, prot, 0);
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool allocFixed(uint64_t address, uint64_t size, int protection) {
|
||||
LOG_USE_MODULE(memory);
|
||||
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
|
||||
if (checkIsGPU(protection)) flags |= MEM_WRITE_WATCH;
|
||||
|
||||
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, flags, convProtection(protection)));
|
||||
if (ptr == 0) {
|
||||
auto err = static_cast<uint32_t>(GetLastError());
|
||||
|
||||
LOG_ERR(L"VirtualAlloc() failed: 0x%04x", err);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ptr != address) {
|
||||
LOG_ERR(L"VirtualAlloc() failed: wrong address");
|
||||
VirtualFree(reinterpret_cast<LPVOID>(ptr), 0, MEM_RELEASE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool free(uint64_t address) {
|
||||
LOG_USE_MODULE(memory);
|
||||
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) == 0) {
|
||||
LOG_ERR(L"VirtualFree() failed: 0x%04x", static_cast<uint32_t>(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool protect(uint64_t address, uint64_t size, int protection, int* oldProt) {
|
||||
LOG_USE_MODULE(memory);
|
||||
|
||||
DWORD oldProtection;
|
||||
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, convProtection(protection), &oldProtection) == 0) {
|
||||
LOG_ERR(L"VirtualProtect() failed addr:0x%08llx size:0x%08llx prot:%d err:0x%04x", address, size, protection, static_cast<uint32_t>(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oldProt != nullptr) *oldProt = (protection & 0xF0) | convProtection(oldProtection);
|
||||
return true;
|
||||
}
|
||||
|
||||
int getProtection(uint64_t address) {
|
||||
LOG_USE_MODULE(memory);
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (!VirtualQuery((const void*)address, &mbi, sizeof(mbi))) {
|
||||
LOG_ERR(L"Failed to query memory");
|
||||
return 0;
|
||||
}
|
||||
return convProtection(mbi.Protect);
|
||||
}
|
||||
|
||||
void installHook_long(uintptr_t dst, uintptr_t src, _t_hook& pGateway, size_t lenOpCodes) {
|
||||
std::array<uint8_t, 14> codeGateway = {
|
||||
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr
|
||||
};
|
||||
|
||||
// setup jmp back code
|
||||
*(uint64_t*)&codeGateway[6] = (uint64_t)(src + lenOpCodes);
|
||||
|
||||
LOG_USE_MODULE(memory);
|
||||
{
|
||||
// Interceptor, jmp to dst
|
||||
std::array code = codeGateway;
|
||||
*(uint64_t*)&code[6] = (uint64_t)(dst);
|
||||
|
||||
int oldMode;
|
||||
protect(src, code.size(), SceProtExecute | SceProtRead | SceProtWrite, &oldMode);
|
||||
|
||||
memcpy(pGateway.data.data(), (uint8_t*)src, lenOpCodes); // save code -> add to gateway
|
||||
memcpy((uint8_t*)src, code.data(), code.size());
|
||||
|
||||
if (::FlushInstructionCache(GetCurrentProcess(), (void*)src, code.size()) == 0) {
|
||||
LOG_ERR(L"FlushInstructionCache() failed: 0x%04x", static_cast<uint32_t>(GetLastError()));
|
||||
return;
|
||||
}
|
||||
protect(src, code.size(), oldMode);
|
||||
// - interceptor
|
||||
}
|
||||
|
||||
memcpy(pGateway.data.data() + lenOpCodes, (uint8_t*)codeGateway.data(), codeGateway.size()); // cpy to gateway
|
||||
|
||||
protect((uintptr_t)pGateway.data.data(), pGateway.data.size(), SceProtExecute | SceProtRead, nullptr);
|
||||
}
|
||||
} // namespace memory
|
@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr int SceProtRead = 1;
|
||||
constexpr int SceProtWrite = 2;
|
||||
constexpr int SceProtExecute = 4;
|
||||
|
||||
constexpr uint64_t DIRECTMEM_START = 0x100000000u;
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
namespace memory {
|
||||
constexpr inline bool isExecute(int prot) {
|
||||
return (prot & 0x4) > 0;
|
||||
}
|
||||
|
||||
struct _t_hook {
|
||||
std::array<uint8_t, 14 + 8> data; // Should be enough for inserting the hook (min 14 max 14+8)
|
||||
};
|
||||
|
||||
__APICALL int getpagesize(void);
|
||||
__APICALL uint64_t getTotalSystemMemory();
|
||||
__APICALL uintptr_t reserve(uint64_t start, uint64_t size, uint64_t alignment, bool isGpu);
|
||||
__APICALL uint64_t commit(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment, int prot);
|
||||
__APICALL uint64_t allocGPUMemory(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment);
|
||||
__APICALL int64_t queryAlloc(uintptr_t addr, uintptr_t* start, uintptr_t* end, int* prot);
|
||||
__APICALL uint64_t alloc(uint64_t address, uint64_t size, int prot);
|
||||
__APICALL uint64_t allocAligned(uint64_t address, uint64_t size, int prot, uint64_t alignment);
|
||||
__APICALL bool allocFixed(uint64_t address, uint64_t size, int prot);
|
||||
__APICALL bool free(uint64_t address);
|
||||
__APICALL bool protect(uint64_t address, uint64_t size, int prot, int* oldMode = nullptr);
|
||||
__APICALL int getProtection(uint64_t address);
|
||||
|
||||
__APICALL void installHook_long(uintptr_t dst, uintptr_t src, _t_hook& pGateway, size_t lenOpCodes);
|
||||
} // namespace memory
|
||||
|
||||
#undef __APICALL
|
@ -1,10 +0,0 @@
|
||||
# Overview
|
||||
|
||||
<div align="center">
|
||||
|
||||
![](../out/docs/uml/modules/coreDeps.svg)
|
||||
</div>
|
||||
|
||||
All communication goes through the core library. The callbacks to the emulator are set during startup, before loading and setting up the target library.
|
||||
|
||||
Modules should only communicate unidirectional with the core library. Everything that can't be done locally inside modules, goes here.
|
@ -1,5 +0,0 @@
|
||||
add_library(systemContent OBJECT
|
||||
systemContent.cpp
|
||||
)
|
||||
|
||||
add_dependencies(systemContent third_party psOff_utility)
|
@ -1,145 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "systemContent.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "logging.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
|
||||
LOG_DEFINE_MODULE(SYSTEMCONTENT);
|
||||
|
||||
namespace {
|
||||
struct ParamInfo {
|
||||
uint16_t type;
|
||||
uint32_t size1;
|
||||
uint32_t size2;
|
||||
uint16_t nameOffset;
|
||||
uint32_t valueOffset;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> loadSFO(std::filesystem::path const& root) {
|
||||
LOG_USE_MODULE(SYSTEMCONTENT);
|
||||
|
||||
auto file = util::openFile(root / L"param.sfo");
|
||||
if (!file) {
|
||||
LOG_ERR(L"No param.sfo");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Check Magic
|
||||
{
|
||||
std::array<uint8_t, 8> buffer;
|
||||
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
|
||||
LOG_ERR(L"Couldn't read .sfo magic");
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t const magic1 = *(uint32_t const*)&buffer[0];
|
||||
uint32_t const magic2 = *(uint32_t const*)&buffer[4];
|
||||
|
||||
if (magic1 != 0x46535000 && magic2 != 0x00000101) {
|
||||
LOG_ERR(L".sfo magic error| 0x%08x 0x%08x", magic1, magic2);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// - Magic
|
||||
|
||||
auto [nameOffset, valueOffset, numParams] = [&]() -> std::tuple<uint32_t, uint32_t, uint32_t> {
|
||||
std::array<uint8_t, 12> buffer;
|
||||
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
|
||||
LOG_ERR(L"Couldn't read .sfo Header");
|
||||
return {};
|
||||
}
|
||||
return std::make_tuple(*(uint32_t const*)&buffer[0], *(uint32_t const*)&buffer[4], *(uint32_t const*)&buffer[8]);
|
||||
}();
|
||||
|
||||
// Read Params
|
||||
std::vector<ParamInfo> params(numParams);
|
||||
{
|
||||
std::array<uint8_t, 16> buffer;
|
||||
for (uint16_t n = 0; n < numParams; ++n) {
|
||||
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
|
||||
LOG_ERR(L"Couldn't read .sfo paramInfo %llu", n);
|
||||
return {};
|
||||
}
|
||||
|
||||
params[n] = ParamInfo {.type = *(uint16_t const*)&buffer[2],
|
||||
.size1 = *(uint32_t const*)&buffer[4],
|
||||
.size2 = *(uint32_t const*)&buffer[8],
|
||||
.nameOffset = *(uint16_t const*)&buffer[0],
|
||||
.valueOffset = *(uint32_t const*)&buffer[12]};
|
||||
|
||||
if (params[n].type != 0x0204 && params[n].type != 0x0404) {
|
||||
LOG_ERR(L"unknown .sfo param %u type:%u", n, params[n].type);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
// - params
|
||||
|
||||
// read nameList
|
||||
const uint32_t nameListSize = valueOffset - nameOffset;
|
||||
std::vector<uint8_t> nameList(nameListSize);
|
||||
file->seekg(nameOffset);
|
||||
if (!util::readFile(file.get(), L"param.sfo", (char*)nameList.data(), nameList.size())) {
|
||||
LOG_ERR(L"Couldn't read .sfo nameList");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> paramList;
|
||||
for (uint16_t n = 0; n < numParams; ++n) {
|
||||
auto const name = std::string((char*)&nameList[params[n].nameOffset]);
|
||||
|
||||
file->seekg(valueOffset + params[n].valueOffset, std::ios_base::beg);
|
||||
std::vector<uint8_t> paramValue(params[n].size1);
|
||||
|
||||
if (!util::readFile(file.get(), L"param.sfo", (char*)paramValue.data(), paramValue.size())) {
|
||||
LOG_ERR(L"Couldn't read .sfo param %u value");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (params[n].type == 0x0404)
|
||||
LOG_DEBUG(L"Read .sfo param[%u] %S| size:%u value(uint):%u", n, name.data(), paramValue.size(), *(uint32_t*)paramValue.data());
|
||||
else if (params[n].type == 0x0204)
|
||||
LOG_DEBUG(L"Read .sfo param[%u] %S| size:%u value(string):%S", n, name.data(), paramValue.size(), (char*)paramValue.data());
|
||||
paramList[std::move(name)] = std::make_pair(params[n].type, std::move(paramValue));
|
||||
}
|
||||
return paramList;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class SystemContent: public ISystemContent {
|
||||
private:
|
||||
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> m_sfoParams;
|
||||
|
||||
public:
|
||||
void init(std::filesystem::path const& path) final;
|
||||
|
||||
std::optional<uint32_t> getInt(std::string_view name) final {
|
||||
if (auto it = m_sfoParams.find(name.data()); it != m_sfoParams.end()) {
|
||||
if (it->second.first != 0x0404) return {};
|
||||
return {*(uint32_t*)it->second.second.data()};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::string_view> getString(std::string_view name) {
|
||||
if (auto it = m_sfoParams.find(name.data()); it != m_sfoParams.end()) {
|
||||
if (it->second.first != 0x0204) return {};
|
||||
return {(const char*)it->second.second.data()};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
void SystemContent::init(std::filesystem::path const& root) {
|
||||
m_sfoParams = loadSFO(root);
|
||||
}
|
||||
|
||||
ISystemContent& accessSystemContent() {
|
||||
static SystemContent inst;
|
||||
return inst;
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace std::filesystem {
|
||||
class path;
|
||||
}
|
||||
|
||||
class ISystemContent {
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if the profided path contain "param.sfo" and reads it
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
virtual void init(std::filesystem::path const& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get an int value from the sfo
|
||||
*
|
||||
* @param name
|
||||
* @return std::optional<uint32_t>
|
||||
*/
|
||||
virtual std::optional<uint32_t> getInt(std::string_view name) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get an string value from the sfo
|
||||
*
|
||||
* @param name
|
||||
* @return std::optional<std::string_view>
|
||||
*/
|
||||
virtual std::optional<std::string_view> getString(std::string_view name) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL ISystemContent& accessSystemContent();
|
||||
#undef __APICALL
|
@ -1,5 +0,0 @@
|
||||
add_library(timer OBJECT
|
||||
timer.cpp
|
||||
)
|
||||
|
||||
add_dependencies(timer third_party boost)
|
@ -1,111 +0,0 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <windows.h>
|
||||
|
||||
namespace System {
|
||||
|
||||
struct SysTimeStruct {
|
||||
uint16_t Year; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Month; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Day; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Hour; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Minute; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Second; // NOLINT(readability-identifier-naming)
|
||||
uint16_t Milliseconds; // NOLINT(readability-identifier-naming)
|
||||
bool is_invalid; // NOLINT(readability-identifier-naming)
|
||||
};
|
||||
|
||||
struct SysFileTimeStruct {
|
||||
FILETIME time;
|
||||
bool is_invalid;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
inline void sys_file_to_system_time_utc(const SysFileTimeStruct& f, SysTimeStruct& t) {
|
||||
SYSTEMTIME s;
|
||||
|
||||
if (f.is_invalid || (FileTimeToSystemTime(&f.time, &s) == 0)) {
|
||||
t.is_invalid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
t.is_invalid = false;
|
||||
t.Year = s.wYear;
|
||||
t.Month = s.wMonth;
|
||||
t.Day = s.wDay;
|
||||
t.Hour = s.wHour;
|
||||
t.Minute = s.wMinute;
|
||||
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
|
||||
t.Milliseconds = s.wMilliseconds;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
inline void sys_time_t_to_system(time_t t, SysTimeStruct& s) {
|
||||
SysFileTimeStruct ft {};
|
||||
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
|
||||
ft.time.dwLowDateTime = static_cast<DWORD>(ll);
|
||||
ft.time.dwHighDateTime = static_cast<DWORD>(static_cast<uint64_t>(ll) >> 32u);
|
||||
ft.is_invalid = false;
|
||||
sys_file_to_system_time_utc(ft, s);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
inline void sys_system_to_file_time_utc(const SysTimeStruct& f, SysFileTimeStruct& t) {
|
||||
SYSTEMTIME s;
|
||||
|
||||
s.wYear = f.Year;
|
||||
s.wMonth = f.Month;
|
||||
s.wDay = f.Day;
|
||||
s.wHour = f.Hour;
|
||||
s.wMinute = f.Minute;
|
||||
s.wSecond = f.Second;
|
||||
s.wMilliseconds = f.Milliseconds;
|
||||
|
||||
t.is_invalid = (f.is_invalid || (SystemTimeToFileTime(&s, &t.time) == 0));
|
||||
}
|
||||
|
||||
// Retrieves the current local date and time
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
inline void sys_get_system_time(SysTimeStruct& t) {
|
||||
SYSTEMTIME s;
|
||||
GetLocalTime(&s);
|
||||
|
||||
t.is_invalid = false;
|
||||
t.Year = s.wYear;
|
||||
t.Month = s.wMonth;
|
||||
t.Day = s.wDay;
|
||||
t.Hour = s.wHour;
|
||||
t.Minute = s.wMinute;
|
||||
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
|
||||
t.Milliseconds = s.wMilliseconds;
|
||||
}
|
||||
|
||||
// Retrieves the current system date and time in Coordinated Universal Time (UTC).
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
inline void sys_get_system_time_utc(SysTimeStruct& t) {
|
||||
SYSTEMTIME s;
|
||||
GetSystemTime(&s);
|
||||
|
||||
t.is_invalid = false;
|
||||
t.Year = s.wYear;
|
||||
t.Month = s.wMonth;
|
||||
t.Day = s.wDay;
|
||||
t.Hour = s.wHour;
|
||||
t.Minute = s.wMinute;
|
||||
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
|
||||
t.Milliseconds = s.wMilliseconds;
|
||||
}
|
||||
|
||||
inline void sys_query_performance_frequency(uint64_t* freq) {
|
||||
LARGE_INTEGER f;
|
||||
QueryPerformanceFrequency(&f);
|
||||
*freq = f.QuadPart;
|
||||
}
|
||||
|
||||
inline void sys_query_performance_counter(uint64_t* counter) {
|
||||
LARGE_INTEGER c;
|
||||
QueryPerformanceCounter(&c);
|
||||
*counter = c.QuadPart;
|
||||
}
|
||||
|
||||
} // namespace System
|
@ -1,163 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "timer.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "SysWindowsTimer.h" // todo use boost
|
||||
|
||||
#include <boost/chrono.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace {
|
||||
void time2timespec(SceKernelTimespec* ts, double sec) {
|
||||
ts->tv_sec = static_cast<decltype(ts->tv_sec)>(sec);
|
||||
ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>((sec - static_cast<double>(ts->tv_sec)) * 1e9);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class Timer: public ITimer {
|
||||
uint64_t m_startTime;
|
||||
uint64_t m_PauseTime = 1;
|
||||
uint64_t m_freq = 0;
|
||||
|
||||
public:
|
||||
Timer() = default;
|
||||
|
||||
void init() final {
|
||||
System::sys_query_performance_counter(&m_startTime);
|
||||
m_PauseTime = 0;
|
||||
System::sys_query_performance_frequency(&m_freq);
|
||||
}
|
||||
|
||||
double getTimeS() final {
|
||||
if (m_PauseTime > 0) {
|
||||
return (static_cast<double>(m_PauseTime - m_startTime)) / static_cast<double>(m_freq);
|
||||
}
|
||||
|
||||
uint64_t currentTime = 0;
|
||||
System::sys_query_performance_counter(¤tTime);
|
||||
|
||||
return (static_cast<double>(currentTime - m_startTime)) / static_cast<double>(m_freq);
|
||||
}
|
||||
|
||||
double getTimeMs() final { return 1e3 * getTimeS(); }
|
||||
|
||||
uint64_t getTicks() final {
|
||||
if (m_PauseTime > 0) {
|
||||
return (m_PauseTime - m_startTime);
|
||||
}
|
||||
|
||||
uint64_t currentTime = 0;
|
||||
System::sys_query_performance_counter(¤tTime);
|
||||
return (currentTime - m_startTime);
|
||||
}
|
||||
|
||||
uint64_t queryPerformance() final {
|
||||
uint64_t ret = 0;
|
||||
System::sys_query_performance_counter(&ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pause() final { System::sys_query_performance_counter(&m_PauseTime); }
|
||||
|
||||
void resume() final {
|
||||
|
||||
uint64_t currentTime = 0;
|
||||
System::sys_query_performance_counter(¤tTime);
|
||||
|
||||
m_startTime += currentTime - m_PauseTime;
|
||||
m_PauseTime = 0;
|
||||
}
|
||||
|
||||
uint64_t getFrequency() final { return m_freq; }
|
||||
|
||||
bool getPaused() final { return m_PauseTime > 0; }
|
||||
|
||||
int getTime(SceKernelClockid id, SceKernelTimespec* tp) final;
|
||||
int getTimeRes(SceKernelClockid id, SceKernelTimespec* tp) final;
|
||||
int getTimeofDay(SceKernelTimeval* tp) final;
|
||||
};
|
||||
|
||||
ITimer& accessTimer() {
|
||||
static Timer inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
int Timer::getTime(SceKernelClockid id, SceKernelTimespec* tp) {
|
||||
if (tp == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
using namespace boost::chrono;
|
||||
auto func = [tp](auto const& now) {
|
||||
tp->tv_sec = time_point_cast<seconds>(now).time_since_epoch().count();
|
||||
tp->tv_nsec = time_point_cast<nanoseconds>(now).time_since_epoch().count() % 1000000000;
|
||||
return Ok;
|
||||
};
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 9:
|
||||
case 10: return func(system_clock::now());
|
||||
case 4:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13: return func(steady_clock::now());
|
||||
case 1:
|
||||
case 2:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8: {
|
||||
double const ts = accessTimer().getTimeS();
|
||||
time2timespec(tp, ts);
|
||||
return Ok;
|
||||
}
|
||||
case 14: return func(thread_clock::now());
|
||||
case 15: return func(process_cpu_clock::now());
|
||||
}
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
int Timer::getTimeRes(SceKernelClockid id, SceKernelTimespec* tp) {
|
||||
if (tp == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
using namespace boost::chrono;
|
||||
auto func = [tp](auto const& period) {
|
||||
tp->tv_sec = static_cast<long>(period.num / period.den);
|
||||
tp->tv_nsec = static_cast<long>((period.num % period.den) * 1e9 / period.den);
|
||||
return Ok;
|
||||
};
|
||||
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 9:
|
||||
case 10: return func(system_clock::period());
|
||||
case 4:
|
||||
case 11:
|
||||
case 12: return func(steady_clock::period());
|
||||
case 1:
|
||||
case 2:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8: {
|
||||
double const ts = 1.0 / accessTimer().getFrequency();
|
||||
time2timespec(tp, ts);
|
||||
return Ok;
|
||||
}
|
||||
case 14: return func(thread_clock::period());
|
||||
case 15: return func(process_cpu_clock::period());
|
||||
}
|
||||
return getErr(ErrCode::_EINVAL);
|
||||
}
|
||||
|
||||
int Timer::getTimeofDay(SceKernelTimeval* tp) {
|
||||
if (tp == nullptr) {
|
||||
return getErr(ErrCode::_EFAULT);
|
||||
}
|
||||
|
||||
using namespace boost::chrono;
|
||||
uint64_t const t = time_point_cast<microseconds>(system_clock::now()).time_since_epoch().count();
|
||||
micro2timeval(tp, t);
|
||||
return Ok;
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
#pragma once
|
||||
#include "modules_include/common.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
class ITimer {
|
||||
CLASS_NO_COPY(ITimer);
|
||||
|
||||
protected:
|
||||
ITimer() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief init the timer. Sets start time for relative time
|
||||
*
|
||||
*/
|
||||
virtual void init() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the relative ticks
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
virtual uint64_t getTicks() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the relative Time in secounds
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
virtual double getTimeS() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the relative Time in ms
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
virtual double getTimeMs() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the absolute ticks
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
virtual uint64_t queryPerformance() = 0;
|
||||
|
||||
/**
|
||||
* @brief Pauses the relative timer
|
||||
*
|
||||
*/
|
||||
virtual void pause() = 0;
|
||||
|
||||
/**
|
||||
* @brief Resumes the relative timer
|
||||
*
|
||||
*/
|
||||
virtual void resume() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the Frequency of the ticks
|
||||
*
|
||||
* @return uint64_t
|
||||
*/
|
||||
virtual uint64_t getFrequency() = 0;
|
||||
|
||||
/**
|
||||
* @brief Check if paused
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
virtual bool getPaused() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the Time from clockId
|
||||
*
|
||||
* @param id
|
||||
* @param tp
|
||||
* @return int getErr()
|
||||
*/
|
||||
virtual int getTime(SceKernelClockid id, SceKernelTimespec* tp) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the Time Resolution from clockId
|
||||
*
|
||||
* @param id
|
||||
* @param tp
|
||||
* @return int getErr()
|
||||
*/
|
||||
virtual int getTimeRes(SceKernelClockid id, SceKernelTimespec* tp) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the timeOfDay
|
||||
*
|
||||
* @param tp
|
||||
* @return int getErr()
|
||||
*/
|
||||
virtual int getTimeofDay(SceKernelTimeval* tp) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL ITimer& accessTimer();
|
||||
#undef __APICALL
|
@ -1,4 +0,0 @@
|
||||
enable_language(C ASM_MASM)
|
||||
add_library(unwinding OBJECT
|
||||
unwind.asm
|
||||
)
|
@ -1,48 +0,0 @@
|
||||
.CODE
|
||||
|
||||
PUBLIC unwinding_setjmp
|
||||
unwinding_setjmp PROC
|
||||
MOV RAX, RDI
|
||||
MOV RDX ,qword ptr [RSP ]
|
||||
MOV qword ptr [RAX ],RDX
|
||||
MOV qword ptr [RAX + 8h ],RBX
|
||||
MOV qword ptr [RAX + 10h ],RSP
|
||||
MOV qword ptr [RAX + 18h ],RBP
|
||||
MOV qword ptr [RAX + 20h ],R12
|
||||
MOV qword ptr [RAX + 28h ],R13
|
||||
MOV qword ptr [RAX + 30h ],R14
|
||||
MOV qword ptr [RAX + 38h ],R15
|
||||
|
||||
FNSTCW word ptr [RAX + 40h ]
|
||||
STMXCSR dword ptr [RAX + 44h ]
|
||||
mov rax, 1
|
||||
ret
|
||||
unwinding_setjmp ENDP
|
||||
|
||||
PUBLIC unwinding_longjmp
|
||||
unwinding_longjmp PROC
|
||||
MOV RDX ,RDI
|
||||
STMXCSR dword ptr [RSP - 4h ]
|
||||
MOV EAX ,dword ptr [RDX + 44h ]
|
||||
AND EAX , 0ffffffc0h
|
||||
MOV EDI,dword ptr [RSP - 4h ]
|
||||
AND EDI ,3fh
|
||||
XOR EDI ,EAX
|
||||
MOV dword ptr [RSP - 4h ],EDI
|
||||
LDMXCSR dword ptr [RSP - 4h ]
|
||||
|
||||
MOV RCX ,qword ptr [RDX ]
|
||||
MOV RBX ,qword ptr [RDX + 8h ]
|
||||
MOV RSP ,qword ptr [RDX + 10h ]
|
||||
MOV RBP ,qword ptr [RDX + 18h ]
|
||||
MOV R12 ,qword ptr [RDX + 20h ]
|
||||
MOV R13 ,qword ptr [RDX + 28h ]
|
||||
MOV R14 ,qword ptr [RDX + 30h ]
|
||||
MOV R15 ,qword ptr [RDX + 38h ]
|
||||
FLDCW word ptr [RDX + 40h ]
|
||||
MOV RAX ,0
|
||||
|
||||
MOV qword ptr [RSP ],RCX
|
||||
ret
|
||||
unwinding_longjmp ENDP
|
||||
End
|
@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <array>
|
||||
#include <stdint.h>
|
||||
|
||||
constexpr int _JBLEN_AMD = 20; /* Size of the jmp_buf on AMD64. */
|
||||
|
||||
struct unwinding_jmp_buf {
|
||||
uint64_t _jb[_JBLEN_AMD];
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
SYSV_ABI bool unwinding_setjmp(unwinding_jmp_buf* data);
|
||||
|
||||
SYSV_ABI void unwinding_longjmp(unwinding_jmp_buf* data);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
add_library(videoout OBJECT
|
||||
videoout.cpp
|
||||
vulkan/vulkanSetup.cpp
|
||||
vulkan/vulkanHelper.cpp
|
||||
imageHandler.cpp
|
||||
)
|
||||
|
||||
add_dependencies(videoout third_party psOff_utility initParams imports boost)
|
||||
target_include_directories(videoout PRIVATE
|
||||
${Vulkan_INCLUDE_DIRS}
|
||||
${PRJ_SRC_DIR}/third_party/optick/src
|
||||
${PRJ_SRC_DIR}/third_party/magic_enum/include
|
||||
${PRJ_SRC_DIR}/third_party/SDL2/include
|
||||
)
|
@ -1,269 +0,0 @@
|
||||
#include "imageHandler.h"
|
||||
|
||||
#include "core/initParams/initParams.h"
|
||||
#include "logging.h"
|
||||
#include "vulkan/vulkanSetup.h"
|
||||
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <vulkan/vk_enum_string_helper.h>
|
||||
|
||||
LOG_DEFINE_MODULE(ImageHandler);
|
||||
|
||||
constexpr uint32_t NUM_DISPLAY_BUFFERS = 3;
|
||||
|
||||
class ImageHandler: public IImageHandler {
|
||||
uint32_t m_maxImages = 1;
|
||||
|
||||
VkDevice m_device;
|
||||
|
||||
// Vulkan internal
|
||||
VkSwapchainKHR m_swapchain = nullptr;
|
||||
VkCommandPool m_commandPool = nullptr;
|
||||
|
||||
std::vector<VkSemaphore> m_semsImageReady;
|
||||
std::vector<VkSemaphore> m_semsImageCopied;
|
||||
std::vector<VkCommandBuffer> m_commandBuffer;
|
||||
|
||||
std::vector<VkFence> m_fenceSubmit;
|
||||
|
||||
size_t m_nextIndex = 0;
|
||||
|
||||
uint32_t m_countImages = 0;
|
||||
|
||||
std::vector<VkImage> m_scImages;
|
||||
// - vulkan
|
||||
|
||||
boost::mutex m_mutexInt;
|
||||
|
||||
size_t m_waitId = 0;
|
||||
size_t m_presentCount = 1;
|
||||
|
||||
boost::condition_variable m_present_condVar; /// regulates number of images
|
||||
|
||||
bool m_stop = false;
|
||||
|
||||
public:
|
||||
ImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue): IImageHandler(extentWindow, queue), m_device(vkDevice) {};
|
||||
|
||||
virtual ~ImageHandler() = default;
|
||||
|
||||
// ### Interace
|
||||
void init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) final;
|
||||
void deinit() final;
|
||||
|
||||
void stop() final {
|
||||
m_stop = true;
|
||||
m_present_condVar.notify_all();
|
||||
}
|
||||
|
||||
std::optional<ImageData> getImage_blocking() final;
|
||||
|
||||
void notify_done(ImageData const&) final;
|
||||
|
||||
VkSwapchainKHR getSwapchain() const final { return m_swapchain; }
|
||||
};
|
||||
|
||||
std::unique_ptr<IImageHandler> createImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue) {
|
||||
return std::make_unique<ImageHandler>(vkDevice, extentWindow, queue);
|
||||
}
|
||||
|
||||
std::optional<ImageData> ImageHandler::getImage_blocking() {
|
||||
LOG_USE_MODULE(ImageHandler);
|
||||
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
// Regulate max images
|
||||
auto waitId = ++m_waitId;
|
||||
LOG_DEBUG(L"waitId:%llu presentCount:%llu", waitId, m_presentCount);
|
||||
m_present_condVar.wait(lock, [this, &waitId] { return m_stop || (waitId == m_presentCount && m_countImages < m_maxImages); });
|
||||
if (m_stop) return {};
|
||||
// -
|
||||
|
||||
ImageData imageData {
|
||||
.semImageReady = m_semsImageReady[m_nextIndex],
|
||||
.semImageCopied = m_semsImageCopied[m_nextIndex],
|
||||
.cmdBuffer = m_commandBuffer[m_nextIndex],
|
||||
.submitFence = m_fenceSubmit[m_nextIndex],
|
||||
};
|
||||
|
||||
// Get swapchain image
|
||||
{
|
||||
int numTries = 2;
|
||||
|
||||
VkResult result = VK_SUCCESS;
|
||||
for (; numTries >= 0; --numTries) {
|
||||
if (result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, imageData.semImageReady, VK_NULL_HANDLE, &imageData.index); result != VK_SUCCESS) {
|
||||
if (result == VK_NOT_READY) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (numTries <= 0) {
|
||||
LOG_ERR(L"vkAcquireNextImageKHR %S", string_VkResult(result));
|
||||
++m_presentCount; // fake present
|
||||
lock.unlock();
|
||||
m_present_condVar.notify_all();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
// - swapchain image
|
||||
|
||||
m_nextIndex = (++m_nextIndex) % m_maxImages;
|
||||
++m_countImages;
|
||||
|
||||
imageData.swapchainImage = m_scImages[imageData.index];
|
||||
|
||||
imageData.extent = m_extentWindow;
|
||||
|
||||
{ // Begin commandbuffer| end is done by user
|
||||
VkCommandBufferBeginInfo const beginInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = 0,
|
||||
.pInheritanceInfo = nullptr,
|
||||
};
|
||||
|
||||
if (auto result = vkBeginCommandBuffer(imageData.cmdBuffer, &beginInfo); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Error vkBeginCommandBuffer: %S", string_VkResult(result));
|
||||
}
|
||||
}
|
||||
|
||||
return imageData;
|
||||
}
|
||||
|
||||
void ImageHandler::notify_done(ImageData const& imageData) {
|
||||
LOG_USE_MODULE(ImageHandler);
|
||||
|
||||
// vkQueuePresentKHR: amd waits and nvidia doesn't -> manual wait
|
||||
vkWaitForFences(m_device, 1, &imageData.submitFence, VK_TRUE, UINT64_MAX);
|
||||
vkResetFences(m_device, 1, &imageData.submitFence);
|
||||
|
||||
vkResetCommandBuffer(imageData.cmdBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
|
||||
|
||||
boost::unique_lock lock(m_mutexInt);
|
||||
|
||||
++m_presentCount;
|
||||
--m_countImages;
|
||||
lock.unlock();
|
||||
|
||||
m_present_condVar.notify_all();
|
||||
}
|
||||
|
||||
void ImageHandler::init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) {
|
||||
LOG_USE_MODULE(ImageHandler);
|
||||
|
||||
{ // Create Display ready sems and fences
|
||||
VkSemaphoreCreateInfo const semCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
.pNext = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
VkFenceCreateInfo const fenceCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||
.pNext = 0,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
m_semsImageReady.resize(m_maxImages);
|
||||
m_semsImageCopied.resize(m_maxImages);
|
||||
m_fenceSubmit.resize(m_maxImages);
|
||||
|
||||
for (size_t n = 0; n < m_maxImages; ++n) {
|
||||
vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &m_semsImageReady[n]);
|
||||
vkCreateSemaphore(m_device, &semCreateInfo, nullptr, &m_semsImageCopied[n]);
|
||||
vkCreateFence(obj->deviceInfo.device, &fenceCreateInfo, nullptr, &m_fenceSubmit[n]);
|
||||
}
|
||||
}
|
||||
|
||||
auto numBuffers = NUM_DISPLAY_BUFFERS;
|
||||
|
||||
{ // swapchain
|
||||
numBuffers = std::clamp(numBuffers, obj->surfaceCapabilities.capabilities.minImageCount, obj->surfaceCapabilities.capabilities.maxImageCount);
|
||||
|
||||
m_extentWindow.width = std::clamp(m_extentWindow.width, obj->surfaceCapabilities.capabilities.minImageExtent.width,
|
||||
obj->surfaceCapabilities.capabilities.maxImageExtent.width);
|
||||
m_extentWindow.height = std::clamp(m_extentWindow.height, obj->surfaceCapabilities.capabilities.minImageExtent.height,
|
||||
obj->surfaceCapabilities.capabilities.maxImageExtent.height);
|
||||
|
||||
auto [displayFormat, displayColorSpace] = getDisplayFormat(obj);
|
||||
m_imageFormat = displayFormat;
|
||||
|
||||
bool const useVsync = accessInitParams()->useVSYNC();
|
||||
|
||||
VkSwapchainCreateInfoKHR const createInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.surface = surface,
|
||||
.minImageCount = numBuffers,
|
||||
.imageFormat = m_imageFormat,
|
||||
.imageColorSpace = displayColorSpace,
|
||||
.imageExtent = m_extentWindow,
|
||||
.imageArrayLayers = 1,
|
||||
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
||||
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.queueFamilyIndexCount = 0,
|
||||
.pQueueFamilyIndices = nullptr,
|
||||
.preTransform = obj->surfaceCapabilities.capabilities.currentTransform,
|
||||
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
.presentMode = useVsync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||
.clipped = VK_TRUE,
|
||||
.oldSwapchain = nullptr,
|
||||
};
|
||||
|
||||
vkCreateSwapchainKHR(obj->deviceInfo.device, &createInfo, nullptr, &m_swapchain);
|
||||
}
|
||||
|
||||
{ // swapchain images
|
||||
uint32_t numImages = 0;
|
||||
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numImages, nullptr);
|
||||
m_scImages.resize(numImages);
|
||||
|
||||
vkGetSwapchainImagesKHR(m_device, m_swapchain, &numImages, m_scImages.data());
|
||||
}
|
||||
|
||||
{ // Command buffer
|
||||
|
||||
VkCommandPoolCreateInfo const poolInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||
.queueFamilyIndex = m_queue->family,
|
||||
};
|
||||
|
||||
if (auto result = vkCreateCommandPool(obj->deviceInfo.device, &poolInfo, nullptr, &m_commandPool); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Couldn't create commandpool(graphics): %d", result);
|
||||
}
|
||||
|
||||
m_commandBuffer.resize(m_maxImages);
|
||||
VkCommandBufferAllocateInfo const allocInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = m_commandPool,
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = m_maxImages,
|
||||
};
|
||||
|
||||
if (auto result = vkAllocateCommandBuffers(obj->deviceInfo.device, &allocInfo, m_commandBuffer.data()); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Couldn't create commandbuffers(graphics): %d", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImageHandler::deinit() {
|
||||
printf("deinit ImageHandler\n");
|
||||
|
||||
for (auto& sem: m_semsImageReady) {
|
||||
vkDestroySemaphore(m_device, sem, nullptr);
|
||||
}
|
||||
for (auto& sem: m_semsImageCopied) {
|
||||
vkDestroySemaphore(m_device, sem, nullptr);
|
||||
}
|
||||
for (auto& fence: m_fenceSubmit) {
|
||||
vkDestroyFence(m_device, fence, nullptr);
|
||||
}
|
||||
|
||||
if (m_commandPool != nullptr) vkDestroyCommandPool(m_device, m_commandPool, nullptr);
|
||||
if (m_swapchain != nullptr) vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
|
||||
|
||||
printf("deinit ImageHandler| done\n");
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
#pragma once
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <optional>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
struct ImageData {
|
||||
VkImage swapchainImage = nullptr;
|
||||
VkSemaphore semImageReady = nullptr; /// image is ready for operation
|
||||
VkSemaphore semImageCopied = nullptr; /// Image has been copied, ready to swap
|
||||
VkCommandBuffer cmdBuffer = nullptr; /// Commandbuffer to be used for transer etc. Already called begin!
|
||||
|
||||
VkFence submitFence; /// used for the cmdBuffer queue submit
|
||||
|
||||
VkExtent2D extent;
|
||||
uint32_t index = 0;
|
||||
};
|
||||
|
||||
namespace vulkan {
|
||||
struct VulkanObj;
|
||||
struct QueueInfo;
|
||||
} // namespace vulkan
|
||||
|
||||
class IImageHandler {
|
||||
CLASS_NO_COPY(IImageHandler);
|
||||
CLASS_NO_MOVE(IImageHandler);
|
||||
|
||||
protected:
|
||||
IImageHandler(VkExtent2D extentWindow, vulkan::QueueInfo* queue): m_extentWindow(extentWindow), m_queue(queue) {}
|
||||
|
||||
VkExtent2D m_extentWindow;
|
||||
VkFormat m_imageFormat;
|
||||
|
||||
vulkan::QueueInfo* m_queue;
|
||||
|
||||
public:
|
||||
virtual ~IImageHandler() = default;
|
||||
|
||||
auto getExtent() const { return m_extentWindow; }
|
||||
|
||||
auto getFormat() const { return m_imageFormat; }
|
||||
|
||||
auto getQueue() const { return m_queue; }
|
||||
|
||||
virtual void init(vulkan::VulkanObj* obj, VkSurfaceKHR surface) = 0;
|
||||
|
||||
virtual void deinit() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual std::optional<ImageData> getImage_blocking() = 0;
|
||||
virtual void notify_done(ImageData const&) = 0;
|
||||
|
||||
virtual VkSwapchainKHR getSwapchain() const = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<IImageHandler> createImageHandler(VkDevice vkDevice, VkExtent2D extentWindow, vulkan::QueueInfo* queue);
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get the required memory alignment. Dont' call from constructor.
|
||||
*
|
||||
* @param format
|
||||
* @param extent shouldn't matter if not 100% correct
|
||||
* @return memory alignment
|
||||
*/
|
||||
|
||||
__APICALL uint64_t getImageAlignment(VkFormat format, VkExtent3D const& extent);
|
||||
#undef __APICALL
|
@ -1,23 +0,0 @@
|
||||
## Video out
|
||||
|
||||
* Manages the display buffers used in Linux/PlayStation.
|
||||
* Setup of Vulkan (GPU detection etc.)
|
||||
* Emits Kernel events: flip, vblank
|
||||
|
||||
<div align="center">
|
||||
|
||||
![](../../out/docs/uml/modules/videoout_class.svg)
|
||||
</div>
|
||||
|
||||
### Swapchain image handling
|
||||
|
||||
User calls getImage_blocking() to retrieve the display image. After present notify_done() is called.
|
||||
|
||||
getImage_blocking() releases users FIFO. The whole swapchain images are handled by ImageHandler
|
||||
|
||||
|
||||
<div align="center">
|
||||
|
||||
![](../../out/docs/uml/modules/videout_swapchain.svg)
|
||||
|
||||
</div>
|
@ -1,808 +0,0 @@
|
||||
#define __APICALL_EXTERN
|
||||
#include "videoout.h"
|
||||
|
||||
#include "intern.h"
|
||||
#undef __APICALL_EXTERN
|
||||
|
||||
#include "core/imports/exports/graphics.h"
|
||||
#include "core/imports/imports_func.h"
|
||||
#include "core/initParams/initParams.h"
|
||||
#include "core/kernel/eventqueue.h"
|
||||
#include "core/systemContent/systemContent.h"
|
||||
#include "core/timer/timer.h"
|
||||
#include "imageHandler.h"
|
||||
#include "logging.h"
|
||||
#include "modules/libSceVideoOut/codes.h"
|
||||
#include "modules/libSceVideoOut/types.h"
|
||||
#include "modules_include/common.h"
|
||||
#include "vulkan/vulkanHelper.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <assert.h>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optick.h>
|
||||
#include <thread>
|
||||
|
||||
LOG_DEFINE_MODULE(VideoOut);
|
||||
using namespace Kernel;
|
||||
|
||||
namespace {
|
||||
size_t constexpr WindowsMAX = 2;
|
||||
|
||||
std::pair<uint32_t, uint32_t> getDisplayBufferSize(uint32_t width, uint32_t height, uint32_t pitch, bool tile, bool neo) {
|
||||
if (pitch == 3840) {
|
||||
if (width == 3840 && height == 2160 && tile && !neo) {
|
||||
return {33423360, 32768};
|
||||
}
|
||||
if (width == 3840 && height == 2160 && tile && neo) {
|
||||
return {33423360, 65536};
|
||||
}
|
||||
if (width == 3840 && height == 2160 && !tile && !neo) {
|
||||
return {33177600, 256};
|
||||
}
|
||||
if (width == 3840 && height == 2160 && !tile && neo) {
|
||||
return {33177600, 256};
|
||||
}
|
||||
}
|
||||
|
||||
if (pitch == 1920) {
|
||||
if (width == 1920 && height == 1080 && tile && !neo) {
|
||||
return {8355840, 32768};
|
||||
}
|
||||
if (width == 1920 && height == 1080 && tile && neo) {
|
||||
return {8847360, 65536};
|
||||
}
|
||||
if (width == 1920 && height == 1080 && !tile && !neo) {
|
||||
return {8294400, 256};
|
||||
}
|
||||
if (width == 1920 && height == 1080 && !tile && neo) {
|
||||
return {8294400, 256};
|
||||
}
|
||||
}
|
||||
|
||||
if (pitch == 1280) {
|
||||
if (width == 1280 && height == 720 && tile && !neo) {
|
||||
return {3932160, 32768};
|
||||
}
|
||||
if (width == 1280 && height == 720 && tile && neo) {
|
||||
return {3932160, 65536};
|
||||
}
|
||||
if (width == 1280 && height == 720 && !tile && !neo) {
|
||||
return {3686400, 256};
|
||||
}
|
||||
if (width == 1280 && height == 720 && !tile && neo) {
|
||||
return {3686400, 256};
|
||||
}
|
||||
}
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
struct VideoOutConfig {
|
||||
SceVideoOutFlipStatus flipStatus;
|
||||
SceVideoOutVblankStatus vblankStatus;
|
||||
SceVideoOutResolutionStatus resolution;
|
||||
|
||||
std::array<vulkan::SwapchainData, 16> bufferSets;
|
||||
|
||||
std::array<int32_t, 16> buffers; // index to bufferSets
|
||||
|
||||
uint8_t buffersSetsCount = 0;
|
||||
|
||||
double fps = 0.0;
|
||||
|
||||
VideoOutConfig() { std::fill(buffers.begin(), buffers.end(), -1); }
|
||||
};
|
||||
|
||||
enum class FlipRate {
|
||||
_60Hz,
|
||||
_30Hz,
|
||||
_20Hz,
|
||||
};
|
||||
|
||||
struct Context {
|
||||
int userId = -1;
|
||||
FlipRate fliprate = FlipRate::_60Hz;
|
||||
|
||||
VideoOutConfig config;
|
||||
SDL_Window* window;
|
||||
VkSurfaceKHR surface = nullptr;
|
||||
|
||||
std::list<EventQueue::IKernelEqueue_t> eventFlip;
|
||||
std::list<EventQueue::IKernelEqueue_t> eventVblank;
|
||||
};
|
||||
|
||||
enum class MessageType { open, close, flip };
|
||||
|
||||
struct Message {
|
||||
MessageType type;
|
||||
int handle = -1;
|
||||
bool* done = nullptr;
|
||||
|
||||
int index = 0;
|
||||
uint32_t setIndex = 0;
|
||||
|
||||
ImageData imageData = {};
|
||||
};
|
||||
|
||||
std::string getTitle(int handle, uint64_t frame, size_t fps, FlipRate maxFPS) {
|
||||
static auto title = [] {
|
||||
auto title = accessSystemContent().getString("TITLE");
|
||||
if (title) return title.value().data();
|
||||
return "psOFF";
|
||||
}();
|
||||
|
||||
return std::format("{}({}): frame={} fps={}(locked:{})", title, handle, frame, fps, magic_enum::enum_name(maxFPS).data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class VideoOut: public IVideoOut, private IEventsGraphics {
|
||||
std::array<Context, WindowsMAX> m_windows;
|
||||
|
||||
uint32_t const m_widthTotal = 1920, m_heightTotal = 1080; // todo: make config
|
||||
|
||||
mutable std::mutex m_mutexInt;
|
||||
vulkan::VulkanObj* m_vulkanObj = nullptr;
|
||||
|
||||
std::unique_ptr<IGraphics> m_graphics;
|
||||
std::unique_ptr<IImageHandler> m_imageHandler;
|
||||
|
||||
std::thread m_threadSDL2;
|
||||
std::condition_variable m_condSDL2;
|
||||
std::condition_variable m_condDone;
|
||||
bool m_stop = false;
|
||||
std::queue<Message> m_messages;
|
||||
|
||||
uint64_t m_vblankTime = (uint64_t)(1e6 / 59.0); // in us
|
||||
|
||||
void vblankEnd(int handle, uint64_t curTime, uint64_t curProcTime);
|
||||
|
||||
// Callback Graphics
|
||||
void eventDoFlip(int handle, int index, int64_t flipArg, VkSemaphore waitSema, size_t waitValue) final {
|
||||
OPTICK_EVENT();
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
|
||||
m_graphics->submited();
|
||||
|
||||
auto& window = m_windows[handle - 1];
|
||||
uint32_t const setIndex = window.config.buffers[index];
|
||||
|
||||
auto& swapchain = window.config.bufferSets[setIndex];
|
||||
auto& displayBufferMeta = swapchain.buffers[index];
|
||||
|
||||
auto imageData = m_imageHandler->getImage_blocking();
|
||||
|
||||
auto& flipStatus = window.config.flipStatus;
|
||||
++flipStatus.gcQueueNum;
|
||||
flipStatus.flipArg = flipArg;
|
||||
|
||||
auto& timer = accessTimer();
|
||||
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
|
||||
|
||||
flipStatus.submitTsc = curTime;
|
||||
|
||||
if (!imageData) {
|
||||
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
|
||||
m_graphics->submitDone();
|
||||
doFlip(window, handle);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(L"-> eventDoFlip(%d):%u %d", handle, setIndex, index);
|
||||
transferDisplay(*imageData, displayBufferMeta, waitSema, waitValue);
|
||||
|
||||
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
|
||||
|
||||
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, *imageData});
|
||||
lock.unlock();
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
LOG_DEBUG(L"<- eventDoFlip(%d):%u %d", handle, setIndex, index);
|
||||
}
|
||||
|
||||
vulkan::QueueInfo* getQueue(vulkan::QueueType type) final;
|
||||
// -
|
||||
|
||||
void transferDisplay(ImageData const& imageData, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema, size_t waitValue);
|
||||
|
||||
std::thread createSDLThread();
|
||||
|
||||
void doFlip(Context& ctx, int handle);
|
||||
|
||||
public:
|
||||
VideoOut() = default;
|
||||
|
||||
virtual ~VideoOut();
|
||||
|
||||
/**
|
||||
* @brief Preinit handle 1(index 0) with main window
|
||||
* Needed for early vulkan init and with it all the managers
|
||||
*/
|
||||
void init() final;
|
||||
int open(int userId) final;
|
||||
void close(int handle) final;
|
||||
|
||||
void setFliprate(int handle, int rate) final {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_INFO(L"Fliprate:%d", rate);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
m_windows[handle - 1].fliprate = (FlipRate)rate;
|
||||
|
||||
switch ((FlipRate)rate) {
|
||||
case FlipRate::_60Hz: m_vblankTime = (uint64_t)(1e6 / 59.0); break;
|
||||
case FlipRate::_30Hz: m_vblankTime = (uint64_t)(1e6 / 29.0); break;
|
||||
case FlipRate::_20Hz: m_vblankTime = (uint64_t)(1e6 / 19.0); break;
|
||||
}
|
||||
}
|
||||
|
||||
vulkan::DeviceInfo* getDeviceInfo() final { return &m_vulkanObj->deviceInfo; }
|
||||
|
||||
int addEvent(int handle, EventQueue::KernelEqueueEvent const& event, Kernel::EventQueue::IKernelEqueue_t eq) final;
|
||||
void removeEvent(int handle, Kernel::EventQueue::IKernelEqueue_t eq, int const ident) final;
|
||||
void submitFlip(int handle, int index, int64_t flipArg) final; // -> Renderer
|
||||
|
||||
void getFlipStatus(int handle, void* status) final;
|
||||
void getVBlankStatus(int handle, void* status) final;
|
||||
|
||||
void getResolution(int handle, void* status) final;
|
||||
|
||||
void getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
|
||||
uint32_t pitch_in_pixel) final;
|
||||
|
||||
int registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) final;
|
||||
|
||||
int getPendingFlips(int handle) final {
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
|
||||
auto& flipStatus = m_windows[handle - 1].config.flipStatus;
|
||||
return flipStatus.gcQueueNum;
|
||||
}
|
||||
|
||||
IGraphics* getGraphics() final {
|
||||
assert(m_graphics);
|
||||
return m_graphics.get();
|
||||
};
|
||||
|
||||
std::unique_lock<std::mutex> getSDLLock() const final { return std::unique_lock(m_mutexInt); }
|
||||
|
||||
// ### Gpu memory forwards
|
||||
bool isGPULocal(uint64_t vaddr) final { return m_graphics->isGPULocal(vaddr); }
|
||||
|
||||
bool notify_allocHeap(uint64_t vaddr, uint64_t size, int memoryProtection) final { return m_graphics->notify_allocHeap(vaddr, size, memoryProtection); }
|
||||
|
||||
// --- Gpu memory forwards
|
||||
};
|
||||
|
||||
IVideoOut& accessVideoOut() {
|
||||
static VideoOut inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
VideoOut::~VideoOut() {
|
||||
// Logging doesn't work here, even with flush
|
||||
printf("shutting down VideoOut\n");
|
||||
m_stop = true;
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
// printf("VideoOut| waiting on gpu idle\n");
|
||||
m_imageHandler->stop();
|
||||
m_graphics->stop();
|
||||
vkQueueWaitIdle(m_imageHandler->getQueue()->queue);
|
||||
|
||||
// shutdown graphics first (uses vulkan)
|
||||
m_graphics->deinit();
|
||||
m_graphics.reset();
|
||||
|
||||
// printf("VideoOut| Destroy surface\n");
|
||||
// for (auto& window: m_windows) {
|
||||
// if (window.userId < 0) continue;
|
||||
// if (window.surface != nullptr) vkDestroySurfaceKHR(m_vulkanObj->deviceInfo.instance, window.surface, nullptr);
|
||||
// }
|
||||
|
||||
m_imageHandler->deinit();
|
||||
m_imageHandler.reset();
|
||||
|
||||
printf("VideoOut| Destroy vulkan\n");
|
||||
|
||||
deinitVulkan(m_vulkanObj);
|
||||
|
||||
printf("shutdown VideoOut done\n");
|
||||
}
|
||||
|
||||
void VideoOut::init() {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_DEBUG(L"createSDLThread()");
|
||||
m_threadSDL2 = createSDLThread();
|
||||
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
static bool done = false;
|
||||
m_messages.push(Message {MessageType::open, 0, &done});
|
||||
lock.unlock();
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
lock.lock();
|
||||
m_condDone.wait(lock, [=] { return done; });
|
||||
}
|
||||
|
||||
int VideoOut::open(int userId) {
|
||||
OPTICK_THREAD("libVideOut");
|
||||
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
|
||||
// Get Free handle
|
||||
int const windowIndex = [&] {
|
||||
for (int n = 0; n < m_windows.size(); ++n) {
|
||||
if (m_windows[n].userId < 0) {
|
||||
m_windows[n].userId = userId;
|
||||
m_windows[n].config.flipStatus = {};
|
||||
m_windows[n].config.vblankStatus = {};
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
// -
|
||||
}();
|
||||
// -
|
||||
|
||||
if (windowIndex == 0) return 1; // Special case windows[0] is mainWindow
|
||||
|
||||
// Create new window
|
||||
static bool done = false;
|
||||
m_messages.push(Message {MessageType::open, windowIndex, &done});
|
||||
lock.unlock();
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
lock.lock();
|
||||
m_condDone.wait(lock, [=] { return done; });
|
||||
return 1 + windowIndex; // handle starts with 1
|
||||
}
|
||||
|
||||
void VideoOut::close(int handle) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_INFO(L"<-- VideoOut Close(%d)", handle);
|
||||
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
|
||||
auto& window = m_windows[handle - 1];
|
||||
window.userId = -1;
|
||||
for (auto& item: window.eventFlip) {
|
||||
if (item != nullptr) {
|
||||
(void)item->deleteEvent(VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
|
||||
}
|
||||
}
|
||||
for (auto& item: window.eventVblank) {
|
||||
if (item != nullptr) {
|
||||
(void)item->deleteEvent(VIDEO_OUT_EVENT_VBLANK, EventQueue::KERNEL_EVFILT_VIDEO_OUT);
|
||||
}
|
||||
}
|
||||
|
||||
window.eventFlip.clear();
|
||||
window.eventVblank.clear();
|
||||
|
||||
static bool done = false;
|
||||
m_messages.push(Message {MessageType::close, handle, &done});
|
||||
lock.unlock();
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
lock.lock();
|
||||
m_condDone.wait(lock, [=] { return done; });
|
||||
}
|
||||
|
||||
int VideoOut::addEvent(int handle, EventQueue::KernelEqueueEvent const& event, EventQueue::IKernelEqueue_t eq) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
|
||||
if (eq == nullptr) return getErr(ErrCode::_EINVAL);
|
||||
|
||||
int result = eq->addEvent(event);
|
||||
switch (event.event.ident) {
|
||||
case VIDEO_OUT_EVENT_FLIP: {
|
||||
LOG_DEBUG(L"+VIDEO_OUT_EVENT_FLIP(%d)", handle);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
m_windows[handle - 1].eventFlip.push_back(eq);
|
||||
} break;
|
||||
case VIDEO_OUT_EVENT_VBLANK: {
|
||||
LOG_DEBUG(L"+VIDEO_OUT_EVENT_VBLANK(%d)", handle);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
m_windows[handle - 1].eventVblank.push_back(eq);
|
||||
} break;
|
||||
default: LOG_ERR(L"undefinded:%d", event.event.ident); result = -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void VideoOut::removeEvent(int handle, Kernel::EventQueue::IKernelEqueue_t eq, int const ident) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
|
||||
switch (ident) {
|
||||
case VIDEO_OUT_EVENT_FLIP: {
|
||||
LOG_DEBUG(L"-VIDEO_OUT_EVENT_FLIP(%d)", handle);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
m_windows[handle - 1].eventFlip.remove(eq);
|
||||
} break;
|
||||
case VIDEO_OUT_EVENT_VBLANK: {
|
||||
LOG_DEBUG(L"-VIDEO_OUT_EVENT_VBLANK(%d)", handle);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
m_windows[handle - 1].eventVblank.remove(eq);
|
||||
} break;
|
||||
default: LOG_CRIT(L"undefinded:%d", ident);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoOut::transferDisplay(ImageData const& imageData, vulkan::SwapchainData::DisplayBuffers& displayBufferMeta, VkSemaphore waitSema, size_t waitValue) {
|
||||
|
||||
vulkan::transfer2Display(&displayBufferMeta, imageData, m_graphics.get());
|
||||
vulkan::submitDisplayTransfer(&displayBufferMeta, imageData, m_imageHandler->getQueue(), waitSema, waitValue);
|
||||
}
|
||||
|
||||
void VideoOut::submitFlip(int handle, int index, int64_t flipArg) {
|
||||
OPTICK_EVENT();
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
|
||||
m_graphics->submited(); // increase internal counter (wait for flip)
|
||||
|
||||
auto& window = m_windows[handle - 1];
|
||||
uint32_t const setIndex = window.config.buffers[index];
|
||||
|
||||
auto& swapchain = window.config.bufferSets[setIndex];
|
||||
auto& displayBufferMeta = swapchain.buffers[index];
|
||||
|
||||
LOG_DEBUG(L"-> submitFlip(%d):%u %d", handle, setIndex, index);
|
||||
|
||||
auto imageData = m_imageHandler->getImage_blocking();
|
||||
|
||||
auto& flipStatus = window.config.flipStatus;
|
||||
++flipStatus.gcQueueNum;
|
||||
flipStatus.flipArg = flipArg;
|
||||
|
||||
auto& timer = accessTimer();
|
||||
auto const curTime = (uint64_t)(1e9 * timer.getTimeS());
|
||||
|
||||
flipStatus.submitTsc = curTime;
|
||||
if (!imageData) {
|
||||
LOG_ERR(L"<- submitFlip(%d):%u %d error", handle, setIndex, index);
|
||||
m_graphics->submitDone();
|
||||
doFlip(window, handle);
|
||||
return;
|
||||
}
|
||||
|
||||
transferDisplay(*imageData, displayBufferMeta, nullptr, 0);
|
||||
|
||||
// window.config.flipStatus.currentBuffer = index; // set after flip, before vblank
|
||||
|
||||
m_messages.push({MessageType::flip, handle - 1, nullptr, index, setIndex, *imageData});
|
||||
lock.unlock();
|
||||
m_condSDL2.notify_one();
|
||||
|
||||
LOG_DEBUG(L"<- submitFlip(%d):%u %d", handle, setIndex, index);
|
||||
}
|
||||
|
||||
void VideoOut::doFlip(Context& ctx, int handle) {
|
||||
auto& flipStatus = ctx.config.flipStatus;
|
||||
|
||||
auto& timer = accessTimer();
|
||||
auto const curTime = (uint64_t)(1e6 * timer.getTimeS());
|
||||
auto const procTime = timer.queryPerformance();
|
||||
auto elapsed_us = curTime - flipStatus.processTime;
|
||||
|
||||
flipStatus.tsc = procTime;
|
||||
flipStatus.processTime = curTime;
|
||||
++flipStatus.count;
|
||||
--flipStatus.gcQueueNum;
|
||||
vblankEnd(handle, curTime, procTime);
|
||||
|
||||
// Trigger Event Flip
|
||||
for (auto& item: ctx.eventFlip) {
|
||||
(void)item->triggerEvent(VIDEO_OUT_EVENT_FLIP, EventQueue::KERNEL_EVFILT_VIDEO_OUT, reinterpret_cast<void*>(ctx.config.flipStatus.flipArg));
|
||||
}
|
||||
// - Flip event
|
||||
}
|
||||
|
||||
void VideoOut::getFlipStatus(int handle, void* status) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
|
||||
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
*(SceVideoOutFlipStatus*)status = m_windows[handle - 1].config.flipStatus;
|
||||
}
|
||||
|
||||
void VideoOut::getVBlankStatus(int handle, void* status) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
|
||||
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
|
||||
auto& vblank = m_windows[handle - 1].config.vblankStatus;
|
||||
|
||||
*(SceVideoOutVblankStatus*)status = vblank;
|
||||
}
|
||||
|
||||
void VideoOut::vblankEnd(int handle, uint64_t curTime, uint64_t curProcTime) {
|
||||
OPTICK_EVENT();
|
||||
|
||||
auto& window = m_windows[handle - 1];
|
||||
auto& vblank = window.config.vblankStatus;
|
||||
|
||||
// vblank.processTime = 0;
|
||||
|
||||
vblank.tsc = curProcTime;
|
||||
vblank.processTime = curTime;
|
||||
++vblank.count;
|
||||
|
||||
for (auto& item: window.eventFlip) {
|
||||
(void)item->triggerEvent(VIDEO_OUT_EVENT_VBLANK, EventQueue::KERNEL_EVFILT_VIDEO_OUT, reinterpret_cast<void*>(window.config.vblankStatus.count));
|
||||
}
|
||||
}
|
||||
|
||||
void VideoOut::getResolution(int handle, void* status) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
|
||||
*(SceVideoOutResolutionStatus*)status = m_windows[handle - 1].config.resolution;
|
||||
}
|
||||
|
||||
void VideoOut::getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
|
||||
uint32_t pitchInPixel) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_TRACE(L"%S", __FUNCTION__);
|
||||
|
||||
auto [displayFormat, _] = vulkan::getDisplayFormat(m_vulkanObj);
|
||||
|
||||
*(SceVideoOutBufferAttribute*)attribute = SceVideoOutBufferAttribute {
|
||||
.pixelFormat = SceVideoOutPixelFormat::PIXEL_FORMAT_A8R8G8B8_SRGB, // todo get vulkan pixel_format?
|
||||
.tilingMode = tiling_mode,
|
||||
.aspectRatio = aspect_ratio,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.pitchInPixel = pitchInPixel,
|
||||
};
|
||||
}
|
||||
|
||||
int VideoOut::registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) {
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_TRACE(L"%S(%d)", __FUNCTION__, handle);
|
||||
|
||||
auto& config = m_windows[handle - 1].config;
|
||||
auto const setIndex = config.buffersSetsCount++;
|
||||
|
||||
[[unlikely]] if (setIndex > 15) {
|
||||
LOG_CRIT(L"buffersSetsCount > 15");
|
||||
return ::Err::VIDEO_OUT_ERROR_NO_EMPTY_SLOT;
|
||||
}
|
||||
|
||||
auto& bufferSet = config.bufferSets[setIndex];
|
||||
bufferSet.buffers.resize(numBuffer);
|
||||
|
||||
for (int i = startIndex; i < startIndex + numBuffer; ++i) {
|
||||
if (config.buffers[i] >= 0) return ::Err::VIDEO_OUT_ERROR_SLOT_OCCUPIED;
|
||||
config.buffers[i] = setIndex;
|
||||
}
|
||||
|
||||
auto const* _att = (SceVideoOutBufferAttribute const*)attribute;
|
||||
|
||||
for (size_t n = 0; n < numBuffer; ++n) {
|
||||
bufferSet.buffers[n].bufferVaddr = (uintptr_t)addresses[n];
|
||||
auto const [displaySize, displaySizeAlign] = getDisplayBufferSize(_att->width, _att->height, _att->pitchInPixel, _att->tilingMode, false);
|
||||
|
||||
bufferSet.buffers[n].bufferSize = displaySize;
|
||||
bufferSet.buffers[n].bufferAlign = displaySizeAlign;
|
||||
LOG_INFO(L"+bufferset[%d] buffer:%d vaddr:0x%08llx-0x%08llx", setIndex, n, (uint64_t)addresses[n], (uint64_t)addresses[n] + displaySizeAlign);
|
||||
|
||||
auto [format, colorSpace] = vulkan::getDisplayFormat(m_vulkanObj);
|
||||
if (!m_graphics->registerDisplayBuffer(bufferSet.buffers[n].bufferVaddr, VkExtent2D {.width = _att->width, .height = _att->height}, _att->pitchInPixel,
|
||||
format))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return setIndex;
|
||||
}
|
||||
|
||||
vulkan::QueueInfo* VideoOut::getQueue(vulkan::QueueType type) {
|
||||
|
||||
std::unique_lock const lock(m_mutexInt);
|
||||
|
||||
auto& queueInfo = m_vulkanObj->queues.items[vulkan::getIndex(type)];
|
||||
auto bestIt = queueInfo.begin();
|
||||
|
||||
// Search for least used
|
||||
for (auto it = queueInfo.begin()++; it != queueInfo.end(); ++it) {
|
||||
if ((*it)->useCount < (*bestIt)->useCount) {
|
||||
bestIt = it;
|
||||
}
|
||||
}
|
||||
// -
|
||||
|
||||
++(*bestIt)->useCount;
|
||||
return bestIt->get();
|
||||
}
|
||||
|
||||
void cbWindow_close(SDL_Window* window) {
|
||||
const SDL_MessageBoxButtonData mbbd[2] {
|
||||
{.flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, .buttonid = 0, .text = "Cancel"},
|
||||
{.flags = 0, .buttonid = 1, .text = "Quit"},
|
||||
};
|
||||
|
||||
const SDL_MessageBoxData mbd {
|
||||
.flags = SDL_MESSAGEBOX_WARNING,
|
||||
.window = window,
|
||||
.title = "Are sure you want to quit?",
|
||||
.message = "All your unsaved progress will be lost!",
|
||||
.numbuttons = 2,
|
||||
.buttons = mbbd,
|
||||
.colorScheme = nullptr,
|
||||
};
|
||||
|
||||
int buttonId = 0;
|
||||
if (SDL_ShowMessageBox(&mbd, &buttonId) == 0 && buttonId != 1) return;
|
||||
|
||||
exit(0); // destructor cleans up.
|
||||
}
|
||||
|
||||
std::thread VideoOut::createSDLThread() {
|
||||
return std::thread([this] {
|
||||
util::setThreadName("VideoOut");
|
||||
OPTICK_THREAD("VideoOut");
|
||||
|
||||
LOG_USE_MODULE(VideoOut);
|
||||
LOG_DEBUG(L"Init SDL2 video");
|
||||
|
||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
|
||||
|
||||
// SDL polling helper
|
||||
auto func_pollSDL = [](auto& window) {
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_CLOSE: cbWindow_close(window); break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
case SDL_KEYUP:
|
||||
if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) {
|
||||
cbWindow_close(window);
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
};
|
||||
// -
|
||||
|
||||
while (!m_stop) {
|
||||
std::unique_lock lock(m_mutexInt);
|
||||
m_condSDL2.wait_for(lock, std::chrono::microseconds(m_vblankTime), [this] { return m_stop || !m_messages.empty(); });
|
||||
if (m_stop) break;
|
||||
|
||||
// Handle VBlank
|
||||
if (m_messages.empty()) {
|
||||
using namespace std::chrono;
|
||||
|
||||
func_pollSDL(m_windows[0].window); // check only main for now
|
||||
|
||||
auto& timer = accessTimer();
|
||||
auto const curTime = (uint64_t)(1e6 * timer.getTimeS());
|
||||
auto const procTime = timer.queryPerformance();
|
||||
for (size_t n = 0; n < m_windows.size(); ++n) {
|
||||
|
||||
vblankEnd(1 + n, curTime, procTime);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// -
|
||||
|
||||
auto item = m_messages.front();
|
||||
auto const handleIndex = item.handle;
|
||||
auto& window = m_windows[handleIndex];
|
||||
|
||||
switch (item.type) {
|
||||
case MessageType::open: {
|
||||
|
||||
auto const title = getTitle(handleIndex, 0, 0, window.fliprate);
|
||||
|
||||
window.window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_widthTotal, m_heightTotal,
|
||||
SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN);
|
||||
|
||||
SDL_GetWindowSize(window.window, (int*)(&window.config.resolution.paneWidth), (int*)(&window.config.resolution.paneHeight));
|
||||
|
||||
LOG_INFO(L"--> VideoOut Open(%S)| %d:%d", title.c_str(), window.config.resolution.paneWidth, window.config.resolution.paneHeight);
|
||||
if (m_vulkanObj == nullptr) {
|
||||
m_vulkanObj = vulkan::initVulkan(window.window, window.surface, accessInitParams()->enableValidation());
|
||||
auto& info = m_vulkanObj->deviceInfo;
|
||||
|
||||
m_graphics = createGraphics(*this, info.device, info.physicalDevice, info.instance);
|
||||
|
||||
auto queue = m_vulkanObj->queues.items[getIndex(vulkan::QueueType::present)][0].get(); // todo use getQeueu
|
||||
m_imageHandler = createImageHandler(info.device, VkExtent2D {window.config.resolution.paneWidth, window.config.resolution.paneHeight}, queue);
|
||||
m_imageHandler->init(m_vulkanObj, window.surface);
|
||||
|
||||
*item.done = true;
|
||||
} else {
|
||||
vulkan::createSurface(m_vulkanObj, window.window, window.surface);
|
||||
}
|
||||
|
||||
m_condDone.notify_one();
|
||||
} break;
|
||||
case MessageType::close: {
|
||||
SDL_DestroyWindow(window.window);
|
||||
*item.done = true;
|
||||
m_condDone.notify_one();
|
||||
} break;
|
||||
case MessageType::flip: {
|
||||
LOG_DEBUG(L"-> flip(%d) set:%u buffer:%u", item.index, item.setIndex, item.imageData.index);
|
||||
OPTICK_FRAME("VideoOut");
|
||||
auto& flipStatus = window.config.flipStatus;
|
||||
|
||||
{
|
||||
OPTICK_EVENT("Present");
|
||||
presentImage(item.imageData, m_imageHandler->getSwapchain(), m_imageHandler->getQueue());
|
||||
m_imageHandler->notify_done(item.imageData);
|
||||
}
|
||||
m_graphics->submitDone();
|
||||
|
||||
auto& timer = accessTimer();
|
||||
auto const curTime = (uint64_t)(1e6 * timer.getTimeS());
|
||||
auto const procTime = timer.queryPerformance();
|
||||
auto elapsed_us = curTime - flipStatus.processTime;
|
||||
|
||||
doFlip(window, 1 + handleIndex);
|
||||
|
||||
double const fps = (window.config.fps * 5.0 + (1e6 / (double)elapsed_us)) / 6.0;
|
||||
auto title = getTitle(1 + handleIndex, flipStatus.count, round(fps), window.fliprate);
|
||||
|
||||
window.config.fps = fps;
|
||||
|
||||
SDL_SetWindowTitle(window.window, title.c_str());
|
||||
func_pollSDL(window.window);
|
||||
|
||||
LOG_DEBUG(L"<- flip(%d) set:%u buffer:%u", handleIndex, item.setIndex, item.imageData.index);
|
||||
} break;
|
||||
}
|
||||
m_messages.pop();
|
||||
}
|
||||
SDL_Quit();
|
||||
});
|
||||
}
|
||||
|
||||
uint64_t getImageAlignment(VkFormat format, VkExtent3D const& extent) {
|
||||
auto device = ((VideoOut&)accessVideoOut()).getDeviceInfo()->device;
|
||||
|
||||
VkImageCreateInfo const imageInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = extent,
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
};
|
||||
|
||||
VkImage image;
|
||||
VkMemoryRequirements reqs;
|
||||
vkCreateImage(device, &imageInfo, nullptr, &image);
|
||||
vkGetImageMemoryRequirements(device, image, &reqs);
|
||||
vkDestroyImage(device, image, nullptr);
|
||||
return reqs.alignment;
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/kernel/eventqueue_types.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <utility/utility.h>
|
||||
|
||||
namespace vulkan {
|
||||
struct SwapchainData;
|
||||
}
|
||||
|
||||
constexpr int VIDEO_OUT_EVENT_FLIP = 0;
|
||||
constexpr int VIDEO_OUT_EVENT_VBLANK = 1;
|
||||
|
||||
class IGraphics;
|
||||
|
||||
class IVideoOut {
|
||||
CLASS_NO_COPY(IVideoOut);
|
||||
CLASS_NO_MOVE(IVideoOut);
|
||||
|
||||
protected:
|
||||
IVideoOut() = default;
|
||||
|
||||
public:
|
||||
virtual ~IVideoOut() = default;
|
||||
|
||||
/**
|
||||
* @brief init interals
|
||||
*
|
||||
*/
|
||||
virtual void init() = 0;
|
||||
|
||||
/**
|
||||
* @brief Open a window
|
||||
*
|
||||
* @param userId
|
||||
* @return int internal handle of the window
|
||||
*/
|
||||
virtual int open(int userId) = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes the window
|
||||
*
|
||||
* @param handle
|
||||
*/
|
||||
virtual void close(int handle) = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the fps to use
|
||||
*
|
||||
* @param handle
|
||||
* @param rate
|
||||
*/
|
||||
virtual void setFliprate(int handle, int rate) = 0;
|
||||
|
||||
/**
|
||||
* @brief Add a VIDEO_OUT_EVENT for the window
|
||||
*
|
||||
* @param handle
|
||||
* @param event
|
||||
* @param eq
|
||||
* @return int
|
||||
*/
|
||||
virtual int addEvent(int handle, Kernel::EventQueue::KernelEqueueEvent const& event, Kernel::EventQueue::IKernelEqueue_t eq) = 0;
|
||||
|
||||
/**
|
||||
* @brief Removes a VIDEO_OUT_EVENT for the window
|
||||
*
|
||||
* @param handle
|
||||
* @param eq
|
||||
* @param ident
|
||||
*/
|
||||
virtual void removeEvent(int handle, Kernel::EventQueue::IKernelEqueue_t eq, int const ident) = 0;
|
||||
|
||||
/**
|
||||
* @brief Submit flip video buffers to the queue
|
||||
*
|
||||
* @param index
|
||||
* @param flipArg used by the flip event
|
||||
*/
|
||||
virtual void submitFlip(int handle, int index, int64_t flipArg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the Flip Status
|
||||
*
|
||||
* @param handle
|
||||
* @param status
|
||||
*/
|
||||
virtual void getFlipStatus(int handle, void* status) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the VBlank Status (currently faked by a timer)
|
||||
*
|
||||
* @param handle
|
||||
* @param status
|
||||
*/
|
||||
virtual void getVBlankStatus(int handle, void* status) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the current resolution
|
||||
*
|
||||
* @param handle
|
||||
* @param status
|
||||
*/
|
||||
virtual void getResolution(int handle, void* status) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the number of unhandled flip submits
|
||||
*
|
||||
* @param handle
|
||||
* @return int
|
||||
*/
|
||||
virtual int getPendingFlips(int handle) = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the video Buffer Attributes
|
||||
*
|
||||
* @param attribute
|
||||
* @param pixel_format
|
||||
* @param tiling_mode
|
||||
* @param aspect_ratio
|
||||
* @param width
|
||||
* @param height
|
||||
* @param pitch_in_pixel
|
||||
*/
|
||||
virtual void getBufferAttribute(void* attribute, uint32_t pixel_format, int32_t tiling_mode, int32_t aspect_ratio, uint32_t width, uint32_t height,
|
||||
uint32_t pitch_in_pixel) = 0;
|
||||
|
||||
/**
|
||||
* @brief Registers a video buffer
|
||||
*
|
||||
* @param handle
|
||||
* @param startIndex
|
||||
* @param addresses
|
||||
* @param numBuffer
|
||||
* @param attribute
|
||||
* @return int
|
||||
*/
|
||||
virtual int registerBuffers(int handle, int startIndex, void* const* addresses, int numBuffer, const void* attribute) = 0;
|
||||
|
||||
/**
|
||||
* @brief Access the graphics interface
|
||||
*
|
||||
* @return IGraphics*
|
||||
*/
|
||||
virtual IGraphics* getGraphics() = 0;
|
||||
|
||||
/**
|
||||
* @brief locks the sdlpoll
|
||||
*
|
||||
* @return std::unique_lock<std::mutex>
|
||||
*/
|
||||
virtual std::unique_lock<std::mutex> getSDLLock() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Notify a gpu visible memory range
|
||||
*
|
||||
* @return true: Memory has been allocated successfully
|
||||
*/
|
||||
virtual bool notify_allocHeap(uint64_t vaddr, uint64_t size, int memoryProtection) = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if the vaddr is gpu memory (prev notify_allocHeap)
|
||||
*
|
||||
* @param vaddr
|
||||
* @return true is gpu local memory
|
||||
* @return false
|
||||
*/
|
||||
virtual bool isGPULocal(uint64_t vaddr) = 0;
|
||||
};
|
||||
|
||||
#if defined(__APICALL_EXTERN)
|
||||
#define __APICALL __declspec(dllexport)
|
||||
#elif defined(__APICALL_IMPORT)
|
||||
#define __APICALL __declspec(dllimport)
|
||||
#else
|
||||
#define __APICALL
|
||||
#endif
|
||||
__APICALL IVideoOut& accessVideoOut();
|
||||
|
||||
#undef __APICALL
|
@ -1,220 +0,0 @@
|
||||
#include "vulkanHelper.h"
|
||||
|
||||
#include "../imageHandler.h"
|
||||
#include "core/imports/exports/graphics.h"
|
||||
#include "logging.h"
|
||||
#include "utility/utility.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <assert.h>
|
||||
#include <boost/assert.hpp>
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <mutex>
|
||||
#include <optick.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vulkan/vk_enum_string_helper.h>
|
||||
|
||||
LOG_DEFINE_MODULE(vulkanHelper);
|
||||
|
||||
namespace vulkan {
|
||||
|
||||
void createSurface(VulkanObj* obj, SDL_Window* window, VkSurfaceKHR& surfaceOut) {
|
||||
LOG_USE_MODULE(vulkanHelper);
|
||||
if (SDL_Vulkan_CreateSurface(window, obj->deviceInfo.instance, &surfaceOut)) {
|
||||
LOG_CRIT(L"Couldn't create surface");
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj) {
|
||||
if (obj->surfaceCapabilities.formats.empty()) {
|
||||
return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR};
|
||||
}
|
||||
|
||||
VkFormat format = obj->surfaceCapabilities.formats[0].format;
|
||||
VkColorSpaceKHR imageColorSpace = obj->surfaceCapabilities.formats[0].colorSpace;
|
||||
if (obj->surfaceCapabilities.format_unorm_bgra32) {
|
||||
format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||
imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
} else if (obj->surfaceCapabilities.format_srgb_bgra32) {
|
||||
format = VK_FORMAT_B8G8R8A8_SRGB;
|
||||
imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
}
|
||||
return {format, imageColorSpace};
|
||||
}
|
||||
|
||||
void submitDisplayTransfer(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, QueueInfo const* queue, VkSemaphore waitSema,
|
||||
size_t waitValue) {
|
||||
LOG_USE_MODULE(vulkanHelper);
|
||||
|
||||
size_t waitValues[] = {0, waitValue};
|
||||
uint32_t waitCount = waitSema != nullptr ? 2 : 1;
|
||||
|
||||
VkTimelineSemaphoreSubmitInfo const timelineInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||
.waitSemaphoreValueCount = waitCount,
|
||||
.pWaitSemaphoreValues = waitValues,
|
||||
};
|
||||
|
||||
VkPipelineStageFlags waitStage[] = {VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT};
|
||||
VkSemaphore sems[] = {imageData.semImageReady, waitSema};
|
||||
|
||||
VkSubmitInfo const submitInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = waitSema != nullptr ? &timelineInfo : nullptr,
|
||||
|
||||
.waitSemaphoreCount = waitCount,
|
||||
.pWaitSemaphores = sems,
|
||||
.pWaitDstStageMask = waitStage,
|
||||
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &imageData.cmdBuffer,
|
||||
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &imageData.semImageCopied,
|
||||
};
|
||||
|
||||
{
|
||||
std::unique_lock lock(queue->mutex);
|
||||
if (VkResult result = vkQueueSubmit(queue->queue, 1, &submitInfo, imageData.submitFence); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Couldn't vkQueueSubmit Transfer %S", string_VkResult(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, IGraphics* graphics) {
|
||||
LOG_USE_MODULE(vulkanHelper);
|
||||
|
||||
{
|
||||
VkImageMemoryBarrier const barrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
|
||||
.image = imageData.swapchainImage,
|
||||
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}};
|
||||
|
||||
vkCmdPipelineBarrier(imageData.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
graphics->copyDisplayBuffer(displayBuffer->bufferVaddr, imageData.cmdBuffer, imageData.swapchainImage, imageData.extent); // let gpumemorymanager decide
|
||||
|
||||
{
|
||||
// Change to Present Layout
|
||||
VkImageMemoryBarrier const barrier {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = 0,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
|
||||
.image = imageData.swapchainImage,
|
||||
.subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}};
|
||||
|
||||
vkCmdPipelineBarrier(imageData.cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
// - Present layout
|
||||
|
||||
// End CmdBuffer -> Submit
|
||||
if (vkEndCommandBuffer(imageData.cmdBuffer) != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Couldn't end commandbuffer");
|
||||
}
|
||||
// -
|
||||
}
|
||||
|
||||
void presentImage(ImageData const& imageData, VkSwapchainKHR swapchain, QueueInfo const* queue) {
|
||||
LOG_USE_MODULE(vulkanHelper);
|
||||
|
||||
VkPresentInfoKHR const presentInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
||||
.pNext = nullptr,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = &imageData.semImageCopied,
|
||||
.swapchainCount = 1,
|
||||
.pSwapchains = &swapchain,
|
||||
.pImageIndices = &imageData.index,
|
||||
.pResults = nullptr,
|
||||
};
|
||||
|
||||
{
|
||||
OPTICK_GPU_FLIP(&swapchain);
|
||||
OPTICK_CATEGORY("Present", Optick::Category::Wait);
|
||||
|
||||
std::unique_lock lock(queue->mutex);
|
||||
vkQueuePresentKHR(queue->queue, &presentInfo);
|
||||
}
|
||||
}
|
||||
} // namespace vulkan
|
||||
|
||||
// ### Vulkan EXT Function Definitions
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateShadersEXT(VkDevice device, uint32_t createInfoCount, const VkShaderCreateInfoEXT* pCreateInfos,
|
||||
const VkAllocationCallbacks* pAllocator, VkShaderEXT* pShaders) {
|
||||
static auto fn = (PFN_vkCreateShadersEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCreateShadersEXT");
|
||||
return fn(device, createInfoCount, pCreateInfos, pAllocator, pShaders);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthClipNegativeOneToOneEXT(VkCommandBuffer commandBuffer, VkBool32 negativeOneToOne) {
|
||||
static auto fn = (PFN_vkCmdSetDepthClipNegativeOneToOneEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetDepthClipNegativeOneToOneEXT");
|
||||
fn(commandBuffer, negativeOneToOne);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables) {
|
||||
static auto fn = (PFN_vkCmdSetColorWriteEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorWriteEnableEXT");
|
||||
fn(commandBuffer, attachmentCount, pColorWriteEnables);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorWriteMaskEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
|
||||
const VkColorComponentFlags* pColorWriteMasks) {
|
||||
static auto fn = (PFN_vkCmdSetColorWriteMaskEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorWriteMaskEXT");
|
||||
fn(commandBuffer, firstAttachment, attachmentCount, pColorWriteMasks);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEnableEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
|
||||
const VkBool32* pColorBlendEnables) {
|
||||
static auto fn = (PFN_vkCmdSetColorBlendEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorBlendEnableEXT");
|
||||
fn(commandBuffer, firstAttachment, attachmentCount, pColorBlendEnables);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetColorBlendEquationEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount,
|
||||
const VkColorBlendEquationEXT* pColorBlendEquations) {
|
||||
static auto fn = (PFN_vkCmdSetColorBlendEquationEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetColorBlendEquationEXT");
|
||||
fn(commandBuffer, firstAttachment, attachmentCount, pColorBlendEquations);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetPolygonModeEXT(VkCommandBuffer commandBuffer, VkPolygonMode polygonMode) {
|
||||
static auto fn = (PFN_vkCmdSetPolygonModeEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetPolygonModeEXT");
|
||||
fn(commandBuffer, polygonMode);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetRasterizationSamplesEXT(VkCommandBuffer commandBuffer, VkSampleCountFlagBits rasterizationSamples) {
|
||||
static auto fn = (PFN_vkCmdSetRasterizationSamplesEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetRasterizationSamplesEXT");
|
||||
fn(commandBuffer, rasterizationSamples);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToCoverageEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToCoverageEnable) {
|
||||
static auto fn = (PFN_vkCmdSetAlphaToCoverageEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetAlphaToCoverageEnableEXT");
|
||||
fn(commandBuffer, alphaToCoverageEnable);
|
||||
}
|
||||
|
||||
VKAPI_ATTR void VKAPI_CALL vkCmdSetAlphaToOneEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToOneEnable) {
|
||||
static auto fn = (PFN_vkCmdSetAlphaToOneEnableEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkCmdSetAlphaToOneEnableEXT");
|
||||
fn(commandBuffer, alphaToOneEnable);
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryHostPointerPropertiesEXT(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, const void* pHostPointer,
|
||||
VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties) {
|
||||
static auto fn = (PFN_vkGetMemoryHostPointerPropertiesEXT)vkGetInstanceProcAddr(vulkan::getVkInstance(), "vkGetMemoryHostPointerPropertiesEXT");
|
||||
return fn(device, handleType, pHostPointer, pMemoryHostPointerProperties);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "vulkanSetup.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class IGraphics;
|
||||
struct ImageData;
|
||||
|
||||
namespace vulkan {
|
||||
struct PresentData {
|
||||
VkImage swapchainImage = nullptr;
|
||||
VkSemaphore displayReady = nullptr;
|
||||
VkSemaphore presentReady = nullptr;
|
||||
uint32_t index = 0;
|
||||
};
|
||||
|
||||
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj);
|
||||
|
||||
void submitDisplayTransfer(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, QueueInfo const* queue, VkSemaphore waitSema,
|
||||
size_t waitValue);
|
||||
|
||||
void transfer2Display(SwapchainData::DisplayBuffers const* displayBuffer, ImageData const& imageData, IGraphics* graphics);
|
||||
|
||||
void presentImage(ImageData const& imageData, VkSwapchainKHR swapchain, QueueInfo const* queue);
|
||||
|
||||
} // namespace vulkan
|
@ -1,777 +0,0 @@
|
||||
#include "vulkanSetup.h"
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <format>
|
||||
#include <utility/utility.h>
|
||||
#include <vulkan/vk_enum_string_helper.h>
|
||||
|
||||
LOG_DEFINE_MODULE(vulkanSetup);
|
||||
|
||||
constexpr std::array<VkValidationFeatureDisableEXT, 0> disabledValidationFeatures {};
|
||||
|
||||
constexpr std::array enabledValidationFeatures {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
|
||||
|
||||
constexpr std::array requiredExtensions {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, VK_KHR_MAINTENANCE_2_EXTENSION_NAME, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
|
||||
VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
|
||||
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
|
||||
VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME,
|
||||
// VK_EXT_SHADER_OBJECT_EXTENSION_NAME,
|
||||
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, "VK_KHR_external_memory_win32"};
|
||||
|
||||
namespace vulkan {
|
||||
struct VulkanExtensions {
|
||||
bool enableValidationLayers = true;
|
||||
|
||||
std::vector<const char*> requiredExtensions;
|
||||
|
||||
std::vector<VkExtensionProperties> availableExtensions;
|
||||
std::vector<const char*> requiredLayers;
|
||||
std::vector<VkLayerProperties> availableLayers;
|
||||
};
|
||||
|
||||
struct VulkanQueues {
|
||||
struct QueueInfo {
|
||||
uint32_t family = 0;
|
||||
uint32_t index = 0;
|
||||
uint32_t count = 0;
|
||||
|
||||
bool graphics = false;
|
||||
bool compute = false;
|
||||
bool transfer = false;
|
||||
bool present = false;
|
||||
};
|
||||
|
||||
uint32_t familyCount = 0;
|
||||
std::vector<uint32_t> familyUsed;
|
||||
std::vector<QueueInfo> graphics;
|
||||
std::vector<QueueInfo> compute;
|
||||
std::vector<QueueInfo> transfer;
|
||||
std::vector<QueueInfo> present;
|
||||
};
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL createDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* createInfo,
|
||||
const VkAllocationCallbacks* allocator, VkDebugUtilsMessengerEXT* messenger) {
|
||||
|
||||
if (auto func = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); func != nullptr) {
|
||||
return func(instance, createInfo, allocator, messenger);
|
||||
}
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessengerCallback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callback_data, void* /*user_data*/) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
|
||||
const char* severityStr = nullptr;
|
||||
bool skip = false;
|
||||
bool error = false;
|
||||
bool debugPrintf = false;
|
||||
|
||||
switch (message_severity) {
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
||||
severityStr = "V";
|
||||
skip = true;
|
||||
break;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||||
if ((messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) != 0 && strcmp(callback_data->pMessageIdName, "UNASSIGNED-DEBUG-PRINTF") == 0) {
|
||||
debugPrintf = true;
|
||||
skip = true;
|
||||
} else {
|
||||
severityStr = "I";
|
||||
}
|
||||
break;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: severityStr = "W"; break;
|
||||
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||
severityStr = "E";
|
||||
error = true;
|
||||
break;
|
||||
default: severityStr = "?";
|
||||
}
|
||||
|
||||
if (error) {
|
||||
LOG_WARN(L"[Vulkan][%S][%u]: %S", severityStr, static_cast<uint32_t>(messageTypes), callback_data->pMessage);
|
||||
} else if (!skip) {
|
||||
LOG_DEBUG(L"[Vulkan][%S][%u]: %S", severityStr, static_cast<uint32_t>(messageTypes), callback_data->pMessage);
|
||||
}
|
||||
|
||||
if (debugPrintf) {
|
||||
auto strs = util::splitString(std::string(callback_data->pMessage), U'|');
|
||||
if (!strs.empty()) {
|
||||
LOG_DEBUG(L"%S", std::string(strs.back()).data());
|
||||
}
|
||||
}
|
||||
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
VulkanExtensions getExtensions(SDL_Window* window, bool enableValidation) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
|
||||
uint32_t countAvailableExtensions = 0;
|
||||
uint32_t countAvailableLayers = 0;
|
||||
uint32_t countRequiredExtensions = 0;
|
||||
|
||||
VulkanExtensions r = {.enableValidationLayers = enableValidation};
|
||||
|
||||
SDL_Vulkan_GetInstanceExtensions(window, &countRequiredExtensions, NULL);
|
||||
auto extensions = static_cast<const char**>(SDL_malloc(sizeof(char*) * countRequiredExtensions));
|
||||
SDL_Vulkan_GetInstanceExtensions(window, &countRequiredExtensions, extensions);
|
||||
for (size_t n = 0; n < countRequiredExtensions; n++) {
|
||||
r.requiredExtensions.push_back(extensions[n]);
|
||||
}
|
||||
SDL_free(extensions);
|
||||
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &countAvailableExtensions, nullptr);
|
||||
r.availableExtensions = std::vector<VkExtensionProperties>(countAvailableExtensions, VkExtensionProperties {});
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &countAvailableExtensions, r.availableExtensions.data());
|
||||
|
||||
if (std::find_if(r.availableExtensions.begin(), r.availableExtensions.end(),
|
||||
[](VkExtensionProperties s) { return strcmp(s.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; }) != r.availableExtensions.end()) {
|
||||
r.requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
} else {
|
||||
r.enableValidationLayers = false;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < r.requiredExtensions.size(); ++n) {
|
||||
LOG_INFO(L"SDL2 required extension: %S", r.requiredExtensions[n]);
|
||||
}
|
||||
|
||||
for (const auto& ext: r.availableExtensions) {
|
||||
LOG_DEBUG(L"Vulkan available extension: %S specVer:%u", ext.extensionName, ext.specVersion);
|
||||
}
|
||||
|
||||
vkEnumerateInstanceLayerProperties(&countAvailableLayers, nullptr);
|
||||
r.availableLayers = std::vector<VkLayerProperties>(countAvailableLayers, VkLayerProperties {});
|
||||
vkEnumerateInstanceLayerProperties(&countAvailableLayers, r.availableLayers.data());
|
||||
|
||||
for (const auto& l: r.availableLayers) {
|
||||
LOG_DEBUG(L"Vulkan available layer:%S specVer:%u.%u implVer:%u (%S)", l.layerName, VK_API_VERSION_MAJOR(l.specVersion), VK_API_VERSION_MINOR(l.specVersion),
|
||||
l.implementationVersion, l.description);
|
||||
}
|
||||
|
||||
if (r.enableValidationLayers) {
|
||||
r.requiredLayers.push_back("VK_LAYER_KHRONOS_validation");
|
||||
for (auto const l: r.requiredLayers) {
|
||||
if (std::find_if(r.availableLayers.begin(), r.availableLayers.end(), [&l](auto s) { return strcmp(s.layerName, l) == 0; }) == r.availableLayers.end()) {
|
||||
LOG_INFO(L"no validation layer:%S", l);
|
||||
r.enableValidationLayers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r.enableValidationLayers) {
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &countAvailableExtensions, nullptr);
|
||||
std::vector<VkExtensionProperties> availableExtensions(countAvailableExtensions, VkExtensionProperties {});
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &countAvailableExtensions, availableExtensions.data());
|
||||
|
||||
for (const auto& ext: availableExtensions) {
|
||||
LOG_DEBUG(L"VK_LAYER_KHRONOS_validation available extension: %S version:%u", ext.extensionName, ext.specVersion);
|
||||
}
|
||||
|
||||
if (std::find_if(availableExtensions.begin(), availableExtensions.end(),
|
||||
[](auto s) { return strcmp(s.extensionName, "VK_EXT_validation_features") == 0; }) != availableExtensions.end()) {
|
||||
r.requiredExtensions.push_back("VK_EXT_validation_features");
|
||||
} else {
|
||||
r.enableValidationLayers = false;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
VulkanQueues findQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
VulkanQueues qs;
|
||||
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &qs.familyCount, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(qs.familyCount, VkQueueFamilyProperties {});
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &qs.familyCount, queueFamilies.data());
|
||||
|
||||
std::vector<VulkanQueues::QueueInfo> queueInfos;
|
||||
qs.familyUsed.resize(queueFamilies.size());
|
||||
|
||||
for (uint32_t n = 0; n < queueFamilies.size(); ++n) {
|
||||
VkBool32 presentationSupported = VK_FALSE;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, n, surface, &presentationSupported);
|
||||
|
||||
auto const& queue = queueFamilies[n];
|
||||
LOG_DEBUG(L"queue family[%u]: %S [count:%u] [present=%S]", n, string_VkQueueFlags(queue.queueFlags).c_str(), queue.queueCount,
|
||||
util::getBoolStr(presentationSupported == VK_TRUE));
|
||||
|
||||
VulkanQueues::QueueInfo info {
|
||||
.family = n,
|
||||
.count = queue.queueCount,
|
||||
.graphics = (queue.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0,
|
||||
.compute = (queue.queueFlags & VK_QUEUE_COMPUTE_BIT) != 0,
|
||||
.transfer = (queue.queueFlags & VK_QUEUE_TRANSFER_BIT) != 0,
|
||||
.present = (bool)presentationSupported,
|
||||
};
|
||||
|
||||
queueInfos.push_back(info);
|
||||
}
|
||||
|
||||
// Prio
|
||||
auto gather = [&queueInfos, &qs](uint32_t count, std::vector<VulkanQueues::QueueInfo>& queue, auto const cmp) {
|
||||
for (uint32_t i = queue.size(); i < count; i++) {
|
||||
if (auto it = std::find_if(queueInfos.begin(), queueInfos.end(), cmp); it != queueInfos.end()) {
|
||||
it->index = 0;
|
||||
|
||||
auto& item = queue.emplace_back(*it);
|
||||
if (it->count > qs.familyUsed[it->family]) {
|
||||
item.index = qs.familyUsed[it->family];
|
||||
qs.familyUsed[it->family]++;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gather(1, qs.graphics, [](VulkanQueues::QueueInfo& q) { return q.graphics && q.transfer && q.present; });
|
||||
gather(1, qs.present, [](VulkanQueues::QueueInfo& q) { return q.graphics && q.transfer && q.present; });
|
||||
gather(1, qs.transfer, [](VulkanQueues::QueueInfo& q) { return q.transfer && !q.graphics && !q.compute; });
|
||||
gather(1, qs.compute, [](VulkanQueues::QueueInfo& q) { return q.compute && !q.graphics; });
|
||||
//-
|
||||
|
||||
// Fill in whatever is left
|
||||
gather(1, qs.graphics, [](VulkanQueues::QueueInfo& q) { return q.transfer == true; });
|
||||
gather(1, qs.present, [](VulkanQueues::QueueInfo& q) { return q.present == true; });
|
||||
gather(1, qs.transfer, [](VulkanQueues::QueueInfo& q) { return q.graphics == true; });
|
||||
gather(1, qs.compute, [](VulkanQueues::QueueInfo& q) { return q.compute == true; });
|
||||
//
|
||||
|
||||
//-
|
||||
|
||||
return qs;
|
||||
}
|
||||
|
||||
void dumpQueues(const VulkanQueues& qs) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
|
||||
std::string familyString;
|
||||
for (auto u: qs.familyUsed) {
|
||||
familyString += std::to_string(u) + std::string(", ");
|
||||
}
|
||||
LOG_INFO(L"\t familyUsed = [%S]", familyString.c_str());
|
||||
|
||||
LOG_INFO(L"\t graphics:");
|
||||
for (const auto& q: qs.graphics) {
|
||||
LOG_INFO(L"\t\t family:%u index:%u", q.family, q.index);
|
||||
}
|
||||
|
||||
LOG_INFO(L"\t compute:");
|
||||
for (const auto& q: qs.compute) {
|
||||
LOG_INFO(L"\t\t family:%u index:%u", q.family, q.index);
|
||||
}
|
||||
|
||||
LOG_INFO(L"\t transfer:");
|
||||
for (const auto& q: qs.transfer) {
|
||||
LOG_INFO(L"\t\t family:%u, index:%u", q.family, q.index);
|
||||
}
|
||||
LOG_INFO(L"\t present:");
|
||||
for (const auto& q: qs.present) {
|
||||
LOG_INFO(L"\t\t family:%u, index:%u", q.family, q.index);
|
||||
}
|
||||
}
|
||||
|
||||
void getSurfaceCapabilities(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, SurfaceCapabilities& r) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &r.capabilities);
|
||||
|
||||
uint32_t formats_count = 0;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formats_count, nullptr);
|
||||
|
||||
r.formats = std::vector<VkSurfaceFormatKHR>(formats_count, VkSurfaceFormatKHR {});
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formats_count, r.formats.data());
|
||||
|
||||
uint32_t present_modes_count = 0;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &present_modes_count, nullptr);
|
||||
|
||||
r.presentModes = std::vector<VkPresentModeKHR>(present_modes_count, VkPresentModeKHR {});
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &present_modes_count, r.presentModes.data());
|
||||
|
||||
r.format_srgb_bgra32 = false;
|
||||
for (const auto& f: r.formats) {
|
||||
if (f.format == VK_FORMAT_B8G8R8A8_SRGB && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
r.format_srgb_bgra32 = true;
|
||||
break;
|
||||
}
|
||||
if (f.format == VK_FORMAT_B8G8R8A8_UNORM && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
r.format_unorm_bgra32 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool checkFormat(VkPhysicalDevice device, VkFormat format, VkFormatFeatureFlags features) {
|
||||
VkFormatProperties formatProps;
|
||||
vkGetPhysicalDeviceFormatProperties(device, format, &formatProps);
|
||||
|
||||
if ((formatProps.optimalTilingFeatures & features) == features || (formatProps.linearTilingFeatures & features) == features) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void findPhysicalDevice(VkInstance instance, VkSurfaceKHR surface, SurfaceCapabilities* outCapabilities, VkPhysicalDevice* outDevice, VulkanQueues* outQueues,
|
||||
bool enableValidation) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
|
||||
uint32_t devicesCount = 0;
|
||||
vkEnumeratePhysicalDevices(instance, &devicesCount, nullptr);
|
||||
|
||||
if (devicesCount == 0) {
|
||||
LOG_CRIT(L"No GPUs found");
|
||||
}
|
||||
|
||||
std::vector<VkPhysicalDevice> devices(devicesCount);
|
||||
vkEnumeratePhysicalDevices(instance, &devicesCount, devices.data());
|
||||
|
||||
// Find Best
|
||||
VkPhysicalDevice bestDevice = nullptr;
|
||||
VulkanQueues bestQueues;
|
||||
|
||||
bool skipDevice = false;
|
||||
for (const auto& device: devices) {
|
||||
skipDevice = false;
|
||||
|
||||
VkPhysicalDeviceInlineUniformBlockFeaturesEXT uniBlock {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extendedDynamic3Features {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
|
||||
.pNext = &uniBlock,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depthClipControlFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
|
||||
.pNext = &extendedDynamic3Features,
|
||||
.depthClipControl = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
|
||||
.pNext = &depthClipControlFeatures,
|
||||
.dynamicRendering = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeature {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
|
||||
.pNext = &dynamicRenderingFeature,
|
||||
.timelineSemaphore = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddress {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES,
|
||||
.pNext = &timelineSemaphoreFeature};
|
||||
|
||||
VkPhysicalDeviceDescriptorIndexingFeatures descIndexing {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES,
|
||||
.pNext = &bufferDeviceAddress,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceShaderObjectFeaturesEXT shaderObj {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, .pNext = &descIndexing};
|
||||
|
||||
VkPhysicalDeviceDescriptorIndexingFeatures indexingFeature {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT,
|
||||
.pNext = &shaderObj};
|
||||
|
||||
VkPhysicalDeviceColorWriteEnableFeaturesEXT colorWriteExt {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT,
|
||||
.pNext = &indexingFeature,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceFeatures2 deviceFeatures {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = &colorWriteExt};
|
||||
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
vkGetPhysicalDeviceProperties(device, &deviceProperties);
|
||||
vkGetPhysicalDeviceFeatures2(device, &deviceFeatures);
|
||||
|
||||
LOG_INFO(L"Vulkan device: %S api:%u.%u:%u", deviceProperties.deviceName, VK_API_VERSION_MAJOR(deviceProperties.apiVersion),
|
||||
VK_API_VERSION_MINOR(deviceProperties.apiVersion), VK_API_VERSION_PATCH(deviceProperties.apiVersion));
|
||||
|
||||
auto qs = findQueues(device, surface);
|
||||
dumpQueues(qs);
|
||||
|
||||
if (shaderObj.shaderObject == VK_FALSE) {
|
||||
LOG_ERR(L"shaderObject is not supported");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (qs.graphics.empty() || qs.compute.empty() || qs.transfer.empty() || qs.present.empty()) {
|
||||
LOG_ERR(L"Not enough queues");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (colorWriteExt.colorWriteEnable != VK_TRUE) {
|
||||
LOG_ERR(L"colorWriteEnable is not supported");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (deviceFeatures.features.fragmentStoresAndAtomics != VK_TRUE) {
|
||||
LOG_ERR(L"fragmentStoresAndAtomics is not supported");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (deviceFeatures.features.samplerAnisotropy != VK_TRUE) {
|
||||
LOG_ERR(L"samplerAnisotropy is not supported");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice) {
|
||||
uint32_t numExt = 0;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &numExt, nullptr);
|
||||
if (numExt == 0) {
|
||||
LOG_CRIT(L"no extensions found");
|
||||
}
|
||||
|
||||
std::vector<VkExtensionProperties> availableExt(numExt, VkExtensionProperties {});
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &numExt, availableExt.data());
|
||||
|
||||
for (const char* ext: requiredExtensions) {
|
||||
if (std::find_if(availableExt.begin(), availableExt.end(), [&ext](auto p) { return strcmp(p.extensionName, ext) == 0; }) == availableExt.end()) {
|
||||
LOG_ERR(L"%S not supported", ext);
|
||||
skipDevice = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (enableValidation) {
|
||||
for (const auto& ext: availableExt) {
|
||||
LOG_DEBUG(L"Vulkan available extension: %S, version:%u", ext.extensionName, ext.specVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skipDevice) {
|
||||
getSurfaceCapabilities(device, surface, *outCapabilities);
|
||||
|
||||
if ((outCapabilities->capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
|
||||
LOG_DEBUG(L"Surface cannot be destination of blit");
|
||||
skipDevice = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as transfer source");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_D32_SFLOAT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_D32_SFLOAT cannot be used as depth buffer");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_D32_SFLOAT_S8_UINT cannot be used as depth buffer");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_D16_UNORM, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_D16_UNORM cannot be used as depth buffer");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_D24_UNORM_S8_UINT cannot be used as depth buffer");
|
||||
// skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_BC3_SRGB_BLOCK, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_BC3_SRGB_BLOCK cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8_UNORM cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8G8_UNORM cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_SRGB cannot be used as texture");
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_R8G8B8A8_UNORM cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_B8G8R8A8_SRGB cannot be used as texture");
|
||||
|
||||
if (!skipDevice && !checkFormat(device, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT)) {
|
||||
LOG_DEBUG(L"Format VK_FORMAT_B8G8R8A8_UNORM cannot be used as texture");
|
||||
skipDevice = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipDevice && deviceProperties.limits.maxSamplerAnisotropy < 16.0f) {
|
||||
LOG_DEBUG(L"maxSamplerAnisotropy < 16.0f");
|
||||
skipDevice = true;
|
||||
}
|
||||
|
||||
if (skipDevice) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bestDevice == nullptr || deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
|
||||
bestDevice = device;
|
||||
bestQueues = qs;
|
||||
}
|
||||
}
|
||||
|
||||
// -Find best
|
||||
|
||||
*outDevice = bestDevice;
|
||||
*outQueues = bestQueues;
|
||||
}
|
||||
|
||||
VkDevice createDevice(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VulkanExtensions const extensions, VulkanQueues const& queues,
|
||||
bool enableValidation) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfo(queues.familyCount);
|
||||
std::vector<std::vector<float>> queuePrio(queues.familyCount);
|
||||
|
||||
uint32_t numQueueCreateInfo = 0;
|
||||
for (uint32_t i = 0; i < queues.familyCount; i++) {
|
||||
if (queues.familyUsed[i] != 0) {
|
||||
for (uint32_t pi = 0; pi < queues.familyUsed[i]; pi++) {
|
||||
queuePrio[numQueueCreateInfo].push_back(1.0f);
|
||||
}
|
||||
|
||||
queueCreateInfo[numQueueCreateInfo].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queueCreateInfo[numQueueCreateInfo].pNext = nullptr;
|
||||
queueCreateInfo[numQueueCreateInfo].flags = 0;
|
||||
queueCreateInfo[numQueueCreateInfo].queueFamilyIndex = i;
|
||||
queueCreateInfo[numQueueCreateInfo].queueCount = queues.familyUsed[i];
|
||||
queueCreateInfo[numQueueCreateInfo].pQueuePriorities = queuePrio[numQueueCreateInfo].data();
|
||||
|
||||
numQueueCreateInfo++;
|
||||
}
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures {
|
||||
.geometryShader = VK_TRUE, .fillModeNonSolid = VK_TRUE, .wideLines = VK_TRUE, .samplerAnisotropy = VK_TRUE, .fragmentStoresAndAtomics = VK_TRUE,
|
||||
// deviceFeatures.shaderImageGatherExtended = VK_TRUE;
|
||||
};
|
||||
|
||||
VkPhysicalDeviceInlineUniformBlockFeaturesEXT uniBlock {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES, .pNext = nullptr,
|
||||
//.inlineUniformBlock = VK_TRUE,
|
||||
//.descriptorBindingInlineUniformBlockUpdateAfterBind = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceShaderObjectFeaturesEXT shaderObjectFeatures {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_OBJECT_FEATURES_EXT, .shaderObject = VK_TRUE};
|
||||
|
||||
VkPhysicalDeviceExtendedDynamicState3FeaturesEXT extendedDynamic3Features {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT,
|
||||
.pNext = &uniBlock,
|
||||
.extendedDynamicState3PolygonMode = VK_TRUE,
|
||||
.extendedDynamicState3RasterizationSamples = VK_TRUE,
|
||||
.extendedDynamicState3AlphaToCoverageEnable = VK_TRUE,
|
||||
//.extendedDynamicState3AlphaToOneEnable = VK_TRUE,
|
||||
.extendedDynamicState3ColorBlendEnable = VK_TRUE,
|
||||
.extendedDynamicState3ColorBlendEquation = VK_TRUE,
|
||||
.extendedDynamicState3ColorWriteMask = VK_TRUE,
|
||||
.extendedDynamicState3DepthClipNegativeOneToOne = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDepthClipControlFeaturesEXT depthClipControlFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT,
|
||||
.pNext = &extendedDynamic3Features,
|
||||
.depthClipControl = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDynamicRenderingFeatures dynamicRenderingFeature {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES,
|
||||
.pNext = &depthClipControlFeatures,
|
||||
.dynamicRendering = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceTimelineSemaphoreFeatures timelineSemaphoreFeature {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
|
||||
.pNext = &dynamicRenderingFeature,
|
||||
.timelineSemaphore = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceColorWriteEnableFeaturesEXT colorWriteExt {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COLOR_WRITE_ENABLE_FEATURES_EXT,
|
||||
.pNext = &timelineSemaphoreFeature,
|
||||
.colorWriteEnable = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddress {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, .pNext = &colorWriteExt, .bufferDeviceAddress = VK_TRUE,
|
||||
|
||||
//.bufferDeviceAddressCaptureReplay = enableValidation ? VK_TRUE : VK_FALSE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDescriptorIndexingFeatures const descIndexing {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES,
|
||||
.pNext = &bufferDeviceAddress,
|
||||
.descriptorBindingUniformBufferUpdateAfterBind = VK_FALSE, // Todo: only optional!
|
||||
.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE,
|
||||
.descriptorBindingStorageImageUpdateAfterBind = VK_TRUE,
|
||||
.descriptorBindingStorageBufferUpdateAfterBind = VK_TRUE,
|
||||
.descriptorBindingPartiallyBound = VK_TRUE,
|
||||
.descriptorBindingVariableDescriptorCount = VK_TRUE,
|
||||
.runtimeDescriptorArray = VK_TRUE,
|
||||
};
|
||||
|
||||
VkDeviceCreateInfo const createInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
.pNext = &descIndexing,
|
||||
.flags = 0,
|
||||
.queueCreateInfoCount = numQueueCreateInfo,
|
||||
.pQueueCreateInfos = queueCreateInfo.data(),
|
||||
.enabledLayerCount = (uint32_t)extensions.requiredLayers.size(),
|
||||
.ppEnabledLayerNames = extensions.requiredLayers.data(),
|
||||
.enabledExtensionCount = requiredExtensions.size(),
|
||||
.ppEnabledExtensionNames = requiredExtensions.data(),
|
||||
.pEnabledFeatures = &deviceFeatures,
|
||||
};
|
||||
|
||||
VkDevice device = nullptr;
|
||||
if (auto result = vkCreateDevice(physicalDevice, &createInfo, nullptr, &device); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"Couldn't create vulkanDevice %S", string_VkResult(result));
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
static VkPhysicalDeviceProperties g_PhysicalDeviceProperties;
|
||||
static VkInstance g_VkInstance;
|
||||
|
||||
VulkanObj* initVulkan(SDL_Window* window, VkSurfaceKHR& surface, bool enableValidation) {
|
||||
LOG_USE_MODULE(vulkanSetup);
|
||||
auto obj = new VulkanObj;
|
||||
|
||||
// Create Instance&Debug
|
||||
VkDebugUtilsMessengerCreateInfoEXT dbgCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.messageSeverity =
|
||||
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) | static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) |
|
||||
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) | static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT),
|
||||
.messageType = static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) |
|
||||
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) |
|
||||
static_cast<uint32_t>(VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT),
|
||||
.pfnUserCallback = debugMessengerCallback,
|
||||
.pUserData = nullptr,
|
||||
};
|
||||
|
||||
VkApplicationInfo const appInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pNext = nullptr,
|
||||
.pApplicationName = "psOff",
|
||||
.applicationVersion = 1,
|
||||
.pEngineName = "psEngine",
|
||||
.engineVersion = 1,
|
||||
.apiVersion = VK_API_VERSION_1_3,
|
||||
};
|
||||
|
||||
auto extensions = getExtensions(window, enableValidation);
|
||||
|
||||
VkInstanceCreateInfo const instInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pNext = extensions.enableValidationLayers ? &dbgCreateInfo : nullptr,
|
||||
.flags = 0,
|
||||
.pApplicationInfo = &appInfo,
|
||||
.enabledLayerCount = (uint32_t)extensions.requiredLayers.size(),
|
||||
.ppEnabledLayerNames = extensions.requiredLayers.data(),
|
||||
.enabledExtensionCount = (uint32_t)extensions.requiredExtensions.size(),
|
||||
.ppEnabledExtensionNames = extensions.requiredExtensions.data(),
|
||||
};
|
||||
|
||||
if (VkResult result = vkCreateInstance(&instInfo, nullptr, &obj->deviceInfo.instance); result != VK_SUCCESS) {
|
||||
if (result == VK_ERROR_INCOMPATIBLE_DRIVER)
|
||||
LOG_CRIT(L"vkCreateInstance() Error:VK_ERROR_INCOMPATIBLE_DRIVER");
|
||||
else
|
||||
LOG_CRIT(L"vkCreateInstance() Error:%S", string_VkResult(result));
|
||||
}
|
||||
g_VkInstance = obj->deviceInfo.instance;
|
||||
|
||||
if (extensions.enableValidationLayers) {
|
||||
// dbgCreateInfo.pNext = nullptr;
|
||||
if (auto result = createDebugUtilsMessengerEXT(obj->deviceInfo.instance, &dbgCreateInfo, nullptr, &obj->debugMessenger); result != VK_SUCCESS) {
|
||||
LOG_CRIT(L"createDebugUtilsMessengerEXT() %S", string_VkResult(result));
|
||||
}
|
||||
}
|
||||
// -
|
||||
|
||||
if (auto result = SDL_Vulkan_CreateSurface(window, obj->deviceInfo.instance, &surface); result != true) {
|
||||
LOG_CRIT(L"SDL_Vulkan_CreateSurface() Error:%S", SDL_GetError());
|
||||
}
|
||||
|
||||
VulkanQueues queues;
|
||||
findPhysicalDevice(obj->deviceInfo.instance, surface, &obj->surfaceCapabilities, &obj->deviceInfo.physicalDevice, &queues, enableValidation);
|
||||
if (obj->deviceInfo.physicalDevice == nullptr) {
|
||||
LOG_CRIT(L"Couldn't find a suitable device");
|
||||
}
|
||||
|
||||
vkGetPhysicalDeviceProperties(obj->deviceInfo.physicalDevice, &g_PhysicalDeviceProperties);
|
||||
|
||||
{
|
||||
auto const text =
|
||||
std::format("Selected GPU:{} api:{}.{}.{}", g_PhysicalDeviceProperties.deviceName, VK_API_VERSION_MAJOR(g_PhysicalDeviceProperties.apiVersion),
|
||||
VK_API_VERSION_MINOR(g_PhysicalDeviceProperties.apiVersion), VK_API_VERSION_PATCH(g_PhysicalDeviceProperties.apiVersion));
|
||||
printf("%s\n", text.data());
|
||||
LOG_INFO(L"%S", text.data());
|
||||
}
|
||||
|
||||
obj->deviceInfo.device = createDevice(obj->deviceInfo.physicalDevice, surface, extensions, queues, enableValidation);
|
||||
|
||||
// Create queues
|
||||
{
|
||||
auto queueFunc = [](VkDevice device, std::vector<VulkanQueues::QueueInfo> const queueInfos, std::vector<std::unique_ptr<QueueInfo>>& out) {
|
||||
for (auto& item: queueInfos) {
|
||||
VkQueue queue;
|
||||
vkGetDeviceQueue(device, item.family, item.index, &queue);
|
||||
out.push_back(std::make_unique<QueueInfo>(queue, item.family));
|
||||
}
|
||||
};
|
||||
|
||||
queueFunc(obj->deviceInfo.device, queues.compute, obj->queues.items[getIndex(QueueType::compute)]);
|
||||
queueFunc(obj->deviceInfo.device, queues.present, obj->queues.items[getIndex(QueueType::present)]);
|
||||
queueFunc(obj->deviceInfo.device, queues.transfer, obj->queues.items[getIndex(QueueType::transfer)]);
|
||||
queueFunc(obj->deviceInfo.device, queues.graphics, obj->queues.items[getIndex(QueueType::graphics)]);
|
||||
}
|
||||
//-
|
||||
return obj;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceLimits const* getPhysicalLimits() {
|
||||
return &g_PhysicalDeviceProperties.limits;
|
||||
}
|
||||
|
||||
VkInstance const getVkInstance() {
|
||||
return g_VkInstance;
|
||||
}
|
||||
|
||||
std::string_view const getGPUName() {
|
||||
return g_PhysicalDeviceProperties.deviceName;
|
||||
}
|
||||
|
||||
void deinitVulkan(VulkanObj* obj) {
|
||||
vkDestroyInstance(obj->deviceInfo.instance, nullptr);
|
||||
vkDestroyDevice(obj->deviceInfo.device, nullptr);
|
||||
|
||||
delete obj;
|
||||
}
|
||||
} // namespace vulkan
|
@ -1,89 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace vulkan {
|
||||
|
||||
struct DeviceInfo {
|
||||
VkInstance instance = nullptr;
|
||||
VkPhysicalDevice physicalDevice = nullptr;
|
||||
VkDevice device = nullptr;
|
||||
};
|
||||
|
||||
enum class QueueType : uint8_t {
|
||||
graphics = 0,
|
||||
compute,
|
||||
transfer,
|
||||
present,
|
||||
numTypes,
|
||||
};
|
||||
|
||||
constexpr std::underlying_type<QueueType>::type getIndex(QueueType type) {
|
||||
return (std::underlying_type<QueueType>::type)type;
|
||||
}
|
||||
|
||||
struct QueueInfo {
|
||||
VkQueue queue = nullptr;
|
||||
uint32_t family = 0;
|
||||
size_t useCount = 0;
|
||||
|
||||
mutable std::mutex mutex; // sync queue submit access
|
||||
|
||||
QueueInfo(VkQueue queue_, uint32_t family_): queue(queue_), family(family_) {}
|
||||
};
|
||||
|
||||
struct Queues {
|
||||
std::array<std::vector<std::unique_ptr<QueueInfo>>, getIndex(QueueType::numTypes)> items {}; /// first: VkQueue, second: familyindex
|
||||
};
|
||||
|
||||
struct SwapchainData {
|
||||
|
||||
struct DisplayBuffers {
|
||||
uint64_t bufferVaddr = 0;
|
||||
uint32_t bufferSize = 0;
|
||||
uint32_t bufferAlign = 0;
|
||||
};
|
||||
|
||||
std::vector<DisplayBuffers> buffers;
|
||||
};
|
||||
|
||||
struct SurfaceCapabilities {
|
||||
VkSurfaceCapabilitiesKHR capabilities {};
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
|
||||
bool format_srgb_bgra32 = false;
|
||||
bool format_unorm_bgra32 = false;
|
||||
};
|
||||
|
||||
struct VulkanObj {
|
||||
DeviceInfo deviceInfo;
|
||||
|
||||
VkDebugUtilsMessengerEXT debugMessenger = nullptr;
|
||||
SurfaceCapabilities surfaceCapabilities;
|
||||
Queues queues;
|
||||
};
|
||||
|
||||
VulkanObj* initVulkan(SDL_Window* window, VkSurfaceKHR& surface, bool useValidation);
|
||||
void deinitVulkan(VulkanObj* obj);
|
||||
|
||||
void createSurface(VulkanObj* obj, SDL_Window* window, VkSurfaceKHR& surfaceOut);
|
||||
|
||||
VkPhysicalDeviceLimits const* getPhysicalLimits();
|
||||
|
||||
VkInstance const getVkInstance();
|
||||
|
||||
std::string_view const getGPUName();
|
||||
|
||||
std::pair<VkFormat, VkColorSpaceKHR> getDisplayFormat(VulkanObj* obj);
|
||||
} // namespace vulkan
|
23
docs/README.md
Normal file
23
docs/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Docs README
|
||||
|
||||
## Wiki update rules
|
||||
|
||||
### General rules
|
||||
1. You should not put anything to root wiki directory (`docs/wiki/`);
|
||||
2. All the `.md` files should go to `docs/wiki/markdown/` folder;
|
||||
3. Your links should not lead to shady sites;
|
||||
4. If you removed screenshot from the markdown code then delete the image itself in the `docs/wiki/markdown/images/` folder.
|
||||
|
||||
### Name your wiki files properly!
|
||||
1. Do not use the following characters in your wiki page's titles: `\ / : * ? " < > |`;
|
||||
2. Use `-` sign instead of space;
|
||||
3. Too long name is not good at all, keep it ~32 chars long maximum;
|
||||
4. It's cool that GitHub's markup engine supports so much markup languages, but keep it `md` for consistency, please. Markdowns are the easiest to maintain and most common pretty everywhere;
|
||||
5. You should write wiki pages **only** in English. We also do not accept translated pages there since GitHub does not support them. The wiki would be a mess if we did accept these changes.
|
||||
|
||||
### Add screenshots if needed!
|
||||
1. Don't do it too often;
|
||||
2. Add screenshots that will actually help;
|
||||
3. Draw eye directing arrows or circles of some kind on them, if possible and necessary;
|
||||
4. Screenshots should be uploaded to this repository only, there are special folder for these: `docs/wiki/images/`, just use relative \(`/docs/wiki/images/image.png`\) links to add them in your markdown page;
|
||||
5. Give your screenshots meaningful file names and put them in sub-directories so they don't get lost.
|
33
docs/json/audio.json
Normal file
33
docs/json/audio.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"device": {
|
||||
"description": "The name of your sound output device e.g. \"Speakers (Realtek(R) Audio)\".\n* \"[default]\" means that emulator should use OS default audio device.\n* \"[null]\" means that emulator should not send the received data to any of audio devices.",
|
||||
"type": "string"
|
||||
},
|
||||
"volume": {
|
||||
"description": "Master volume for every emulator's sound output.",
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 1.0
|
||||
},
|
||||
"padspeakers": {
|
||||
"type": "array",
|
||||
"description": "Host audio devices for each of connected gamepads.\n* \"[null]\" means that emulator should not send the received data to any of audio devices.",
|
||||
"minItems": 4,
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"device",
|
||||
"volume",
|
||||
"padspeakers"
|
||||
]
|
||||
}
|
194
docs/json/controls.json
Normal file
194
docs/json/controls.json
Normal file
@ -0,0 +1,194 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"button": {
|
||||
"type": "string",
|
||||
"description": "This gamepad button will be pressed when you press the speified button on the keyboard.\n\"gamepad_key\": \"keyboard_key\"\n\nYou can find keyboard key names there: <https://wiki.libsdl.org/SDL2/SDL_Scancode>."
|
||||
},
|
||||
"deadzone": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"x": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 0.9
|
||||
},
|
||||
"y": {
|
||||
"type": "number",
|
||||
"minimum": 0.0,
|
||||
"maximum": 0.9
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"x",
|
||||
"y"
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"keybinds": {
|
||||
"type": "object",
|
||||
"description": "Information about keybinds for gamepad emulation, hover your cursor on any key inside this object to get more info.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"gamereport.send": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"overlay.open": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.circle": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.cross": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.dpad_down": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.dpad_left": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.dpad_right": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.dpad_up": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.l1": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.l2": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.l3": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.lx+": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.lx-": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.ly+": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.ly-": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.options": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.r1": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.r2": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.r3": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.rx+": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.rx-": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.ry+": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.ry-": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.square": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.touchpad": {
|
||||
"$ref": "#/definitions/button"
|
||||
},
|
||||
"controller.triangle": {
|
||||
"$ref": "#/definitions/button"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"gamereport.send",
|
||||
"controller.circle",
|
||||
"controller.cross",
|
||||
"controller.dpad_down",
|
||||
"controller.dpad_left",
|
||||
"controller.dpad_right",
|
||||
"controller.dpad_up",
|
||||
"controller.l1",
|
||||
"controller.l2",
|
||||
"controller.l3",
|
||||
"controller.lx+",
|
||||
"controller.lx-",
|
||||
"controller.ly+",
|
||||
"controller.ly-",
|
||||
"controller.options",
|
||||
"controller.r1",
|
||||
"controller.r2",
|
||||
"controller.r3",
|
||||
"controller.rx+",
|
||||
"controller.rx-",
|
||||
"controller.ry+",
|
||||
"controller.ry-",
|
||||
"controller.square",
|
||||
"controller.touchpad",
|
||||
"controller.triangle"
|
||||
]
|
||||
},
|
||||
"pads": {
|
||||
"type": "array",
|
||||
"description": "This array contains information about connected pads.",
|
||||
"minItems": 4,
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"description": "The pad information itself.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"deadzones": {
|
||||
"type": "object",
|
||||
"description": "Dead zones are WIP, not working for now.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"left_stick": {
|
||||
"$ref": "#/definitions/deadzone"
|
||||
},
|
||||
"right_stick": {
|
||||
"$ref": "#/definitions/deadzone"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"left_stick",
|
||||
"right_stick"
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Backend for this gamepad. Possible values:\n* SDL (almost any gamepad): sdl\n* All Xbox-compatible gamepads: xinput\n* Gamepad emulation using keyboard: keyboard",
|
||||
"enum": [
|
||||
"sdl",
|
||||
"xinput",
|
||||
"keyboard"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"deadzones"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"keybinds",
|
||||
"pads"
|
||||
]
|
||||
}
|
82
docs/json/general.json
Normal file
82
docs/json/general.json
Normal file
@ -0,0 +1,82 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"netEnabled": {
|
||||
"type": "boolean",
|
||||
"description": "Network subsystem state, it is not recomended to enable network, it may cause your emulator to crash."
|
||||
},
|
||||
"netMAC": {
|
||||
"type": "string",
|
||||
"description": "You should specify your host adapter MAC address here. This adapter will be used for network connection to game's servers. This option is ignored if \"netEnabled\" set to `false`.\n\n00:00:00:00:00:00 means \"first available adapter with IPv4 address\".",
|
||||
"pattern": "(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$"
|
||||
},
|
||||
"onlineUsers": {
|
||||
"type": "integer",
|
||||
"description": "This option specifies the number of logged in users. The maximum number of users is 4. Every user must have their own configured gamepad (see controls.json).",
|
||||
"minimum": 1,
|
||||
"maximum": 4
|
||||
},
|
||||
"profiles": {
|
||||
"type": "array",
|
||||
"description": "This array contains information about all the console users. The specified name and lightbar color will be sent to the game on request.",
|
||||
"minItems": 4,
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"color": {
|
||||
"type": "string",
|
||||
"description": "The user color, probably unused.",
|
||||
"enum": [
|
||||
"blue",
|
||||
"red",
|
||||
"green",
|
||||
"pink"
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The displayed user name, the game may draw it somewhere.",
|
||||
"minLength": 3,
|
||||
"maxLength": 16
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"color",
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"systemlang": {
|
||||
"type": "integer",
|
||||
"description": "The game language, defaults to EnglishUS. Game must support it.\n Languages list: <https://github.com/SysRay/psOff_public/wiki/Config-files#generaljson>.",
|
||||
"minimum": 0,
|
||||
"maximum": 29
|
||||
},
|
||||
"userIndex": {
|
||||
"type": "number",
|
||||
"description": "Index of the user who launched the game, should be less than \"onlineUsers\" or equal.",
|
||||
"minimum": 1,
|
||||
"maximum": 4
|
||||
},
|
||||
"trophyKey": {
|
||||
"type": "string",
|
||||
"description": "ERK trophy key in hex format. You should dump it from your console, otherwise trophies will not work.",
|
||||
"pattern": "^(0x)?[0-9A-Fa-f]*$",
|
||||
"maximum": 32
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"netEnabled",
|
||||
"netMAC",
|
||||
"profiles",
|
||||
"systemlang",
|
||||
"userIndex",
|
||||
"trophyKey"
|
||||
]
|
||||
}
|
43
docs/json/graphics.json
Normal file
43
docs/json/graphics.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"display": {
|
||||
"type": "number",
|
||||
"description": "The display index where emulator windows will be shown."
|
||||
},
|
||||
"fullscreen": {
|
||||
"type": "boolean",
|
||||
"description": "Wether emulator will run in fullscreen mode or not."
|
||||
},
|
||||
"width": {
|
||||
"type": "number",
|
||||
"description": "The emulator window width, ignored in fullscreen mode.",
|
||||
"minimum": 400
|
||||
},
|
||||
"height": {
|
||||
"type": "number",
|
||||
"description": "The emulator window height, ignored in fullscreen mode.",
|
||||
"minimum": 225
|
||||
},
|
||||
"xpos": {
|
||||
"type": "number",
|
||||
"description": "The emulator window horizontal position."
|
||||
},
|
||||
"ypos": {
|
||||
"type": "number",
|
||||
"description": "The emulator window vertical position."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"display",
|
||||
"fullscreen",
|
||||
"width",
|
||||
"height",
|
||||
"xpos",
|
||||
"ypos"
|
||||
]
|
||||
}
|
36
docs/json/logging.json
Normal file
36
docs/json/logging.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"sink": {
|
||||
"type": "string",
|
||||
"description": "Logging output.",
|
||||
"enum": [
|
||||
"Null",
|
||||
"Auto",
|
||||
"Syslog",
|
||||
"Console",
|
||||
"FileTxt",
|
||||
"FileBin",
|
||||
"Baical"
|
||||
]
|
||||
},
|
||||
"verbosity": {
|
||||
"type": "integer",
|
||||
"description": "Verbosity level of logger.\nPossible values:\n0 - Trace\n1 - Debug\n2 - Warning\n3 - Error\n4 - Critical\n5 - None",
|
||||
"minimum": 0,
|
||||
"maximum": 5
|
||||
},
|
||||
"_customVerb": {
|
||||
"type": "object",
|
||||
"description": "Custom verbosity level for each module."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sink",
|
||||
"verbosity"
|
||||
]
|
||||
}
|
BIN
docs/wiki/images/vsbuild/cmake_build.png
Normal file
BIN
docs/wiki/images/vsbuild/cmake_build.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
docs/wiki/images/vsbuild/cmake_configure.png
Normal file
BIN
docs/wiki/images/vsbuild/cmake_configure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user