mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-24 21:06:55 +02:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e8e335b7b | |||
| 947576363e | |||
| 9da390bc0a | |||
| ef639610e7 | |||
| 6c3b562822 | |||
| f1413569ce | |||
| fcc9dff5b9 |
+47
-22
@@ -208,34 +208,66 @@ jobs:
|
||||
run: |
|
||||
archive="$RUNNER_TEMP/MoltenVK-macos.tar"
|
||||
package="$RUNNER_TEMP/MoltenVK"
|
||||
install_dir="_Build/macos/install"
|
||||
app_dir="$install_dir/KytyPS5.app"
|
||||
moltenvk="$install_dir/libMoltenVK.dylib"
|
||||
curl --fail --location --retry 3 \
|
||||
--output "$archive" \
|
||||
"https://github.com/KhronosGroup/MoltenVK/releases/download/$MOLTENVK_VERSION/MoltenVK-macos.tar"
|
||||
echo "$MOLTENVK_SHA256 $archive" | shasum -a 256 --check
|
||||
tar -xf "$archive" -C "$RUNNER_TEMP"
|
||||
test -d "$app_dir"
|
||||
install -m 755 \
|
||||
"$package/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" \
|
||||
_Build/macos/install/libMoltenVK.dylib
|
||||
install -m 644 "$package/LICENSE" _Build/macos/install/LICENSE.MoltenVK
|
||||
codesign --force --sign - --timestamp=none _Build/macos/install/libMoltenVK.dylib
|
||||
"$moltenvk"
|
||||
install -m 644 "$package/LICENSE" "$install_dir/LICENSE.MoltenVK"
|
||||
codesign --force --sign - --timestamp=none "$moltenvk"
|
||||
install -d -m 755 "$app_dir/Contents/Frameworks" "$app_dir/Contents/Resources"
|
||||
install -m 755 "$moltenvk" "$app_dir/Contents/Frameworks/libMoltenVK.dylib"
|
||||
install -m 644 "$package/LICENSE" "$app_dir/Contents/Resources/LICENSE.MoltenVK"
|
||||
codesign --force --sign - --timestamp=none "$app_dir"
|
||||
|
||||
- name: Verify artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
file _Build/macos/install/launcher
|
||||
file _Build/macos/install/kyty_emulator
|
||||
file _Build/macos/install/libMoltenVK.dylib
|
||||
lipo _Build/macos/install/launcher -verify_arch x86_64
|
||||
lipo _Build/macos/install/kyty_emulator -verify_arch x86_64
|
||||
lipo _Build/macos/install/libMoltenVK.dylib -verify_arch x86_64
|
||||
codesign --verify --strict _Build/macos/install/kyty_emulator
|
||||
codesign --verify --strict _Build/macos/install/libMoltenVK.dylib
|
||||
install_dir="_Build/macos/install"
|
||||
app_dir="$install_dir/KytyPS5.app"
|
||||
test -d "$app_dir"
|
||||
for binary in \
|
||||
"$install_dir/kyty_emulator" \
|
||||
"$install_dir/libMoltenVK.dylib" \
|
||||
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||
file "$binary"
|
||||
lipo "$binary" -verify_arch x86_64
|
||||
done
|
||||
for binary in \
|
||||
"$install_dir/kyty_emulator" \
|
||||
"$install_dir/libMoltenVK.dylib" \
|
||||
"$app_dir/Contents/MacOS/KytyPS5" \
|
||||
"$app_dir/Contents/MacOS/kyty_emulator" \
|
||||
"$app_dir/Contents/Frameworks/libMoltenVK.dylib"; do
|
||||
codesign --verify --strict "$binary"
|
||||
done
|
||||
codesign --verify --deep --strict --verbose=2 "$app_dir"
|
||||
|
||||
- name: Package macOS artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
install_dir="_Build/macos/install"
|
||||
archive="$RUNNER_TEMP/KytyPS5-macOS-x86_64.zip"
|
||||
(
|
||||
cd "$install_dir"
|
||||
zip -qry "$archive" .
|
||||
)
|
||||
unzip -t "$archive"
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: KytyPS5-macOS-x86_64
|
||||
path: _Build/macos/install/**
|
||||
path: ${{ runner.temp }}/KytyPS5-macOS-x86_64.zip
|
||||
if-no-files-found: error
|
||||
|
||||
linux:
|
||||
@@ -424,16 +456,9 @@ jobs:
|
||||
if: needs.macos.result == 'success'
|
||||
shell: bash
|
||||
run: |
|
||||
macos_dir="artifacts/KytyPS5-macOS-x86_64"
|
||||
test -d "$macos_dir"
|
||||
chmod a+x \
|
||||
"$macos_dir/launcher" \
|
||||
"$macos_dir/kyty_emulator" \
|
||||
"$macos_dir/libMoltenVK.dylib"
|
||||
(
|
||||
cd "$macos_dir"
|
||||
zip -r "$GITHUB_WORKSPACE/$RELEASE_NAME-macOS-x86_64.zip" .
|
||||
)
|
||||
macos_archive="artifacts/KytyPS5-macOS-x86_64/KytyPS5-macOS-x86_64.zip"
|
||||
test -f "$macos_archive"
|
||||
cp "$macos_archive" "$RELEASE_NAME-macOS-x86_64.zip"
|
||||
|
||||
- name: Package Linux build
|
||||
if: needs.linux.result == 'success'
|
||||
|
||||
@@ -220,17 +220,23 @@ cmake --install _Build/macos --prefix _Build/macos/install
|
||||
```
|
||||
|
||||
The build re-signs `kyty_emulator` with the JIT entitlements it needs to execute translated
|
||||
guest code; no manual signing step is required.
|
||||
guest code; no manual signing step is required. When the launcher is built, the install
|
||||
also produces `_Build/macos/install/KytyPS5.app` — double-click to launch the GUI.
|
||||
A flat `kyty_emulator` is kept for CLI usage.
|
||||
|
||||
Vulkan comes from MoltenVK. Download `MoltenVK-macos.tar` from the
|
||||
[MoltenVK releases](https://github.com/KhronosGroup/MoltenVK/releases), then copy
|
||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to `kyty_emulator` and ad-hoc sign it:
|
||||
`MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib` next to the flat `kyty_emulator`
|
||||
(and, for the bundle, into `KytyPS5.app/Contents/Frameworks/`) and ad-hoc sign it:
|
||||
|
||||
```bash
|
||||
codesign --force --sign - _Build/macos/install/libMoltenVK.dylib
|
||||
# For the bundle (if present):
|
||||
codesign --force --sign - _Build/macos/install/KytyPS5.app/Contents/Frameworks/libMoltenVK.dylib
|
||||
codesign --force --sign - _Build/macos/install/KytyPS5.app
|
||||
```
|
||||
|
||||
Release archives already include a signed `libMoltenVK.dylib`.
|
||||
Release archives already include a signed `libMoltenVK.dylib` (both flat and inside the bundle).
|
||||
|
||||
### Regression tests
|
||||
|
||||
@@ -274,6 +280,10 @@ To use the graphical launcher:
|
||||
./_Build/linux/install/launcher
|
||||
```
|
||||
|
||||
```bash
|
||||
open _Build/macos/install/KytyPS5.app # or double-click in Finder
|
||||
```
|
||||
|
||||
On first launch, add one or more game folders in the global settings. The launcher searches those
|
||||
folders recursively for game directories containing `eboot.bin`. Select a detected game and run it
|
||||
from the game list.
|
||||
@@ -288,12 +298,17 @@ The emulator can also be started directly with a legally obtained game directory
|
||||
./_Build/linux/install/kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
On macOS, point SDL at the MoltenVK library explicitly; the hardened runtime prevents it from
|
||||
being picked up from the executable's directory:
|
||||
On macOS, the adjacent flat or app-bundled `libMoltenVK.dylib` is found automatically; no
|
||||
environment variable is required:
|
||||
|
||||
```bash
|
||||
cd _Build/macos/install
|
||||
SDL_VULKAN_LIBRARY="$PWD/libMoltenVK.dylib" ./kyty_emulator --game "/games/ExampleGame"
|
||||
./_Build/macos/install/kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
To override the Vulkan loader, set `SDL_VULKAN_LIBRARY`:
|
||||
|
||||
```bash
|
||||
SDL_VULKAN_LIBRARY=/path/to/libMoltenVK.dylib ./kyty_emulator --game "/games/ExampleGame"
|
||||
```
|
||||
|
||||
Run `kyty_emulator --help` to see the available graphics, logging, validation, profiling, and
|
||||
|
||||
@@ -59,7 +59,7 @@ struct ConfigOptions {
|
||||
bool command_buffer_dump_enabled = false;
|
||||
std::filesystem::path command_buffer_dump_folder = "_Buffers";
|
||||
bool graphics_debug_dump_enabled = false;
|
||||
OutputDirection printf_direction = OutputDirection::Console;
|
||||
OutputDirection printf_direction = OutputDirection::Silent;
|
||||
std::filesystem::path printf_output_file = "_kyty.txt";
|
||||
ProfilerDirection profiler_direction = ProfilerDirection::None;
|
||||
bool spirv_debug_printf_enabled = false;
|
||||
|
||||
+3
-6
@@ -25,9 +25,6 @@
|
||||
#ifdef CopyFile
|
||||
#undef CopyFile
|
||||
#endif
|
||||
#ifdef MoveFile
|
||||
#undef MoveFile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
@@ -544,14 +541,14 @@ bool File::CopyFile(const std::filesystem::path& src,
|
||||
return SysFileCopyFile(src, dst);
|
||||
}
|
||||
|
||||
bool File::MoveFile(const std::filesystem::path& src,
|
||||
const std::filesystem::path& dst) // @suppress("Member declaration not found")
|
||||
bool File::RenameFile(const std::filesystem::path& src,
|
||||
const std::filesystem::path& dst) // @suppress("Member declaration not found")
|
||||
{
|
||||
if (IsFileExisting(dst)) {
|
||||
DeleteFile(dst); // @suppress("Invalid arguments")
|
||||
}
|
||||
|
||||
return SysFileMoveFile(src, dst);
|
||||
return SysFileRenameFile(src, dst);
|
||||
}
|
||||
|
||||
void File::RemoveReadonly(const std::filesystem::path& name) {
|
||||
|
||||
+1
-4
@@ -20,9 +20,6 @@
|
||||
#ifdef CopyFile
|
||||
#undef CopyFile
|
||||
#endif
|
||||
#ifdef MoveFile
|
||||
#undef MoveFile
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
@@ -107,7 +104,7 @@ public:
|
||||
static std::vector<FindInfo> FindFiles(const std::filesystem::path& path);
|
||||
static std::vector<DirEntry> GetDirEntries(const std::filesystem::path& path);
|
||||
static bool CopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static bool MoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static bool RenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
static void RemoveReadonly(const std::filesystem::path& name);
|
||||
|
||||
KYTY_CLASS_NO_COPY(File);
|
||||
|
||||
@@ -80,7 +80,7 @@ void SysFileFindFiles(const std::filesystem::path& path, std::vector<sys_file_fi
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
void SysFileGetDents(const std::filesystem::path& path, std::vector<sys_dir_entry_t>& out);
|
||||
bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst);
|
||||
void SysFileRemoveReadonly(const std::filesystem::path& name);
|
||||
|
||||
#endif /* KYTY_COMMON_PLATFORM_SYSFILEIO_H_ */
|
||||
|
||||
@@ -693,7 +693,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
|
||||
!error;
|
||||
}
|
||||
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
auto real_src = get_internal_name(src);
|
||||
auto real_dst = get_internal_name(dst);
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ bool SysFileCopyFile(const std::filesystem::path& src, const std::filesystem::pa
|
||||
return CopyFileW(src_wide.c_str(), dst_wide.c_str(), FALSE) != 0;
|
||||
}
|
||||
|
||||
bool SysFileMoveFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
bool SysFileRenameFile(const std::filesystem::path& src, const std::filesystem::path& dst) {
|
||||
auto src_wide = src.wstring();
|
||||
auto dst_wide = dst.wstring();
|
||||
return MoveFileW(src_wide.c_str(), dst_wide.c_str()) != 0;
|
||||
|
||||
@@ -219,8 +219,9 @@ void RenderExecutor::ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer&
|
||||
rt.attrib.num_fragments, bytes_per_element, rt.cmask.addr, rt.fmask.addr,
|
||||
rt.dcc_addr.addr);
|
||||
}
|
||||
if ((standard64 || depth_tile) && (rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 ||
|
||||
view.base_layer != 0 || view.image_layers != 1)) {
|
||||
if ((standard64 || depth_tile) &&
|
||||
(rt.attrib3.dimension != 1 || rt.attrib3.depth != 0 ||
|
||||
(depth_tile && (view.base_layer != 0 || view.image_layers != 1)))) {
|
||||
EXIT("unsupported 64KB texture-tiled render-target view: dimension=%u depth=%u"
|
||||
" layer=%u/%u\n",
|
||||
rt.attrib3.dimension, rt.attrib3.depth, view.base_layer, view.image_layers);
|
||||
|
||||
@@ -327,7 +327,7 @@ void PipelineCache::Save() {
|
||||
const bool flushed = !file.IsInvalid() && file.Flush();
|
||||
file.Close();
|
||||
if (prefix_written != prefix.size() || payload_written != payload.size() || !flushed ||
|
||||
!Common::File::MoveFile(temp_path, m_driver_cache_path)) {
|
||||
!Common::File::RenameFile(temp_path, m_driver_cache_path)) {
|
||||
PipelineCacheLog("Vulkan pipeline cache: failed to write {}",
|
||||
Common::PathToString(m_driver_cache_path));
|
||||
return;
|
||||
|
||||
@@ -942,6 +942,18 @@ void WindowContext::CreateVulkan() {
|
||||
inst_info.sType = vk::StructureType::eInstanceCreateInfo;
|
||||
inst_info.pNext = (r.enable_validation_layers ? &dbg_create_info : nullptr);
|
||||
inst_info.flags = {};
|
||||
#if defined(__APPLE__)
|
||||
// MoltenVK requires VK_KHR_portability_enumeration + flag to surface
|
||||
// portability devices. Without this, enumeratePhysicalDevices hides the
|
||||
// MoltenVK adapter on some driver versions.
|
||||
if (HasExtension(r.available_extensions, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
if (!HasExtension(r.required_extensions, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
r.required_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
}
|
||||
inst_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
||||
LOGF("Vulkan instance: enabled %s\n", VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
inst_info.pApplicationInfo = &app_info;
|
||||
inst_info.enabledExtensionCount = static_cast<uint32_t>(r.required_extensions.size());
|
||||
inst_info.ppEnabledExtensionNames = r.required_extensions.data();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "SDL.h"
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_filesystem.h"
|
||||
#include "SDL_gamecontroller.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_joystick.h"
|
||||
@@ -40,13 +41,12 @@
|
||||
#include "loader/systemContent.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fmt/format.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vulkan/vk_platform.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_SIMD
|
||||
#include "stb_image.h"
|
||||
@@ -812,6 +812,24 @@ static void WindowCreate(WindowContext& context) {
|
||||
|
||||
uint32_t window_flags = WindowContext::InitialWindowFlags(Config::FullscreenEnabled());
|
||||
#if defined(__APPLE__)
|
||||
// SDL loads Vulkan while creating a Vulkan window, so select the bundled
|
||||
// MoltenVK loader before calling SDL_CreateWindow. Keep an explicit user
|
||||
// override, and fall back to SDL's normal loader search when no bundle is present.
|
||||
if (std::getenv("SDL_VULKAN_LIBRARY") == nullptr) {
|
||||
if (char* base_path = SDL_GetBasePath(); base_path != nullptr) {
|
||||
const std::string base_path_str = base_path;
|
||||
SDL_free(base_path);
|
||||
std::string moltenvk_path = base_path_str + "libMoltenVK.dylib";
|
||||
if (!Common::File::IsFileExisting(moltenvk_path)) {
|
||||
moltenvk_path = base_path_str + "../Frameworks/libMoltenVK.dylib";
|
||||
}
|
||||
if (Common::File::IsFileExisting(moltenvk_path) &&
|
||||
SDL_setenv("SDL_VULKAN_LIBRARY", moltenvk_path.c_str(), 0) == 0) {
|
||||
LOGF("Vulkan loader: %s\n", moltenvk_path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// macOS 26 window chrome (CoreUI asset decode, SwiftUI titlebar) has been observed
|
||||
// throwing NSExceptions under Rosetta during the first CATransaction commit. A
|
||||
// borderless window skips that machinery entirely.
|
||||
|
||||
@@ -103,6 +103,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||
{0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32},
|
||||
{0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32},
|
||||
{0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32},
|
||||
{0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16},
|
||||
{0xa6u, Opcode::DS_READ_U16_D16, 1, 16},
|
||||
{0xa7u, Opcode::DS_READ_U16_D16_HI, 1, 16},
|
||||
{0xb0u, Opcode::DS_WRITE_ADDTID_B32, 1, 32}, {0xb1u, Opcode::DS_READ_ADDTID_B32, 1, 32},
|
||||
@@ -148,6 +149,7 @@ bool IsDsWriteOpcode(Opcode opcode) {
|
||||
switch (opcode) {
|
||||
case Opcode::DS_WRITE_B8:
|
||||
case Opcode::DS_WRITE_B16:
|
||||
case Opcode::DS_WRITE_B16_D16_HI:
|
||||
case Opcode::DS_WRITE2_B32:
|
||||
case Opcode::DS_WRITE2ST64_B32:
|
||||
case Opcode::DS_WRITE2_B64:
|
||||
@@ -430,6 +432,9 @@ void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
|
||||
}
|
||||
DecodeVectorGpr(addr, inst.src0);
|
||||
DecodeVectorGpr(data0, inst.src1);
|
||||
if (inst.opcode == Opcode::DS_WRITE_B16_D16_HI) {
|
||||
inst.src1.sdwa_sel = 5u;
|
||||
}
|
||||
DecodeVectorGpr(data1, inst.src2);
|
||||
inst.src_count = DsSourceCount(inst.opcode);
|
||||
}
|
||||
|
||||
@@ -675,6 +675,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::DS_READ_B128:
|
||||
case Opcode::DS_WRITE_B8:
|
||||
case Opcode::DS_WRITE_B16:
|
||||
case Opcode::DS_WRITE_B16_D16_HI:
|
||||
case Opcode::DS_WRITE2_B32:
|
||||
case Opcode::DS_WRITE2ST64_B32:
|
||||
case Opcode::DS_WRITE2_B64:
|
||||
|
||||
@@ -510,6 +510,7 @@ enum class Opcode {
|
||||
DS_READ_B128,
|
||||
DS_WRITE_B8,
|
||||
DS_WRITE_B16,
|
||||
DS_WRITE_B16_D16_HI,
|
||||
DS_WRITE2_B32,
|
||||
DS_WRITE2ST64_B32,
|
||||
DS_WRITE2_B64,
|
||||
|
||||
@@ -59,7 +59,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = {
|
||||
{0x23u, Opcode::V_MBCNT_LO_U32_B32},
|
||||
{0x24u, Opcode::V_MBCNT_HI_U32_B32},
|
||||
{0x25u, Opcode::V_ADD_NC_U32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x28u, Opcode::V_ADDC_U32},
|
||||
{0x28u, Opcode::V_ADDC_U32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
{0x2au, Opcode::V_SUBREV_CO_CI_U32},
|
||||
{0x26u, Opcode::V_SUB_NC_U32, Vop2SdwaProfile::IntegerPartialDestination},
|
||||
{0x27u, Opcode::V_SUBREV_NC_U32, Vop2SdwaProfile::IntegerFullDestination},
|
||||
|
||||
@@ -257,6 +257,7 @@ Decoder::Operand MemorySourceAt(const Decoder::Instruction& decoded, uint32_t in
|
||||
return index == 0u ? decoded.src1 : index == 1u ? decoded.src0 : decoded.src2;
|
||||
case Decoder::Opcode::DS_WRITE_B8:
|
||||
case Decoder::Opcode::DS_WRITE_B16:
|
||||
case Decoder::Opcode::DS_WRITE_B16_D16_HI:
|
||||
case Decoder::Opcode::DS_WRITE_B32:
|
||||
case Decoder::Opcode::DS_WRITE_B64:
|
||||
case Decoder::Opcode::DS_WRITE_B96:
|
||||
@@ -1061,6 +1062,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error
|
||||
case Decoder::Opcode::DS_WRITE2ST64_B64: return DS_WRITE2(inst);
|
||||
case Decoder::Opcode::DS_WRITE_B8:
|
||||
case Decoder::Opcode::DS_WRITE_B16:
|
||||
case Decoder::Opcode::DS_WRITE_B16_D16_HI:
|
||||
case Decoder::Opcode::DS_WRITE_B32:
|
||||
case Decoder::Opcode::DS_WRITE_B64:
|
||||
case Decoder::Opcode::DS_WRITE_B96:
|
||||
|
||||
@@ -1066,7 +1066,7 @@ int KYTY_SYSV_ABI KernelRename(const char* from, const char* to) {
|
||||
Common::File::DeleteFile(real_to);
|
||||
}
|
||||
|
||||
if (!Common::File::MoveFile(real_from, real_to)) {
|
||||
if (!Common::File::RenameFile(real_from, real_to)) {
|
||||
return KERNEL_ERROR_EIO;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@ public:
|
||||
|
||||
explicit GuestBackingStore(uint64_t size): m_size(size) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
m_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE,
|
||||
static_cast<DWORD>(m_size >> 32u),
|
||||
static_cast<DWORD>(m_size & 0xffffffffu), nullptr);
|
||||
m_handle = CreateFileMappingA(
|
||||
INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE | SEC_COMMIT,
|
||||
static_cast<DWORD>(m_size >> 32u), static_cast<DWORD>(m_size & 0xffffffffu), nullptr);
|
||||
if (m_handle == nullptr) {
|
||||
LOGF_COLOR(Log::Color::Red,
|
||||
"\t direct-memory backing: CreateFileMapping failed: 0x%08" PRIx32 "\n",
|
||||
@@ -659,7 +659,16 @@ public:
|
||||
#endif
|
||||
ReserveGuestRegions();
|
||||
m_backing = new GuestBackingStore(backing_size);
|
||||
EXIT_IF(!m_backing->IsAvailable());
|
||||
if (!m_backing->IsAvailable()) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
EXIT("could not reserve %" PRIu64 " MB for guest direct memory. Windows commits\n"
|
||||
"this up front, so the paging file is usually what needs to be larger.\n",
|
||||
backing_size / (1024U * 1024U));
|
||||
#else
|
||||
EXIT("could not reserve %" PRIu64 " MB for guest direct memory\n",
|
||||
backing_size / (1024U * 1024U));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
~GuestAddressSpace() {
|
||||
|
||||
@@ -66,10 +66,46 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
set(launcher_name "launcher")
|
||||
if(APPLE)
|
||||
set(launcher_name "KytyPS5")
|
||||
endif()
|
||||
|
||||
set_target_properties(launcher PROPERTIES OUTPUT_NAME ${launcher_name})
|
||||
|
||||
install(TARGETS launcher DESTINATION .)
|
||||
if(APPLE)
|
||||
# macOS bundle: KytyPS5.app with launcher as main executable and
|
||||
# kyty_emulator as helper inside Contents/MacOS. MoltenVK is found
|
||||
# next to a flat executable or in the bundle's Contents/Frameworks;
|
||||
# no rpath fixup is needed because SDL loads it by absolute path.
|
||||
# Qt deployment (Frameworks + plugins) is handled by macdeployqt.
|
||||
set_target_properties(launcher PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
|
||||
INSTALL_RPATH "@executable_path/../Frameworks"
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS launcher
|
||||
BUNDLE DESTINATION .
|
||||
RUNTIME DESTINATION .)
|
||||
|
||||
if(APPLE)
|
||||
install(TARGETS kyty_emulator DESTINATION "KytyPS5.app/Contents/MacOS")
|
||||
# Deploy and ad-hoc sign the Qt frameworks and plugins in the bundle.
|
||||
find_program(KYTY_MACDEPLOYQT NAMES macdeployqt PATHS "${Qt6_DIR}/../../../bin" NO_DEFAULT_PATH)
|
||||
if(NOT KYTY_MACDEPLOYQT)
|
||||
message(FATAL_ERROR "Could not find macdeployqt")
|
||||
endif()
|
||||
install(CODE "
|
||||
execute_process(COMMAND \"${KYTY_MACDEPLOYQT}\" \"\${CMAKE_INSTALL_PREFIX}/KytyPS5.app\"
|
||||
-codesign=-
|
||||
RESULT_VARIABLE _kyty_deploy_result)
|
||||
if(NOT _kyty_deploy_result EQUAL 0)
|
||||
message(FATAL_ERROR \"macdeployqt failed with \${_kyty_deploy_result}\")
|
||||
endif()
|
||||
")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
install(CODE "
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>KytyPS5</string>
|
||||
<key>CFBundleDisplayName</key><string>KytyPS5</string>
|
||||
<key>CFBundleIdentifier</key><string>org.kytyps5.kytyps5</string>
|
||||
<key>CFBundleVersion</key><string>@PROJECT_VERSION@</string>
|
||||
<key>CFBundleShortVersionString</key><string>@PROJECT_VERSION@</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleSignature</key><string>????</string>
|
||||
<key>CFBundleExecutable</key><string>KytyPS5</string>
|
||||
<key>LSMinimumSystemVersion</key><string>13.0</string>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
<key>NSRequiresAquaSystemAppearance</key><false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -7283,26 +7283,30 @@ public:
|
||||
std::printf("[gpu] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
void CheckRenderExecutorColorStandard4TileDiscovery() {
|
||||
constexpr const char *name = "RenderExecutorColorStandard4Tile";
|
||||
void CheckRenderExecutorColorStandardTileDiscovery() {
|
||||
constexpr const char *name = "RenderExecutorColorStandardTile";
|
||||
constexpr uintptr_t base = 0x0000000203b00000ull;
|
||||
constexpr uint64_t target_address = base + 0x4000;
|
||||
constexpr uint64_t target_size = 0x180000;
|
||||
constexpr uint64_t allocation_size = 0x184000;
|
||||
constexpr uint64_t allocation_alignment = 0x4000;
|
||||
constexpr uint64_t allocation_size = 0x190000;
|
||||
constexpr uint64_t allocation_alignment = 0x10000;
|
||||
constexpr auto format = Prospero::BufferFormat::k32Float;
|
||||
constexpr auto tile = Prospero::TileMode::kStandard4KB;
|
||||
struct TileCase {
|
||||
Prospero::TileMode tile;
|
||||
uint64_t target_offset;
|
||||
uint64_t alignment;
|
||||
};
|
||||
constexpr std::array cases{
|
||||
TileCase{Prospero::TileMode::kStandard4KB, 0x4000, 0x1000},
|
||||
TileCase{Prospero::TileMode::kStandard64KB, 0x10000, 0x10000},
|
||||
};
|
||||
EnsureRuntimeContext();
|
||||
|
||||
const auto pitch = TileGetTexturePitch(format, 256, tile);
|
||||
TileSizeAlign slice{};
|
||||
TileGetTextureSize(format, 256, 256, 1, tile, &slice, nullptr, nullptr);
|
||||
const auto odd_pitch = TileGetTexturePitch(format, 257, tile);
|
||||
const auto odd_pitch =
|
||||
TileGetTexturePitch(format, 257, Prospero::TileMode::kStandard4KB);
|
||||
TileSizeAlign odd_slice{};
|
||||
TileGetTextureSize(format, 257, 131, 1, tile, &odd_slice, nullptr, nullptr);
|
||||
TileGetTextureSize(format, 257, 131, 1, Prospero::TileMode::kStandard4KB,
|
||||
&odd_slice, nullptr, nullptr);
|
||||
Require(name, "tile layout",
|
||||
pitch == 256 && slice.size == 0x40000 && slice.align == 0x1000 &&
|
||||
odd_pitch == 288 && odd_slice.size == 0x2d000 &&
|
||||
odd_pitch == 288 && odd_slice.size == 0x2d000 &&
|
||||
odd_slice.align == 0x1000,
|
||||
"Standard4KB pitch, footprint, or block alignment is incorrect");
|
||||
|
||||
@@ -7311,17 +7315,29 @@ public:
|
||||
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(),
|
||||
allocation_size, allocation_alignment, 0, &direct_offset) == 0,
|
||||
"Standard4KB color allocation failed");
|
||||
"standard-tiled color allocation failed");
|
||||
void *mapped = reinterpret_cast<void *>(base);
|
||||
Require(name, "direct mapping",
|
||||
Libs::LibKernel::Memory::KernelMapDirectMemory(
|
||||
&mapped, allocation_size, 0x3, 0x10, direct_offset,
|
||||
allocation_alignment) == 0 &&
|
||||
mapped == reinterpret_cast<void *>(base),
|
||||
"Standard4KB color mapping failed");
|
||||
"standard-tiled color mapping failed");
|
||||
std::memset(mapped, 0, allocation_size);
|
||||
|
||||
{
|
||||
for (const auto &tile_case : cases) {
|
||||
const auto pitch = TileGetTexturePitch(format, 256, tile_case.tile);
|
||||
TileSizeAlign slice{};
|
||||
TileGetTextureSize(format, 256, 256, 1, tile_case.tile, &slice, nullptr,
|
||||
nullptr);
|
||||
Require(
|
||||
name, "tile layout",
|
||||
pitch == 256 && slice.size == 0x40000 &&
|
||||
slice.align == tile_case.alignment,
|
||||
"standard tile pitch, footprint, or block alignment is incorrect");
|
||||
|
||||
const auto target_address = base + tile_case.target_offset;
|
||||
const auto target_size = slice.size * 6ull;
|
||||
RenderContext context(m_runtime_context);
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
HW::Context registers{};
|
||||
@@ -7335,7 +7351,7 @@ public:
|
||||
registers.SetColorView(
|
||||
0, {.base_array_slice_index = 5, .last_array_slice_index = 5});
|
||||
registers.SetColorAttrib2(0, {.height = 255, .width = 255});
|
||||
registers.SetColorAttrib3(0, {.tile_mode = tile,
|
||||
registers.SetColorAttrib3(0, {.tile_mode = tile_case.tile,
|
||||
.dimension = 1,
|
||||
.cmask_pipe_aligned = true,
|
||||
.dcc_pipe_aligned = true});
|
||||
@@ -7358,7 +7374,7 @@ public:
|
||||
color.base_array_layer == 5 &&
|
||||
color.desc.info.data.address == target_address &&
|
||||
color.desc.info.data.size == target_size &&
|
||||
color.desc.info.tile_mode == tile &&
|
||||
color.desc.info.tile_mode == tile_case.tile &&
|
||||
color.desc.info.extent == vk::Extent3D{256, 256, 1} &&
|
||||
color.desc.info.resources == ImageSubresources{1, 6} &&
|
||||
color.desc.info.pitch == pitch &&
|
||||
@@ -7367,9 +7383,9 @@ public:
|
||||
color.desc.view_info.type == vk::ImageViewType::e2D &&
|
||||
color.desc.view_info.base_layer == 5 &&
|
||||
color.desc.view_info.layer_count == 1 &&
|
||||
(target_address & 0xffffu) == 0x4000 &&
|
||||
(target_address & (tile_case.alignment - 1)) == 0 &&
|
||||
image.usage.render_target && image.IsGpuModified(),
|
||||
"slice 5 Standard4KB render target was not resolved");
|
||||
"slice 5 standard-tiled render target was not resolved");
|
||||
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
resources.UnmapMemory(base, allocation_size);
|
||||
@@ -7378,11 +7394,11 @@ public:
|
||||
|
||||
Require(name, "unmap direct backing",
|
||||
Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0,
|
||||
"Standard4KB color mapping release failed");
|
||||
"standard-tiled color mapping release failed");
|
||||
Require(name, "release direct backing",
|
||||
Libs::LibKernel::Memory::KernelReleaseDirectMemory(
|
||||
direct_offset, allocation_size) == 0,
|
||||
"Standard4KB color allocation release failed");
|
||||
"standard-tiled color allocation release failed");
|
||||
std::printf("[gpu] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
@@ -12027,6 +12043,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
||||
case Opcode::DS_READ_B128:
|
||||
case Opcode::DS_WRITE_B8:
|
||||
case Opcode::DS_WRITE_B16:
|
||||
case Opcode::DS_WRITE_B16_D16_HI:
|
||||
case Opcode::DS_WRITE2_B32:
|
||||
case Opcode::DS_WRITE2ST64_B32:
|
||||
case Opcode::DS_WRITE_B32:
|
||||
@@ -13940,37 +13957,35 @@ TestCase VectorAddcUsesPerLaneCarryIn() {
|
||||
TestCase VectorAddcWritesPerLaneCarryOut() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code = {
|
||||
EncodeVop1(0x01, 1, 0),
|
||||
EncodeVop2(0x1a, 1, InlineU32(2), 1),
|
||||
EncodeVop2(0x25, 1, Vgpr(0), 1),
|
||||
EncodeVopc(0xc0, Vgpr(0), 0),
|
||||
};
|
||||
AppendVMovLiteral(&code, 2, 0xffffffffu);
|
||||
code.push_back(EncodeVop1(0x01, 3, InlineU32(1)));
|
||||
code.push_back(EncodeVop2(0x28, 5, Vgpr(2), 3));
|
||||
std::vector<u32> code = {EncodeVop1(0x01, 1, Vgpr(0)),
|
||||
EncodeVop2(0x1a, 4, InlineU32(2), 1),
|
||||
EncodeVopc(0xc7, Vgpr(0), 0)};
|
||||
AppendBufferLoadDword(&code, 26, 4);
|
||||
code.push_back(0x500434f9u);
|
||||
code.push_back(0x0c860680u);
|
||||
code.push_back(EncodeVop1(0x01, 6, 106));
|
||||
code.push_back(EncodeVop2(0x1a, 4, InlineU32(2), 1));
|
||||
AppendBufferStoreDword(&code, 6, 4);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test;
|
||||
test.name = "VectorAddcWritesPerLaneCarryOut";
|
||||
test.code = code;
|
||||
test.initial = {0x0000ffffu, 0u, 0x0000ffffu, 0u};
|
||||
// VCC is represented as the current invocation's carry bit, not a shared
|
||||
// four-lane ballot word.
|
||||
test.expected = std::vector<u32>(8, 1u);
|
||||
test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32, O::V_ADD_NC_U32,
|
||||
O::V_CMP_F_U32, O::V_ADDC_U32, O::BUFFER_STORE_DWORD,
|
||||
test.expected = {1u, 0u, 1u, 0u};
|
||||
test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32, O::V_CMP_T_U32,
|
||||
O::BUFFER_LOAD_DWORD, O::V_ADDC_U32, O::BUFFER_STORE_DWORD,
|
||||
O::S_ENDPGM};
|
||||
test.decoded_counts = {
|
||||
{"V_ADDC_U32 v2, vcc_lo, 0, v26.sdwa(sel=4,sext=1), vcc_lo", 1}};
|
||||
test.ir_counts = {{" = BitFieldSExtract ", 1}, {" = IAddCarry32 ", 2}};
|
||||
test.required_spirv = {"OpBitFieldSExtract"};
|
||||
test.compute_info.threads_num[0] = 4;
|
||||
test.compute_info.threads_num[1] = 1;
|
||||
test.compute_info.threads_num[2] = 1;
|
||||
test.compute_info.group_id[0] = true;
|
||||
test.compute_info.thread_ids_num = 1;
|
||||
test.compute_info.workgroup_register = 0;
|
||||
test.has_compute_info = true;
|
||||
test.dispatch_x = 2;
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -18100,6 +18115,37 @@ TestCase DsReadU16D16CapturedPreservesHighHalf() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsWriteB16D16HiCapturedUsesHighHalf() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovU32(&code, 1, 0);
|
||||
AppendVMovLiteral(&code, 3, 0xdeadbeefu);
|
||||
code.push_back(EncodeDs0(0x0d));
|
||||
code.push_back(EncodeDs1(0, 3, 1)); // ds_write_b32 v1, v3
|
||||
|
||||
AppendVMovU32(&code, 20, 2);
|
||||
AppendVMovLiteral(&code, 2, 0x1234abcdu);
|
||||
AppendVMovLiteral(&code, 0, 0x87654321u);
|
||||
code.push_back(0xda840000u);
|
||||
code.push_back(0x00000214u); // captured ds_write_b16_d16_hi v20, v2
|
||||
|
||||
code.push_back(EncodeDs0(0x36));
|
||||
code.push_back(EncodeDs1(4, 0, 1)); // ds_read_b32 v4, v1
|
||||
AppendStoreVgpr(&code, 4, 0);
|
||||
AppendStoreVgpr(&code, 0, 1);
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test{"DsWriteB16D16HiCapturedUsesHighHalf",
|
||||
code,
|
||||
std::vector<u32>(2, 0),
|
||||
{0x1234beefu, 0x87654321u},
|
||||
{O::V_MOV_B32, O::DS_WRITE_B32, O::DS_WRITE_B16_D16_HI,
|
||||
O::DS_READ_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"DS_WRITE_B16_D16_HI", 1}};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase DsReadU16D16HiCapturedPreservesLowHalf() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -20684,6 +20730,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(ScratchIsPrivatePerInvocation);
|
||||
AddCase(FlatStoreVariants);
|
||||
AddCase(DsReadWriteVariants);
|
||||
AddCase(DsWriteB16D16HiCapturedUsesHighHalf);
|
||||
AddCase(DsReadU16D16CapturedPreservesHighHalf);
|
||||
AddCase(DsReadU16D16HiCapturedPreservesLowHalf);
|
||||
AddCase(DsAppendConsumeUsesEncodedLdsSelector);
|
||||
@@ -24469,6 +24516,11 @@ int main(int argc, char **argv) {
|
||||
RunCase(&vulkan, Vop2SdwaAshrrevCapturedWord0SignExtends());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--sdwa-addc-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorAddcWritesPerLaneCarryOut());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--cvt-pk-i16-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, Vop3CvtPkI16I32Captured());
|
||||
@@ -24570,9 +24622,9 @@ int main(int argc, char **argv) {
|
||||
CheckStandard64RenderTargetTileRoundTrip();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--standard4-rt-only") == 0) {
|
||||
if (argc == 2 && std::strcmp(argv[1], "--standard-tile-rt-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckRenderExecutorColorStandard4TileDiscovery();
|
||||
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) {
|
||||
@@ -24693,7 +24745,7 @@ int main(int argc, char **argv) {
|
||||
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
||||
vulkan.CheckRenderExecutorColor1DDiscovery();
|
||||
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
||||
vulkan.CheckRenderExecutorColorStandard4TileDiscovery();
|
||||
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
||||
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||
vulkan.CheckUnifiedTextureCacheFlow();
|
||||
|
||||
@@ -4132,6 +4132,19 @@ void TestNewShaderDecoderArchitecture() {
|
||||
d16_hi.dst.sdwa_dst_unused == 2u && d16_hi.src0.reg == 6u,
|
||||
"DS decoder rejected the captured high-half D16 read");
|
||||
|
||||
const uint32_t d16_hi_write_ds[] = {0xda840000u, 0x00000214u};
|
||||
Instruction d16_hi_write;
|
||||
Check(DecodeInstruction(d16_hi_write_ds, 0u, d16_hi_write, &error),
|
||||
error.c_str());
|
||||
Check(d16_hi_write.family == Family::DS &&
|
||||
d16_hi_write.opcode == Opcode::DS_WRITE_B16_D16_HI &&
|
||||
d16_hi_write.word_count == 2u && d16_hi_write.src_count == 2u &&
|
||||
d16_hi_write.data_dwords == 1u && d16_hi_write.data_bits == 16u &&
|
||||
d16_hi_write.offset == 0u && !d16_hi_write.gds &&
|
||||
d16_hi_write.src0.reg == 20u && d16_hi_write.src1.reg == 2u &&
|
||||
d16_hi_write.src1.sdwa_sel == 5u,
|
||||
"DS decoder rejected the captured high-half D16 write");
|
||||
|
||||
constexpr uint32_t packed_source_selectors[][2] = {
|
||||
{0xcc0e0000u, 0x0c0a0300u}, // Source 0: instruction bit 59.
|
||||
{0xcc0e0000u, 0x140a0300u}, // Source 1: instruction bit 60.
|
||||
|
||||
Reference in New Issue
Block a user