mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-06-14 16:18:46 +02:00
Merge 3cae2e05ae
into dc20a61527
This commit is contained in:
commit
80471a4750
|
@ -189,6 +189,7 @@ dependencies {
|
||||||
/* Other Java */
|
/* Other Java */
|
||||||
implementation 'info.debatty:java-string-similarity:2.0.0'
|
implementation 'info.debatty:java-string-similarity:2.0.0'
|
||||||
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
|
implementation 'com.github.KikiManjaro:colorpicker:v1.1.12'
|
||||||
|
implementation 'com.github.android:renderscript-intrinsics-replacement-toolkit:344be3f'
|
||||||
}
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
|
|
|
@ -414,6 +414,6 @@ namespace skyline::gpu {
|
||||||
if (!*state.settings->disableShaderCache)
|
if (!*state.settings->disableShaderCache)
|
||||||
graphicsPipelineCacheManager.emplace(state,
|
graphicsPipelineCacheManager.emplace(state,
|
||||||
state.os->publicAppFilesPath + "graphics_pipeline_cache/" + titleId);
|
state.os->publicAppFilesPath + "graphics_pipeline_cache/" + titleId);
|
||||||
graphicsPipelineManager.emplace(*this);
|
graphicsPipelineManager.emplace(*this, *state.jvm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,6 +214,9 @@ namespace skyline::gpu {
|
||||||
|
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
compilePendingDescs.erase(pipelineDescIt);
|
compilePendingDescs.erase(pipelineDescIt);
|
||||||
|
if (compilationCallback)
|
||||||
|
compilationCallback();
|
||||||
|
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,4 +255,15 @@ namespace skyline::gpu {
|
||||||
SerialisePipelineCache(gpu, pipelineCacheDir, rawData);
|
SerialisePipelineCache(gpu, pipelineCacheDir, rawData);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphicsPipelineAssembler::RegisterCompilationCallback(std::function<void()> callback) {
|
||||||
|
if (compilationCallback)
|
||||||
|
throw exception("A compilation callback is already registered");
|
||||||
|
|
||||||
|
compilationCallback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPipelineAssembler::UnregisterCompilationCallback() {
|
||||||
|
compilationCallback = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <BS_thread_pool.hpp>
|
#include <BS_thread_pool.hpp>
|
||||||
#include <vulkan/vulkan_raii.hpp>
|
#include <vulkan/vulkan_raii.hpp>
|
||||||
|
@ -59,6 +60,7 @@ namespace skyline::gpu {
|
||||||
vk::raii::PipelineCache vkPipelineCache; //!< A Vulkan Pipeline Cache which stores all unique graphics pipelines
|
vk::raii::PipelineCache vkPipelineCache; //!< A Vulkan Pipeline Cache which stores all unique graphics pipelines
|
||||||
BS::thread_pool pool;
|
BS::thread_pool pool;
|
||||||
std::string pipelineCacheDir;
|
std::string pipelineCacheDir;
|
||||||
|
std::function<void()> compilationCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief All unique metadata in a single attachment for a compatible render pass according to Render Pass Compatibility clause in the Vulkan specification
|
* @brief All unique metadata in a single attachment for a compatible render pass according to Render Pass Compatibility clause in the Vulkan specification
|
||||||
|
@ -158,5 +160,15 @@ namespace skyline::gpu {
|
||||||
* @brief Saves the current Vulkan pipeline cache to the filesystem
|
* @brief Saves the current Vulkan pipeline cache to the filesystem
|
||||||
*/
|
*/
|
||||||
void SavePipelineCache();
|
void SavePipelineCache();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a callback that is called whenever a pipeline is compiled
|
||||||
|
*/
|
||||||
|
void RegisterCompilationCallback(std::function<void()> callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregisters the compilation callback
|
||||||
|
*/
|
||||||
|
void UnregisterCompilationCallback();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <gpu/graphics_pipeline_assembler.h>
|
#include <gpu/graphics_pipeline_assembler.h>
|
||||||
#include <gpu/shader_manager.h>
|
#include <gpu/shader_manager.h>
|
||||||
#include <gpu.h>
|
#include <gpu.h>
|
||||||
|
#include <jvm.h>
|
||||||
#include <vulkan/vulkan_enums.hpp>
|
#include <vulkan/vulkan_enums.hpp>
|
||||||
#include "graphics_pipeline_state_accessor.h"
|
#include "graphics_pipeline_state_accessor.h"
|
||||||
#include "pipeline_manager.h"
|
#include "pipeline_manager.h"
|
||||||
|
@ -942,12 +943,19 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineManager::PipelineManager(GPU &gpu) {
|
PipelineManager::PipelineManager(GPU &gpu, JvmManager &jvm) {
|
||||||
if (!gpu.graphicsPipelineCacheManager)
|
if (!gpu.graphicsPipelineCacheManager)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::ifstream stream{gpu.graphicsPipelineCacheManager->OpenReadStream()};
|
std::atomic<u32> compiledCount{};
|
||||||
|
auto [stream, totalPipelineCount]{gpu.graphicsPipelineCacheManager->OpenReadStream()};
|
||||||
i64 lastKnownGoodOffset{stream.tellg()};
|
i64 lastKnownGoodOffset{stream.tellg()};
|
||||||
|
|
||||||
|
jvm.ShowPipelineLoadingScreen(totalPipelineCount);
|
||||||
|
gpu.graphicsPipelineAssembler->RegisterCompilationCallback([&]() {
|
||||||
|
jvm.UpdatePipelineLoadingProgress(++compiledCount);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto startTime{util::GetTimeNs()};
|
auto startTime{util::GetTimeNs()};
|
||||||
PipelineStateBundle bundle;
|
PipelineStateBundle bundle;
|
||||||
|
@ -986,8 +994,10 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||||
} catch (const exception &e) {
|
} catch (const exception &e) {
|
||||||
Logger::Warn("Pipeline cache corrupted at: 0x{:X}, error: {}", lastKnownGoodOffset, e.what());
|
Logger::Warn("Pipeline cache corrupted at: 0x{:X}, error: {}", lastKnownGoodOffset, e.what());
|
||||||
gpu.graphicsPipelineCacheManager->InvalidateAllAfter(static_cast<u64>(lastKnownGoodOffset));
|
gpu.graphicsPipelineCacheManager->InvalidateAllAfter(static_cast<u64>(lastKnownGoodOffset));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gpu.graphicsPipelineAssembler->UnregisterCompilationCallback();
|
||||||
|
jvm.HidePipelineLoadingScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipeline *PipelineManager::FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries) {
|
Pipeline *PipelineManager::FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries) {
|
||||||
|
|
|
@ -272,7 +272,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PipelineManager(GPU &gpu);
|
PipelineManager(GPU &gpu, JvmManager &jvm);
|
||||||
|
|
||||||
Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries);
|
Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,19 +8,26 @@
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
struct PipelineCacheFileHeader {
|
struct PipelineCacheFileHeader {
|
||||||
static constexpr u32 Magic{util::MakeMagic<u32>("PCHE")}; //!< The magic value used to identify a pipeline cache file
|
static constexpr u32 Magic{util::MakeMagic<u32>("PCHE")}; //!< The magic value used to identify a pipeline cache file
|
||||||
static constexpr u32 Version{2}; //!< The version of the pipeline cache file format, MUST be incremented for any format changes
|
static constexpr u32 Version{3}; //!< The version of the pipeline cache file format, MUST be incremented for any format changes
|
||||||
|
|
||||||
u32 magic{Magic};
|
u32 magic{Magic};
|
||||||
u32 version{Version};
|
u32 version{Version};
|
||||||
|
u32 count{0}; //!< The total number of pipeline cache bundles in the file
|
||||||
|
|
||||||
auto operator<=>(const PipelineCacheFileHeader &) const = default;
|
auto operator<=>(const PipelineCacheFileHeader &) const = default;
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr PipelineCacheFileHeader ValidPipelineCacheFileHeader{};
|
/**
|
||||||
|
* @brief Checks if the header is valid
|
||||||
|
*/
|
||||||
|
bool IsValid() {
|
||||||
|
return magic == Magic && version == Version;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void PipelineCacheManager::Run() {
|
void PipelineCacheManager::Run() {
|
||||||
std::ofstream stream{stagingPath, std::ios::binary | std::ios::trunc};
|
std::ofstream stream{stagingPath, std::ios::binary | std::ios::trunc};
|
||||||
stream.write(reinterpret_cast<const char *>(&ValidPipelineCacheFileHeader), sizeof(PipelineCacheFileHeader));
|
PipelineCacheFileHeader header{};
|
||||||
|
stream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::unique_lock lock(writeMutex);
|
std::unique_lock lock(writeMutex);
|
||||||
|
@ -31,7 +38,15 @@ namespace skyline::gpu {
|
||||||
auto bundle{std::move(writeQueue.front())};
|
auto bundle{std::move(writeQueue.front())};
|
||||||
writeQueue.pop();
|
writeQueue.pop();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
bundle->Serialise(stream);
|
bundle->Serialise(stream);
|
||||||
|
|
||||||
|
header.count++;
|
||||||
|
// Rewrite the header with the updated count
|
||||||
|
auto savedPosition{stream.tellp()};
|
||||||
|
stream.seekp(0, std::ios_base::beg);
|
||||||
|
stream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||||
|
stream.seekp(savedPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +55,8 @@ namespace skyline::gpu {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PipelineCacheFileHeader header{};
|
PipelineCacheFileHeader header{};
|
||||||
stream.read(reinterpret_cast<char *>(&header), sizeof(header));
|
stream.read(reinterpret_cast<char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||||
return header == ValidPipelineCacheFileHeader;
|
return header.IsValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCacheManager::MergeStaging() {
|
void PipelineCacheManager::MergeStaging() {
|
||||||
|
@ -49,12 +64,24 @@ namespace skyline::gpu {
|
||||||
if (stagingStream.fail())
|
if (stagingStream.fail())
|
||||||
return; // If the staging file doesn't exist then there's nothing to merge
|
return; // If the staging file doesn't exist then there's nothing to merge
|
||||||
|
|
||||||
if (!ValidateHeader(stagingStream)) {
|
PipelineCacheFileHeader stagingHeader{};
|
||||||
|
stagingStream.read(reinterpret_cast<char *>(&stagingHeader), sizeof(PipelineCacheFileHeader));
|
||||||
|
if (!stagingHeader.IsValid()) {
|
||||||
Logger::Warn("Discarding invalid pipeline cache staging file");
|
Logger::Warn("Discarding invalid pipeline cache staging file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ofstream mainStream{mainPath, std::ios::binary | std::ios::app};
|
std::fstream mainStream{mainPath, std::ios::binary | std::ios::in | std::ios::out};
|
||||||
|
PipelineCacheFileHeader mainHeader{};
|
||||||
|
mainStream.seekg(0, std::ios_base::beg);
|
||||||
|
mainStream.read(reinterpret_cast<char *>(&mainHeader), sizeof(PipelineCacheFileHeader));
|
||||||
|
|
||||||
|
// Update the main header with the new count
|
||||||
|
mainHeader.count += stagingHeader.count;
|
||||||
|
mainStream.seekp(0, std::ios_base::beg);
|
||||||
|
mainStream.write(reinterpret_cast<const char *>(&mainHeader), sizeof(PipelineCacheFileHeader));
|
||||||
|
|
||||||
|
mainStream.seekp(0, std::ios::end);
|
||||||
mainStream << stagingStream.rdbuf();
|
mainStream << stagingStream.rdbuf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +100,8 @@ namespace skyline::gpu {
|
||||||
if (!didExist) { // If the main file didn't exist we need to write the header
|
if (!didExist) { // If the main file didn't exist we need to write the header
|
||||||
std::filesystem::create_directories(std::filesystem::path{mainPath}.parent_path());
|
std::filesystem::create_directories(std::filesystem::path{mainPath}.parent_path());
|
||||||
std::ofstream mainStream{mainPath, std::ios::binary | std::ios::app};
|
std::ofstream mainStream{mainPath, std::ios::binary | std::ios::app};
|
||||||
mainStream.write(reinterpret_cast<const char *>(&ValidPipelineCacheFileHeader), sizeof(PipelineCacheFileHeader));
|
PipelineCacheFileHeader header{};
|
||||||
|
mainStream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge any staging changes into the main file before starting the writer thread
|
// Merge any staging changes into the main file before starting the writer thread
|
||||||
|
@ -87,12 +115,17 @@ namespace skyline::gpu {
|
||||||
writeCondition.notify_one();
|
writeCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream PipelineCacheManager::OpenReadStream() {
|
std::pair<std::ifstream, u32> PipelineCacheManager::OpenReadStream() {
|
||||||
auto mainStream{std::ifstream{mainPath, std::ios::binary}};
|
auto mainStream{std::ifstream{mainPath, std::ios::binary}};
|
||||||
if (!ValidateHeader(mainStream))
|
if (mainStream.fail())
|
||||||
|
throw exception("Pipeline cache main file missing at runtime!");
|
||||||
|
|
||||||
|
PipelineCacheFileHeader header{};
|
||||||
|
mainStream.read(reinterpret_cast<char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||||
|
if (!header.IsValid())
|
||||||
throw exception("Pipeline cache main file corrupted at runtime!");
|
throw exception("Pipeline cache main file corrupted at runtime!");
|
||||||
|
|
||||||
return mainStream;
|
return {std::move(mainStream), header.count};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCacheManager::InvalidateAllAfter(u64 offset) {
|
void PipelineCacheManager::InvalidateAllAfter(u64 offset) {
|
||||||
|
|
|
@ -34,7 +34,11 @@ namespace skyline::gpu {
|
||||||
*/
|
*/
|
||||||
void QueueWrite(std::unique_ptr<interconnect::PipelineStateBundle> bundle);
|
void QueueWrite(std::unique_ptr<interconnect::PipelineStateBundle> bundle);
|
||||||
|
|
||||||
std::ifstream OpenReadStream();
|
/**
|
||||||
|
* @brief Opens the main pipeline cache file for reading
|
||||||
|
* @return A pair containing the stream and the total pipeline count
|
||||||
|
*/
|
||||||
|
std::pair<std::ifstream, u32> OpenReadStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Shrinks the pipeline cache file to `offset` bytes, removing any (potentially invalid) data after that point
|
* @brief Shrinks the pipeline cache file to `offset` bytes, removing any (potentially invalid) data after that point
|
||||||
|
|
|
@ -63,8 +63,11 @@ namespace skyline {
|
||||||
waitForSubmitOrCancelId{environ->GetMethodID(instanceClass, "waitForSubmitOrCancel", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)[Ljava/lang/Object;")},
|
waitForSubmitOrCancelId{environ->GetMethodID(instanceClass, "waitForSubmitOrCancel", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)[Ljava/lang/Object;")},
|
||||||
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
|
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
|
||||||
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
|
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
|
||||||
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")},
|
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")},
|
||||||
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")} {
|
showPipelineLoadingScreenId{environ->GetMethodID(instanceClass, "showPipelineLoadingScreen", "(I)V")},
|
||||||
|
updatePipelineLoadingProgressId{environ->GetMethodID(instanceClass, "updatePipelineLoadingProgress", "(I)V")},
|
||||||
|
hidePipelineLoadingScreenId{environ->GetMethodID(instanceClass, "hidePipelineLoadingScreen", "()V")},
|
||||||
|
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")} {
|
||||||
env.Initialize(environ);
|
env.Initialize(environ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,14 +140,26 @@ namespace skyline {
|
||||||
env->DeleteGlobalRef(dialog);
|
env->DeleteGlobalRef(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 JvmManager::GetVersionCode() {
|
|
||||||
return env->CallIntMethod(instance, getVersionCodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
JvmManager::KeyboardCloseResult JvmManager::ShowValidationResult(jobject dialog, KeyboardTextCheckResult checkResult, std::u16string message) {
|
JvmManager::KeyboardCloseResult JvmManager::ShowValidationResult(jobject dialog, KeyboardTextCheckResult checkResult, std::u16string message) {
|
||||||
auto str{env->NewString(reinterpret_cast<const jchar *>(message.data()), static_cast<int>(message.length()))};
|
auto str{env->NewString(reinterpret_cast<const jchar *>(message.data()), static_cast<int>(message.length()))};
|
||||||
auto result{static_cast<KeyboardCloseResult>(env->CallIntMethod(instance, showValidationResultId, dialog, checkResult, str))};
|
auto result{static_cast<KeyboardCloseResult>(env->CallIntMethod(instance, showValidationResultId, dialog, checkResult, str))};
|
||||||
env->DeleteLocalRef(str);
|
env->DeleteLocalRef(str);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JvmManager::ShowPipelineLoadingScreen(u32 totalPipelineCount) {
|
||||||
|
env->CallVoidMethod(instance, showPipelineLoadingScreenId, static_cast<jint>(totalPipelineCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JvmManager::UpdatePipelineLoadingProgress(u32 progress) {
|
||||||
|
env->CallVoidMethod(instance, updatePipelineLoadingProgressId, static_cast<jint>(progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JvmManager::HidePipelineLoadingScreen() {
|
||||||
|
env->CallVoidMethod(instance, hidePipelineLoadingScreenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 JvmManager::GetVersionCode() {
|
||||||
|
return env->CallIntMethod(instance, getVersionCodeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,21 @@ namespace skyline {
|
||||||
*/
|
*/
|
||||||
KeyboardCloseResult ShowValidationResult(KeyboardHandle dialog, KeyboardTextCheckResult checkResult, std::u16string message);
|
KeyboardCloseResult ShowValidationResult(KeyboardHandle dialog, KeyboardTextCheckResult checkResult, std::u16string message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A call to EmulationActivity.showPipelineLoadingScreen in Kotlin
|
||||||
|
*/
|
||||||
|
void ShowPipelineLoadingScreen(u32 totalPipelineCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A call to EmulationActivity.updatePipelineLoadingProgress in Kotlin
|
||||||
|
*/
|
||||||
|
void UpdatePipelineLoadingProgress(u32 progress);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A call to EmulationActivity.hidePipelineLoadingScreen in Kotlin
|
||||||
|
*/
|
||||||
|
void HidePipelineLoadingScreen();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A call to EmulationActivity.getVersionCode in Kotlin
|
* @brief A call to EmulationActivity.getVersionCode in Kotlin
|
||||||
* @return A version code in Vulkan's format with 14-bit patch + 10-bit major and minor components
|
* @return A version code in Vulkan's format with 14-bit patch + 10-bit major and minor components
|
||||||
|
@ -186,12 +201,17 @@ namespace skyline {
|
||||||
jmethodID initializeControllersId;
|
jmethodID initializeControllersId;
|
||||||
jmethodID vibrateDeviceId;
|
jmethodID vibrateDeviceId;
|
||||||
jmethodID clearVibrationDeviceId;
|
jmethodID clearVibrationDeviceId;
|
||||||
|
|
||||||
jmethodID showKeyboardId;
|
jmethodID showKeyboardId;
|
||||||
jmethodID waitForSubmitOrCancelId;
|
jmethodID waitForSubmitOrCancelId;
|
||||||
jmethodID closeKeyboardId;
|
jmethodID closeKeyboardId;
|
||||||
jmethodID showValidationResultId;
|
jmethodID showValidationResultId;
|
||||||
jmethodID getVersionCodeId;
|
|
||||||
|
|
||||||
jmethodID getIntegerValueId;
|
jmethodID getIntegerValueId;
|
||||||
|
|
||||||
|
jmethodID showPipelineLoadingScreenId;
|
||||||
|
jmethodID updatePipelineLoadingProgressId;
|
||||||
|
jmethodID hidePipelineLoadingScreenId;
|
||||||
|
|
||||||
|
jmethodID getVersionCodeId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,12 @@ import androidx.core.view.updateMargins
|
||||||
import androidx.fragment.app.FragmentTransaction
|
import androidx.fragment.app.FragmentTransaction
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import emu.skyline.BuildConfig
|
|
||||||
import emu.skyline.applet.swkbd.SoftwareKeyboardConfig
|
import emu.skyline.applet.swkbd.SoftwareKeyboardConfig
|
||||||
import emu.skyline.applet.swkbd.SoftwareKeyboardDialog
|
import emu.skyline.applet.swkbd.SoftwareKeyboardDialog
|
||||||
import emu.skyline.data.AppItem
|
import emu.skyline.data.AppItem
|
||||||
import emu.skyline.data.AppItemTag
|
import emu.skyline.data.AppItemTag
|
||||||
import emu.skyline.databinding.EmuActivityBinding
|
import emu.skyline.databinding.EmuActivityBinding
|
||||||
|
import emu.skyline.emulation.PipelineLoadingFragment
|
||||||
import emu.skyline.input.*
|
import emu.skyline.input.*
|
||||||
import emu.skyline.loader.RomFile
|
import emu.skyline.loader.RomFile
|
||||||
import emu.skyline.loader.getRomFormat
|
import emu.skyline.loader.getRomFormat
|
||||||
|
@ -447,7 +447,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode : Boolean, newConfig : Configuration) {
|
||||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
||||||
if (isInPictureInPictureMode) {
|
if (isInPictureInPictureMode) {
|
||||||
|
|
||||||
|
@ -464,10 +464,11 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
unregisterReceiver(pictureInPictureReceiver)
|
unregisterReceiver(pictureInPictureReceiver)
|
||||||
} catch (ignored : Exception) { }
|
} catch (ignored : Exception) {
|
||||||
|
}
|
||||||
|
|
||||||
resumeEmulator()
|
resumeEmulator()
|
||||||
|
|
||||||
binding.onScreenControllerView.apply {
|
binding.onScreenControllerView.apply {
|
||||||
controllerType = inputHandler.getFirstControllerType()
|
controllerType = inputHandler.getFirstControllerType()
|
||||||
isGone = controllerType == ControllerType.None || !appSettings.onScreenControl
|
isGone = controllerType == ControllerType.None || !appSettings.onScreenControl
|
||||||
|
@ -512,6 +513,44 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||||
vibrators.clear()
|
vibrators.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var pipelineLoadingFragment : PipelineLoadingFragment
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by native code to show the pipeline loading progress screen
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
fun showPipelineLoadingScreen(totalPipelineCount : Int) {
|
||||||
|
pipelineLoadingFragment = PipelineLoadingFragment.newInstance(item, totalPipelineCount)
|
||||||
|
|
||||||
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.replace(R.id.emulation_fragment, pipelineLoadingFragment)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by native code to update the pipeline loading progress
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
fun updatePipelineLoadingProgress(progress : Int) {
|
||||||
|
runOnUiThread {
|
||||||
|
pipelineLoadingFragment.updateProgress(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by native code to hide the pipeline loading progress screen
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
fun hidePipelineLoadingScreen() {
|
||||||
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||||
|
.remove(pipelineLoadingFragment)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
|
||||||
override fun surfaceCreated(holder : SurfaceHolder) {
|
override fun surfaceCreated(holder : SurfaceHolder) {
|
||||||
Log.d(Tag, "surfaceCreated Holder: $holder")
|
Log.d(Tag, "surfaceCreated Holder: $holder")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MPL-2.0
|
||||||
|
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package emu.skyline.emulation
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.RenderEffect
|
||||||
|
import android.graphics.Shader
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.android.renderscript.Toolkit
|
||||||
|
import emu.skyline.data.AppItem
|
||||||
|
import emu.skyline.data.AppItemTag
|
||||||
|
import emu.skyline.databinding.PipelineLoadingBinding
|
||||||
|
import emu.skyline.utils.serializable
|
||||||
|
|
||||||
|
private const val TotalPipelineCountTag = "PipelineLoadingFragment::TotalCount"
|
||||||
|
private const val PipelineProgressTag = "PipelineLoadingFragment::Progress"
|
||||||
|
|
||||||
|
class PipelineLoadingFragment : Fragment() {
|
||||||
|
private val item by lazy { requireArguments().serializable<AppItem>(AppItemTag)!! }
|
||||||
|
private val totalPipelineCount by lazy { requireArguments().getInt(TotalPipelineCountTag) }
|
||||||
|
|
||||||
|
private lateinit var binding : PipelineLoadingBinding
|
||||||
|
|
||||||
|
override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) = PipelineLoadingBinding.inflate(inflater).also { binding = it }.root
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.gameTitle.apply {
|
||||||
|
text = item.title
|
||||||
|
isSelected = true
|
||||||
|
}
|
||||||
|
binding.gameVersion.text = item.version
|
||||||
|
binding.gameIcon.setImageBitmap(item.bitmapIcon)
|
||||||
|
|
||||||
|
val progress = savedInstanceState?.getInt(PipelineProgressTag) ?: 0
|
||||||
|
binding.progressBar.max = totalPipelineCount
|
||||||
|
updateProgress(progress)
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
binding.backgroundImage.setImageBitmap(item.bitmapIcon)
|
||||||
|
binding.backgroundImage.setRenderEffect(RenderEffect.createBlurEffect(75f, 75f, Shader.TileMode.MIRROR))
|
||||||
|
} else {
|
||||||
|
binding.backgroundImage.setImageBitmap(Toolkit.blur(item.bitmapIcon, 15))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState : Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putInt(PipelineProgressTag, binding.progressBar.progress)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
fun updateProgress(progress : Int) {
|
||||||
|
if (!this::binding.isInitialized)
|
||||||
|
return
|
||||||
|
|
||||||
|
binding.progressBar.progress = progress
|
||||||
|
binding.progressLabel.text = "$progress/$totalPipelineCount (${(progress.toFloat() / totalPipelineCount * 100).toInt()}%)"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newInstance(item : AppItem, totalPipelineCount : Int) = PipelineLoadingFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(AppItemTag, item)
|
||||||
|
putInt(TotalPipelineCountTag, totalPipelineCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,8 @@
|
||||||
android:layout_gravity="top|left"
|
android:layout_gravity="top|left"
|
||||||
android:layout_marginLeft="@dimen/onScreenItemHorizontalMargin"
|
android:layout_marginLeft="@dimen/onScreenItemHorizontalMargin"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
tools:text="60 FPS\n16.6±0.10ms"
|
android:textColor="@color/colorPerfStatsPrimary"
|
||||||
android:textColor="@color/colorPerfStatsPrimary" />
|
tools:text="60 FPS\n16.6±0.10ms" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/on_screen_pause_toggle"
|
android:id="@+id/on_screen_pause_toggle"
|
||||||
|
@ -53,4 +53,9 @@
|
||||||
android:src="@drawable/ic_show"
|
android:src="@drawable/ic_show"
|
||||||
app:tint="#40FFFFFF"
|
app:tint="#40FFFFFF"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/emulation_fragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/game_icon_bg"
|
android:id="@+id/background_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:contentDescription="@string/icon"
|
android:contentDescription="@string/icon"
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:maxWidth="360dp"
|
android:maxWidth="360dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/shader_info"
|
app:layout_constraintBottom_toTopOf="@+id/pipeline_info"
|
||||||
app:layout_constraintEnd_toEndOf="@id/guidelineTextEdge"
|
app:layout_constraintEnd_toEndOf="@id/guidelineTextEdge"
|
||||||
app:layout_constraintStart_toEndOf="@id/guidelineIconText"
|
app:layout_constraintStart_toEndOf="@id/guidelineIconText"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/shader_info"
|
android:id="@+id/pipeline_info"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
|
@ -90,11 +90,11 @@
|
||||||
app:layout_constraintTop_toBottomOf="@+id/game_info">
|
app:layout_constraintTop_toBottomOf="@+id/game_info">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/shaders_compiling"
|
android:id="@+id/pipelines_compiling"
|
||||||
style="?attr/textAppearanceBodyMedium"
|
style="?attr/textAppearanceBodyMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/compiling_cached_shaders"
|
android:text="@string/compiling_cached_pipelines"
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:textSize="17sp"
|
android:textSize="17sp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
app:trackThickness="10dp" />
|
app:trackThickness="10dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/progress"
|
android:id="@+id/progress_label"
|
||||||
style="?attr/textAppearanceBodyMedium"
|
style="?attr/textAppearanceBodyMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
|
@ -274,7 +274,7 @@
|
||||||
<!-- Software Keyboard -->
|
<!-- Software Keyboard -->
|
||||||
<string name="input_hint">Input Text</string>
|
<string name="input_hint">Input Text</string>
|
||||||
<!-- Misc -->
|
<!-- Misc -->
|
||||||
<string name="compiling_cached_shaders">Compiling cached shaders...</string>
|
<string name="compiling_cached_pipelines">Compiling cached pipelines...</string>
|
||||||
<!--suppress AndroidLintUnusedResources -->
|
<!--suppress AndroidLintUnusedResources -->
|
||||||
<string name="expand_button_title" tools:override="true">Expand</string>
|
<string name="expand_button_title" tools:override="true">Expand</string>
|
||||||
<string name="undo">Undo</string>
|
<string name="undo">Undo</string>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user