Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
[submodule "3rdparty/LibAtrac9"]
path = 3rdparty/LibAtrac9
url = https://github.com/shadps4-emu/ext-LibAtrac9.git
[submodule "3rdparty/opus"]
path = 3rdparty/opus
url = https://github.com/xiph/opus.git
[submodule "3rdparty/ffmpeg-core"]
path = 3rdparty/ffmpeg-core
url = https://github.com/KytyPS5/ext-ffmpeg-core.git
Expand Down
1 change: 1 addition & 0 deletions 3rdparty/opus
Submodule opus added at 22244d
13 changes: 13 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,18 @@ set_target_properties(freetype PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(core/shader/recompiler)
add_subdirectory(core/Decoder/Jpeg)
add_subdirectory(core/Decoder/Png)
enable_language(C)
function(add_opus_dependency)
set(BUILD_TESTING OFF)
set(BUILD_SHARED_LIBS OFF)
set(OPUS_BUILD_SHARED_LIBRARY OFF)
set(OPUS_BUILD_TESTING OFF)
set(OPUS_BUILD_PROGRAMS OFF)
add_subdirectory(3rdparty/opus EXCLUDE_FROM_ALL)
set_target_properties(opus PROPERTIES POSITION_INDEPENDENT_CODE ON C_VISIBILITY_PRESET hidden)
target_compile_definitions(opus PRIVATE OPUS_EXPORT=)
endfunction()
add_opus_dependency()

add_subdirectory(core/libs)
add_subdirectory(core/relinker)
40 changes: 35 additions & 5 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -630,12 +630,42 @@ if(BUILD_TESTING)
endif()
string(REGEX REPLACE "_tests$" "" agcTestName "${agcTest}")
set_target_properties(${agcTest} PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME ${agcTestName} COMMAND ${agcTest})
set_tests_properties(${agcTestName} PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(${agcTestName} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set(agcModes default)
if(agcTestName STREQUAL "agc_driver_flat_store")
set(agcModes)
foreach(agcGroup IN ITEMS scalar feedback narrow unaligned vector2 vector3 vector4)
foreach(agcWave IN ITEMS wave32 wave64 read-only)
list(APPEND agcModes "${agcGroup}-${agcWave}")
endforeach()
endforeach()
elseif(agcTestName STREQUAL "agc_driver_global_vcc_base")
set(agcModes)
foreach(agcGroup IN ITEMS dword vector atomic)
foreach(agcWave IN ITEMS wave32 wave64 split)
list(APPEND agcModes "base-${agcGroup}-${agcWave}")
endforeach()
endforeach()
list(APPEND agcModes addtid-wave32 addtid-wave64 addtid-split)
endif()
foreach(agcMode IN LISTS agcModes)
set(agcRegisteredName ${agcTestName})
set(agcArguments)
if(NOT agcMode STREQUAL "default")
set(agcArguments ${agcMode})
if(NOT agcMode MATCHES "^(scalar|base-dword)-wave32$")
set(agcRegisteredName "${agcTestName}_${agcMode}")
endif()
endif()
add_test(NAME ${agcRegisteredName} COMMAND ${agcTest} ${agcArguments})
set_tests_properties(${agcRegisteredName} PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(NOT agcMode STREQUAL "default")
set_tests_properties(${agcRegisteredName} PROPERTIES TIMEOUT 30 LABELS ${agcTestName})
endif()
if(WIN32)
set_tests_properties(${agcRegisteredName} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
endforeach()
set_tests_properties(agc_driver_flat_store PROPERTIES RUN_SERIAL TRUE)
add_test(NAME agc_driver_pm4_failure COMMAND agc_driver_pm4_tests failure)
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderDiskCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,8 +1033,10 @@ int main(int argc, char** argv) {
verifyVertexTypeSpecialization();
verifyBuiltinSpecialization();
verifySpecializationLiveness();
ShaderRecompiler::ShaderDiskCache::Flush();
std::error_code error;
std::filesystem::remove_all(directory, error);
require(!error, "cannot remove the shader cache test directory: " + error.message());
std::cout << "shader disk cache tests passed\n";
return 0;
} catch (const std::exception& error) {
Expand Down
97 changes: 72 additions & 25 deletions core/libs/prx/libSceAgcDriver/tests/execution/FlatStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <cstring>
#include <iostream>
#include <string>
#include <stdexcept>
#include <vector>

namespace {
Expand All @@ -37,6 +38,26 @@ alignas(256) constexpr std::array<std::uint32_t, 45> FlatStoreCode{
0x00000f01, 0x7da80090, 0xdc708300, 0x00000a01, 0xbf810000,
};

std::vector<std::uint32_t> Kernel(const std::string& group) {
if (group == "all") return {FlatStoreCode.begin(), FlatStoreCode.end()};
std::vector<std::uint32_t> code(FlatStoreCode.begin(), FlatStoreCode.begin() + 19);
const auto append = [&](std::size_t begin, std::size_t end) {
code.insert(code.end(), FlatStoreCode.begin() + begin, FlatStoreCode.begin() + end);
};
if (group == "scalar") append(19, 25);
else if (group == "feedback") {
append(19, 21);
append(37, 44);
} else if (group == "narrow") append(25, 29);
else if (group == "unaligned") append(29, 31);
else if (group == "vector4") append(31, 33);
else if (group == "vector3") append(35, 37);
else if (group == "vector2") append(33, 35);
else throw std::invalid_argument("flat store: invalid instruction group");
code.push_back(FlatStoreCode.back());
return code;
}

class GuestBlock {
public:
explicit GuestBlock(bool writable) {
Expand Down Expand Up @@ -64,34 +85,47 @@ class GuestBlock {
std::uint8_t* block = nullptr;
};

std::vector<std::uint8_t> Expected() {
std::vector<std::uint8_t> Expected(const std::string& group) {
std::vector<std::uint8_t> image(CheckedBytes, Fill);
const auto put = [&](std::uint32_t offset, std::uint32_t value, std::uint32_t bytes) {
for (std::uint32_t byte = 0; byte < bytes; ++byte) image.at(offset + byte) = static_cast<std::uint8_t>(value >> (byte * 8u));
};
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
const std::array<std::uint32_t, 4> data{0xa1b2c300u + tid, 0x51000000u + tid * 4u, 0x00c0ffeeu + tid * 16u, 0x7e570000u + tid * 8u};
put(0x000u + tid * 4u, data[0], 4);
put(0x100u + tid * 4u, data[1], 4);
put(0x200u + tid * 4u, data[2], 4);
if (tid < MaskedThreads) put(0x300u + tid * 4u, data[2], 4);
put(0x400u + tid, data[0], 1);
put(0x480u + tid * 2u, data[1], 2);
put(0x500u + tid * 4u, data[0], 4);
put(0x601u + tid * 8u, data[3], 4);
for (std::uint32_t dword = 0; dword < 3; ++dword) put(0x800u + tid * 16u + dword * 4u, data[dword + 1u], 4);
for (std::uint32_t dword = 0; dword < 4; ++dword) put(0x1000u + tid * 16u + dword * 4u, data[dword], 4);
for (std::uint32_t dword = 0; dword < 2; ++dword) put(0x1400u + tid * 16u + dword * 4u, data[dword], 4);
if (group == "all" || group == "scalar" || group == "feedback") put(0x000u + tid * 4u, data[0], 4);
if (group == "all" || group == "scalar") {
put(0x100u + tid * 4u, data[1], 4);
put(0x200u + tid * 4u, data[2], 4);
}
if (group == "all" || group == "feedback") {
if (tid < MaskedThreads) put(0x300u + tid * 4u, data[2], 4);
put(0x500u + tid * 4u, data[0], 4);
}
if (group == "all" || group == "narrow") {
put(0x400u + tid, data[0], 1);
put(0x480u + tid * 2u, data[1], 2);
}
if (group == "all" || group == "unaligned") put(0x601u + tid * 8u, data[3], 4);
if (group == "all" || group == "vector3") {
for (std::uint32_t dword = 0; dword < 3; ++dword) put(0x800u + tid * 16u + dword * 4u, data[dword + 1u], 4);
}
if (group == "all" || group == "vector4") {
for (std::uint32_t dword = 0; dword < 4; ++dword) put(0x1000u + tid * 16u + dword * 4u, data[dword], 4);
}
if (group == "all" || group == "vector2") {
for (std::uint32_t dword = 0; dword < 2; ++dword) put(0x1400u + tid * 16u + dword * 4u, data[dword], 4);
}
}
return image;
}

void Dispatch(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const std::uint8_t* base) {
void Dispatch(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const std::uint8_t* base, const std::string& group) {
const auto address = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(base));
std::vector<std::uint32_t> userData(8, 0u);
userData[0] = static_cast<std::uint32_t>(address);
userData[1] = static_cast<std::uint32_t>(address >> 32u);
const std::span<const std::uint32_t> code(FlatStoreCode);
const auto kernel = Kernel(group);
const std::span<const std::uint32_t> code(kernel);
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(code)}}};
const ShaderRecompiler::ShaderComputeStageInfo compute{{Threads, 1, 1}, 0, {false, false, false}, false, 1};
ShaderRecompiler::RecompileRequest request{
Expand All @@ -106,34 +140,47 @@ void Dispatch(AgcDriver::VulkanDevice& device, std::uint32_t waveSize, const std
device.WaitIdle();
}

void RunStores(AgcDriver::VulkanDevice& device, GuestBlock& guest, std::uint32_t waveSize) {
void RunStores(AgcDriver::VulkanDevice& device, GuestBlock& guest, std::uint32_t waveSize, const std::string& group) {
guest.Clear();
Dispatch(device, waveSize, guest.Data());
const auto expected = Expected();
Dispatch(device, waveSize, guest.Data(), group);
const auto expected = Expected(group);
for (std::uint32_t offset = 0; offset < CheckedBytes; ++offset) {
const auto actual = guest.Data()[offset];
Require(actual == expected[offset], "flat store: wave" + std::to_string(waveSize) + " byte " + std::to_string(offset) + " is " + std::to_string(actual) + ", expected " + std::to_string(expected[offset]));
}
}

void RunReadOnly(AgcDriver::VulkanDevice& device, const GuestBlock& guest) {
Dispatch(device, 32, guest.Data());
void RunReadOnly(AgcDriver::VulkanDevice& device, const GuestBlock& guest, const std::string& group) {
Dispatch(device, 32, guest.Data(), group);
for (std::uint32_t offset = 0; offset < CheckedBytes; ++offset) {
Require(guest.Data()[offset] == Fill, "flat store: a store into a read-only range changed byte " + std::to_string(offset));
}
}

}

int main() {
int main(int argc, char** argv) {
try {
std::string mode = argc == 1 ? "all" : argv[1];
std::string group = "all";
for (const std::string candidate : {"scalar", "feedback", "narrow", "unaligned", "vector2", "vector3", "vector4"}) {
if (!mode.starts_with(candidate + "-")) continue;
group = candidate;
mode.erase(0, candidate.size() + 1u);
break;
}
Require(argc <= 2 && (mode == "all" || mode == "wave32" || mode == "wave64" || mode == "read-only"), "flat store: invalid test mode");
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
GuestBlock writable(true);
const GuestBlock readOnly(false);
RunStores(*device, writable, 32);
RunStores(*device, writable, 64);
RunReadOnly(*device, readOnly);
if (mode != "read-only") {
GuestBlock writable(true);
if (mode == "all" || mode == "wave32") RunStores(*device, writable, 32, group);
if (mode == "all" || mode == "wave64") RunStores(*device, writable, 64, group);
}
if (mode == "all" || mode == "read-only") {
const GuestBlock readOnly(false);
RunReadOnly(*device, readOnly, group);
}
std::puts("flat store tests passed");
return 0;
} catch (const std::exception& error) {
Expand Down
75 changes: 58 additions & 17 deletions core/libs/prx/libSceAgcDriver/tests/execution/GlobalVccBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#endif
#include <windows.h>
#endif
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
Expand All @@ -17,6 +18,7 @@
#include <iostream>
#include <span>
#include <string>
#include <stdexcept>
#include <vector>

namespace {
Expand Down Expand Up @@ -47,6 +49,24 @@ alignas(256) constexpr std::array<std::uint32_t, 12> VccAddtidCode{
0x006a0506, 0xdc5c8400, 0x006a0500, 0xbf810000,
};

std::vector<std::uint32_t> BaseKernel(const std::string& group) {
if (group == "all") return {VccBaseCode.begin(), VccBaseCode.end()};
std::vector<std::uint32_t> code(VccBaseCode.begin(), VccBaseCode.begin() + 8);
const auto append = [&](std::size_t begin, std::size_t end) {
code.insert(code.end(), VccBaseCode.begin() + begin, VccBaseCode.begin() + end);
};
if (group == "dword") append(8, 10);
else if (group == "vector") append(12, 14);
else if (group == "atomic") append(10, 12);
else throw std::invalid_argument("global vcc base: invalid instruction group");
code.push_back(VccBaseCode[14]);
if (group == "dword") append(17, 19);
else if (group == "vector") append(15, 17);
else append(19, 21);
code.push_back(VccBaseCode.back());
return code;
}

class GuestBlock {
public:
GuestBlock() {
Expand Down Expand Up @@ -89,15 +109,19 @@ std::vector<std::uint32_t> Initial() {
return memory;
}

std::vector<std::uint32_t> ExpectedBase() {
std::vector<std::uint32_t> ExpectedBase(const std::string& group) {
const auto initial = Initial();
auto memory = initial;
for (std::uint32_t tid = 0; tid < Threads; ++tid) {
auto* out = &memory[OutputBase + tid * OutputDwords];
for (std::uint32_t component = 0; component < 4u; ++component) out[component] = initial[VectorBase + tid * 4u + component];
out[4] = initial[DwordBase + tid];
out[5] = initial[AtomicBase + tid];
memory[AtomicBase + tid] = initial[AtomicBase + tid] + tid * 16u;
if (group == "all" || group == "vector") {
for (std::uint32_t component = 0; component < 4u; ++component) out[component] = initial[VectorBase + tid * 4u + component];
}
if (group == "all" || group == "dword") out[4] = initial[DwordBase + tid];
if (group == "all" || group == "atomic") {
out[5] = initial[AtomicBase + tid];
memory[AtomicBase + tid] = initial[AtomicBase + tid] + tid * 16u;
}
}
return memory;
}
Expand Down Expand Up @@ -166,23 +190,40 @@ void CheckRejections(const AgcDriver::VulkanDevice& device) {

}

int main() {
int main(int argc, char** argv) {
try {
const std::string mode = argc == 1 ? "all" : argv[1];
const std::array<std::string, 12> modes{
"base-dword-wave32", "base-dword-wave64", "base-dword-split",
"base-vector-wave32", "base-vector-wave64", "base-vector-split",
"base-atomic-wave32", "base-atomic-wave64", "base-atomic-split",
"addtid-wave32", "addtid-wave64", "addtid-split"
};
Require(argc <= 2 && (mode == "all" || std::find(modes.begin(), modes.end(), mode) != modes.end()), "global vcc base: invalid test mode");
const auto device = OpenVulkanTestDevice();
if (!device) return VulkanTestSkipped;
CheckRejections(*device);
if (mode == "all" || mode == "base-dword-wave32") CheckRejections(*device);
GuestBlock guest;
const auto base = ExpectedBase();
Run(*device, guest, VccBaseCode, 32, device->Target(), base, "wave32");
Run(*device, guest, VccBaseCode, 64, device->Target(), base, "wave64");
Run(*device, guest, VccBaseCode, 64, device->ComputeTarget(32), base, "wave64 split");
if (device->Target().subgroupSize < 32u) {
std::printf("addtid cases skipped, the device's subgroups are narrower than a wave (%u lanes)\n", device->Target().subgroupSize);
} else {
Run(*device, guest, VccAddtidCode, 32, device->Target(), ExpectedAddtid(32), "addtid wave32");
Run(*device, guest, VccAddtidCode, 64, device->Target(), ExpectedAddtid(64), "addtid wave64");
Run(*device, guest, VccAddtidCode, 64, device->ComputeTarget(32), ExpectedAddtid(64), "addtid wave64 split");
std::size_t checked = 0;
for (const auto& candidate : modes) {
if (mode != "all" && mode != candidate) continue;
const bool addtid = candidate.starts_with("addtid");
if (addtid && device->Target().subgroupSize < 32u) {
std::printf("%s skipped, the device's subgroups are narrower than a wave (%u lanes)\n", candidate.c_str(), device->Target().subgroupSize);
continue;
}
const std::uint32_t waveSize = candidate.ends_with("wave32") ? 32u : 64u;
const auto target = candidate.ends_with("split") ? device->ComputeTarget(32) : device->Target();
if (addtid) Run(*device, guest, VccAddtidCode, waveSize, target, ExpectedAddtid(waveSize), candidate);
else {
const auto separator = candidate.find('-', 5u);
const auto group = candidate.substr(5u, separator - 5u);
const auto code = BaseKernel(group);
Run(*device, guest, code, waveSize, target, ExpectedBase(group), candidate);
}
++checked;
}
if (checked == 0u) return VulkanTestSkipped;
std::puts("global vcc base tests passed");
return 0;
} catch (const std::exception& error) {
Expand Down
24 changes: 24 additions & 0 deletions core/libs/prx/libSceOpusCeltDec/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
add_library(libSceOpusCeltDec SHARED EXCLUDE_FROM_ALL
Export.cpp
src/Decoder.cpp
)
target_include_directories(libSceOpusCeltDec PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceOpusCeltDec PRIVATE Opus::opus)
if(NOT APPLE)
target_link_options(libSceOpusCeltDec PRIVATE LINKER:--exclude-libs,ALL)
endif()
configure_windows_unwind(libSceOpusCeltDec)
set_target_properties(libSceOpusCeltDec PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)

if(BUILD_TESTING)
add_test_executable(guest_opus_celt_dec_tests ${LIBS_INCLUDE_DIR}/tests/GuestOpusCeltDec.cpp)
target_include_directories(guest_opus_celt_dec_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_opus_celt_dec_tests PRIVATE libSceOpusCeltDec libc)
configure_windows_unwind(guest_opus_celt_dec_tests)
add_test(NAME guest_opus_celt_dec COMMAND guest_opus_celt_dec_tests)
if(WIN32)
set_tests_properties(guest_opus_celt_dec PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceOpusCeltDec>")
endif()
endif()
set_source_files_properties(Export.cpp PROPERTIES COMPILE_OPTIONS -fvisibility=default)
Loading
Loading