early-access version 3709
This commit is contained in:
parent
47d3d8f0dd
commit
c9635ec98a
33 changed files with 242 additions and 135 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3708.
|
This is the source code for early-access 3709.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
2
externals/CMakeLists.txt
vendored
2
externals/CMakeLists.txt
vendored
|
@ -145,7 +145,7 @@ add_subdirectory(nx_tzdb)
|
||||||
|
|
||||||
# VMA
|
# VMA
|
||||||
add_library(vma vma/vma.cpp)
|
add_library(vma vma/vma.cpp)
|
||||||
target_include_directories(vma PUBLIC ./vma/vma/include)
|
target_include_directories(vma PUBLIC ./vma/VulkanMemoryAllocator/include)
|
||||||
target_link_libraries(vma PRIVATE Vulkan::Headers)
|
target_link_libraries(vma PRIVATE Vulkan::Headers)
|
||||||
|
|
||||||
if (NOT TARGET LLVM::Demangle)
|
if (NOT TARGET LLVM::Demangle)
|
||||||
|
|
1
externals/vma/vma.cpp
vendored
1
externals/vma/vma.cpp
vendored
|
@ -4,4 +4,5 @@
|
||||||
#define VMA_IMPLEMENTATION
|
#define VMA_IMPLEMENTATION
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
||||||
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
|
@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
|
||||||
|
|
||||||
class SearchFragment : Fragment() {
|
class SearchFragment : Fragment() {
|
||||||
private var _binding: FragmentSearchBinding? = null
|
private var _binding: FragmentSearchBinding? = null
|
||||||
|
@ -128,10 +127,7 @@ class SearchFragment : Fragment() {
|
||||||
|
|
||||||
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
|
||||||
|
|
||||||
R.id.chip_retail -> baseList.filter {
|
R.id.chip_retail -> baseList.filter { !it.isHomebrew }
|
||||||
FileUtil.hasExtension(it.path, "xci") ||
|
|
||||||
FileUtil.hasExtension(it.path, "nsp")
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> baseList
|
else -> baseList
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Game(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val extensions: Set<String> = HashSet(
|
val extensions: Set<String> = HashSet(
|
||||||
listOf(".xci", ".nsp", ".nca", ".nro")
|
listOf("xci", "nsp", "nca", "nro")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.hasExtension(result, "keys")) {
|
if (FileUtil.getExtension(result) != "keys") {
|
||||||
MessageDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
R.string.reading_keys_failure,
|
R.string.reading_keys_failure,
|
||||||
R.string.install_prod_keys_failure_extension_description
|
R.string.install_prod_keys_failure_extension_description
|
||||||
|
@ -393,7 +393,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileUtil.hasExtension(result, "bin")) {
|
if (FileUtil.getExtension(result) != "bin") {
|
||||||
MessageDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
R.string.reading_keys_failure,
|
R.string.reading_keys_failure,
|
||||||
R.string.install_amiibo_keys_failure_extension_description
|
R.string.install_amiibo_keys_failure_extension_description
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.OpenableColumns
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -185,19 +184,18 @@ object FileUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get file display name from given path
|
* Get file display name from given path
|
||||||
* @param path content uri path
|
* @param uri content uri
|
||||||
* @return String display name
|
* @return String display name
|
||||||
*/
|
*/
|
||||||
fun getFilename(context: Context, path: String): String {
|
fun getFilename(uri: Uri): String {
|
||||||
val resolver = context.contentResolver
|
val resolver = YuzuApplication.appContext.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
||||||
)
|
)
|
||||||
var filename = ""
|
var filename = ""
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
c = resolver.query(uri, columns, null, null, null)
|
||||||
c = resolver.query(mUri, columns, null, null, null)
|
|
||||||
c!!.moveToNext()
|
c!!.moveToNext()
|
||||||
filename = c.getString(0)
|
filename = c.getString(0)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -326,25 +324,9 @@ object FileUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasExtension(path: String, extension: String): Boolean =
|
fun getExtension(uri: Uri): String {
|
||||||
path.substring(path.lastIndexOf(".") + 1).contains(extension)
|
val fileName = getFilename(uri)
|
||||||
|
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||||
fun hasExtension(uri: Uri, extension: String): Boolean {
|
.lowercase()
|
||||||
val fileName: String?
|
|
||||||
val cursor = YuzuApplication.appContext.contentResolver.query(uri, null, null, null, null)
|
|
||||||
val nameIndex = cursor?.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
||||||
cursor?.moveToFirst()
|
|
||||||
|
|
||||||
if (nameIndex == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fileName = cursor.getString(nameIndex)
|
|
||||||
cursor.close()
|
|
||||||
|
|
||||||
if (fileName == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return fileName.substring(fileName.lastIndexOf(".") + 1).contains(extension)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.utils
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import java.util.*
|
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
@ -33,15 +32,9 @@ object GameHelper {
|
||||||
val children = FileUtil.listFiles(context, gamesUri)
|
val children = FileUtil.listFiles(context, gamesUri)
|
||||||
for (file in children) {
|
for (file in children) {
|
||||||
if (!file.isDirectory) {
|
if (!file.isDirectory) {
|
||||||
val filename = file.uri.toString()
|
|
||||||
val extensionStart = filename.lastIndexOf('.')
|
|
||||||
if (extensionStart > 0) {
|
|
||||||
val fileExtension = filename.substring(extensionStart)
|
|
||||||
|
|
||||||
// Check that the file has an extension we care about before trying to read out of it.
|
// Check that the file has an extension we care about before trying to read out of it.
|
||||||
if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
|
if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
|
||||||
games.add(getGame(filename))
|
games.add(getGame(file.uri))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,21 +52,19 @@ object GameHelper {
|
||||||
return games.toList()
|
return games.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGame(filePath: String): Game {
|
private fun getGame(uri: Uri): Game {
|
||||||
|
val filePath = uri.toString()
|
||||||
var name = NativeLibrary.getTitle(filePath)
|
var name = NativeLibrary.getTitle(filePath)
|
||||||
|
|
||||||
// If the game's title field is empty, use the filename.
|
// If the game's title field is empty, use the filename.
|
||||||
if (name.isEmpty()) {
|
if (name.isEmpty()) {
|
||||||
name = filePath.substring(filePath.lastIndexOf("/") + 1)
|
name = FileUtil.getFilename(uri)
|
||||||
}
|
}
|
||||||
var gameId = NativeLibrary.getGameId(filePath)
|
var gameId = NativeLibrary.getGameId(filePath)
|
||||||
|
|
||||||
// If the game's ID field is empty, use the filename without extension.
|
// If the game's ID field is empty, use the filename without extension.
|
||||||
if (gameId.isEmpty()) {
|
if (gameId.isEmpty()) {
|
||||||
gameId = filePath.substring(
|
gameId = name.substring(0, name.lastIndexOf("."))
|
||||||
filePath.lastIndexOf("/") + 1,
|
|
||||||
filePath.lastIndexOf(".")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val newGame = Game(
|
val newGame = Game(
|
||||||
|
|
|
@ -60,6 +60,9 @@
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/renderer_base.h"
|
#include "video_core/renderer_base.h"
|
||||||
|
|
||||||
|
#define jconst [[maybe_unused]] const auto
|
||||||
|
#define jauto [[maybe_unused]] auto
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class EmulationSession final {
|
class EmulationSession final {
|
||||||
|
@ -99,7 +102,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
int InstallFileToNand(std::string filename) {
|
int InstallFileToNand(std::string filename) {
|
||||||
const auto copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
|
||||||
std::size_t block_size) {
|
std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr) {
|
if (src == nullptr || dest == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -109,10 +112,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Common::Literals;
|
using namespace Common::Literals;
|
||||||
std::vector<u8> buffer(1_MiB);
|
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
|
||||||
const auto read = src->Read(buffer.data(), buffer.size(), i);
|
jconst read = src->Read(buffer.data(), buffer.size(), i);
|
||||||
dest->Write(buffer.data(), read, i);
|
dest->Write(buffer.data(), read, i);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -129,14 +132,14 @@ public:
|
||||||
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
|
||||||
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
m_system.GetFileSystemController().CreateFactories(*m_vfs);
|
||||||
|
|
||||||
std::shared_ptr<FileSys::NSP> nsp;
|
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
|
||||||
if (filename.ends_with("nsp")) {
|
if (filename.ends_with("nsp")) {
|
||||||
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||||
if (nsp->IsExtractedType()) {
|
if (nsp->IsExtractedType()) {
|
||||||
return InstallError;
|
return InstallError;
|
||||||
}
|
}
|
||||||
} else if (filename.ends_with("xci")) {
|
} else if (filename.ends_with("xci")) {
|
||||||
const auto xci =
|
jconst xci =
|
||||||
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
|
||||||
nsp = xci->GetSecurePartitionNSP();
|
nsp = xci->GetSecurePartitionNSP();
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,7 +154,7 @@ public:
|
||||||
return InstallError;
|
return InstallError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
|
||||||
*nsp, true, copy_func);
|
*nsp, true, copy_func);
|
||||||
|
|
||||||
switch (res) {
|
switch (res) {
|
||||||
|
@ -234,7 +237,7 @@ public:
|
||||||
m_system.SetFilesystem(m_vfs);
|
m_system.SetFilesystem(m_vfs);
|
||||||
|
|
||||||
// Initialize system.
|
// Initialize system.
|
||||||
auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
|
||||||
m_software_keyboard = android_keyboard.get();
|
m_software_keyboard = android_keyboard.get();
|
||||||
m_system.SetShuttingDown(false);
|
m_system.SetShuttingDown(false);
|
||||||
m_system.ApplySettings();
|
m_system.ApplySettings();
|
||||||
|
@ -332,7 +335,7 @@ public:
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
{
|
{
|
||||||
std::unique_lock lock(m_mutex);
|
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||||
[&]() { return !m_is_running; })) {
|
[&]() { return !m_is_running; })) {
|
||||||
// Emulation halted.
|
// Emulation halted.
|
||||||
|
@ -364,7 +367,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsHandheldOnly() {
|
bool IsHandheldOnly() {
|
||||||
const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
|
||||||
|
|
||||||
if (npad_style_set.fullkey == 1) {
|
if (npad_style_set.fullkey == 1) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -377,17 +380,17 @@ public:
|
||||||
return !Settings::values.use_docked_mode.GetValue();
|
return !Settings::values.use_docked_mode.GetValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDeviceType(int index, int type) {
|
void SetDeviceType([[maybe_unused]] int index, int type) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadConnectEvent(int index) {
|
void OnGamepadConnectEvent([[maybe_unused]] int index) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
// Ensure that player1 is configured correctly and handheld disconnected
|
// Ensure that player1 is configured correctly and handheld disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
|
||||||
auto handheld =
|
jauto handheld =
|
||||||
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
|
||||||
|
@ -399,7 +402,8 @@ public:
|
||||||
|
|
||||||
// Ensure that handheld is configured correctly and player 1 disconnected
|
// Ensure that handheld is configured correctly and player 1 disconnected
|
||||||
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
|
||||||
auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
jauto player1 =
|
||||||
|
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
|
|
||||||
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
|
||||||
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
|
||||||
|
@ -413,8 +417,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnGamepadDisconnectEvent(int index) {
|
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
|
||||||
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +434,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
RomMetadata GetRomMetadata(const std::string& path) {
|
RomMetadata GetRomMetadata(const std::string& path) {
|
||||||
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
||||||
return search->second;
|
return search->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,14 +442,14 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||||
const auto file = Core::GetGameFileFromPath(m_vfs, path);
|
jconst file = Core::GetGameFileFromPath(m_vfs, path);
|
||||||
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||||
|
|
||||||
RomMetadata entry;
|
RomMetadata entry;
|
||||||
loader->ReadTitle(entry.title);
|
loader->ReadTitle(entry.title);
|
||||||
loader->ReadIcon(entry.icon);
|
loader->ReadIcon(entry.icon);
|
||||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||||
auto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
|
jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||||
} else {
|
} else {
|
||||||
entry.isHomebrew = false;
|
entry.isHomebrew = false;
|
||||||
|
@ -516,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
|
|
||||||
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
|
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
|
||||||
|
|
||||||
const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath);
|
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
|
||||||
if (result != Core::SystemResultStatus::Success) {
|
if (result != Core::SystemResultStatus::Success) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -528,24 +532,25 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jclass clazz, jobject surf) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||||
|
[[maybe_unused]] jobject surf) {
|
||||||
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
|
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
|
||||||
EmulationSession::GetInstance().SurfaceChanged();
|
EmulationSession::GetInstance().SurfaceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jclass clazz) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
|
||||||
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
|
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
|
||||||
EmulationSession::GetInstance().SetNativeWindow(nullptr);
|
EmulationSession::GetInstance().SetNativeWindow(nullptr);
|
||||||
EmulationSession::GetInstance().SurfaceChanged();
|
EmulationSession::GetInstance().SurfaceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jclass clazz,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
|
||||||
jstring j_directory) {
|
[[maybe_unused]] jstring j_directory) {
|
||||||
Common::FS::SetAppDirectory(GetJString(env, j_directory));
|
Common::FS::SetAppDirectory(GetJString(env, j_directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jclass clazz,
|
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
|
||||||
jstring j_file) {
|
[[maybe_unused]] jstring j_file) {
|
||||||
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
|
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +575,7 @@ void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* e
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
|
jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
|
||||||
JNIEnv* env, [[maybe_unused]] jobject instance) {
|
JNIEnv* env, jobject instance) {
|
||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
// If the KGSL device exists custom drivers can be loaded using adrenotools
|
// If the KGSL device exists custom drivers can be loaded using adrenotools
|
||||||
return SupportsCustomDriver();
|
return SupportsCustomDriver();
|
||||||
|
@ -648,8 +653,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv*
|
||||||
return static_cast<jboolean>(true);
|
return static_cast<jboolean>(true);
|
||||||
}
|
}
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jint j_device,
|
jint j_device, jint j_button,
|
||||||
jint j_button, jint action) {
|
jint action) {
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
// Ensure gamepad is connected
|
// Ensure gamepad is connected
|
||||||
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
|
||||||
|
@ -718,8 +723,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
|
||||||
}
|
}
|
||||||
|
|
||||||
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jstring j_filename) {
|
||||||
auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
|
||||||
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
||||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||||
reinterpret_cast<jbyte*>(icon_data.data()));
|
reinterpret_cast<jbyte*>(icon_data.data()));
|
||||||
|
@ -727,8 +732,8 @@ jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass cla
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jstring j_filename) {
|
||||||
auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
|
||||||
return env->NewStringUTF(title.c_str());
|
return env->NewStringUTF(title.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,22 +748,21 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass claz
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jstring j_filename) {
|
||||||
return env->NewStringUTF("");
|
return env->NewStringUTF("");
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jstring j_filename) {
|
||||||
return env->NewStringUTF("");
|
return env->NewStringUTF("");
|
||||||
}
|
}
|
||||||
|
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
|
||||||
[[maybe_unused]] jstring j_filename) {
|
jstring j_filename) {
|
||||||
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
|
||||||
[[maybe_unused]] (JNIEnv* env, jclass clazz) {
|
|
||||||
// Create the default config.ini.
|
// Create the default config.ini.
|
||||||
Config{};
|
Config{};
|
||||||
// Initialize the emulated system.
|
// Initialize the emulated system.
|
||||||
|
@ -770,8 +774,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
|
||||||
JNIEnv* env, jclass clazz, [[maybe_unused]] jstring j_file,
|
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
|
||||||
[[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
|
||||||
Config{};
|
Config{};
|
||||||
|
@ -816,7 +819,7 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jcl
|
||||||
jdoubleArray j_stats = env->NewDoubleArray(4);
|
jdoubleArray j_stats = env->NewDoubleArray(4);
|
||||||
|
|
||||||
if (EmulationSession::GetInstance().IsRunning()) {
|
if (EmulationSession::GetInstance().IsRunning()) {
|
||||||
const auto results = EmulationSession::GetInstance().PerfStats();
|
jconst results = EmulationSession::GetInstance().PerfStats();
|
||||||
|
|
||||||
// Converting the structure into an array makes it easier to pass it to the frontend
|
// Converting the structure into an array makes it easier to pass it to the frontend
|
||||||
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
|
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
|
||||||
|
|
|
@ -1250,6 +1250,11 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
|
||||||
const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
|
const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
|
||||||
const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
|
const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
|
||||||
|
|
||||||
|
// Restore previous state
|
||||||
|
if (mapped_nfc_result != Common::Input::DriverResult::Success) {
|
||||||
|
right_output_device->SetPollingMode(Common::Input::PollingMode::Active);
|
||||||
|
}
|
||||||
|
|
||||||
if (virtual_nfc_result == Common::Input::DriverResult::Success) {
|
if (virtual_nfc_result == Common::Input::DriverResult::Success) {
|
||||||
return virtual_nfc_result;
|
return virtual_nfc_result;
|
||||||
}
|
}
|
||||||
|
@ -1329,16 +1334,22 @@ bool EmulatedController::StartNfcPolling() {
|
||||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||||
auto& nfc_virtual_output_device = output_devices[3];
|
auto& nfc_virtual_output_device = output_devices[3];
|
||||||
|
|
||||||
return nfc_output_device->StartNfcPolling() == Common::Input::NfcState::Success ||
|
const auto device_result = nfc_output_device->StartNfcPolling();
|
||||||
nfc_virtual_output_device->StartNfcPolling() == Common::Input::NfcState::Success;
|
const auto virtual_device_result = nfc_virtual_output_device->StartNfcPolling();
|
||||||
|
|
||||||
|
return device_result == Common::Input::NfcState::Success ||
|
||||||
|
virtual_device_result == Common::Input::NfcState::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::StopNfcPolling() {
|
bool EmulatedController::StopNfcPolling() {
|
||||||
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||||
auto& nfc_virtual_output_device = output_devices[3];
|
auto& nfc_virtual_output_device = output_devices[3];
|
||||||
|
|
||||||
return nfc_output_device->StopNfcPolling() == Common::Input::NfcState::Success ||
|
const auto device_result = nfc_output_device->StopNfcPolling();
|
||||||
nfc_virtual_output_device->StopNfcPolling() == Common::Input::NfcState::Success;
|
const auto virtual_device_result = nfc_virtual_output_device->StopNfcPolling();
|
||||||
|
|
||||||
|
return device_result == Common::Input::NfcState::Success ||
|
||||||
|
virtual_device_result == Common::Input::NfcState::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
|
bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
|
||||||
|
|
|
@ -150,6 +150,8 @@ public:
|
||||||
if (sdl_controller) {
|
if (sdl_controller) {
|
||||||
const auto type = SDL_GameControllerGetType(sdl_controller.get());
|
const auto type = SDL_GameControllerGetType(sdl_controller.get());
|
||||||
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
|
return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ||
|
||||||
|
(type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT) ||
|
||||||
|
(type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) ||
|
||||||
(type == SDL_CONTROLLER_TYPE_PS5);
|
(type == SDL_CONTROLLER_TYPE_PS5);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -228,9 +230,8 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Input::BatteryLevel GetBatteryLevel() {
|
Common::Input::BatteryLevel GetBatteryLevel(SDL_JoystickPowerLevel battery_level) {
|
||||||
const auto level = SDL_JoystickCurrentPowerLevel(sdl_joystick.get());
|
switch (battery_level) {
|
||||||
switch (level) {
|
|
||||||
case SDL_JOYSTICK_POWER_EMPTY:
|
case SDL_JOYSTICK_POWER_EMPTY:
|
||||||
return Common::Input::BatteryLevel::Empty;
|
return Common::Input::BatteryLevel::Empty;
|
||||||
case SDL_JOYSTICK_POWER_LOW:
|
case SDL_JOYSTICK_POWER_LOW:
|
||||||
|
@ -378,7 +379,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
||||||
if (joystick_map.find(guid) == joystick_map.end()) {
|
if (joystick_map.find(guid) == joystick_map.end()) {
|
||||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
|
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
|
||||||
PreSetController(joystick->GetPadIdentifier());
|
PreSetController(joystick->GetPadIdentifier());
|
||||||
SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
|
|
||||||
joystick->EnableMotion();
|
joystick->EnableMotion();
|
||||||
joystick_map[guid].emplace_back(std::move(joystick));
|
joystick_map[guid].emplace_back(std::move(joystick));
|
||||||
return;
|
return;
|
||||||
|
@ -398,7 +398,6 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
||||||
const int port = static_cast<int>(joystick_guid_list.size());
|
const int port = static_cast<int>(joystick_guid_list.size());
|
||||||
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
|
auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
|
||||||
PreSetController(joystick->GetPadIdentifier());
|
PreSetController(joystick->GetPadIdentifier());
|
||||||
SetBattery(joystick->GetPadIdentifier(), joystick->GetBatteryLevel());
|
|
||||||
joystick->EnableMotion();
|
joystick->EnableMotion();
|
||||||
joystick_guid_list.emplace_back(std::move(joystick));
|
joystick_guid_list.emplace_back(std::move(joystick));
|
||||||
}
|
}
|
||||||
|
@ -438,8 +437,6 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||||
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
if (const auto joystick = GetSDLJoystickBySDLID(event.jbutton.which)) {
|
||||||
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||||
SetButton(identifier, event.jbutton.button, true);
|
SetButton(identifier, event.jbutton.button, true);
|
||||||
// Battery doesn't trigger an event so just update every button press
|
|
||||||
SetBattery(identifier, joystick->GetBatteryLevel());
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -466,6 +463,13 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SDL_JOYBATTERYUPDATED: {
|
||||||
|
if (auto joystick = GetSDLJoystickBySDLID(event.jbattery.which)) {
|
||||||
|
const PadIdentifier identifier = joystick->GetPadIdentifier();
|
||||||
|
SetBattery(identifier, joystick->GetBatteryLevel(event.jbattery.level));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SDL_JOYDEVICEREMOVED:
|
case SDL_JOYDEVICEREMOVED:
|
||||||
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
|
LOG_DEBUG(Input, "Controller removed with Instance_ID {}", event.jdevice.which);
|
||||||
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
|
CloseJoystick(SDL_JoystickFromInstanceID(event.jdevice.which));
|
||||||
|
@ -505,6 +509,9 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
|
||||||
} else {
|
} else {
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED, "0");
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS, "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
|
// Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
|
||||||
|
@ -512,8 +519,11 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
|
||||||
} else {
|
} else {
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1");
|
||||||
|
|
||||||
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
// Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
|
||||||
// driver on Linux.
|
// driver on Linux.
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0");
|
||||||
|
@ -793,7 +803,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
|
||||||
// This list also excludes Screenshot since there's not really a mapping for that
|
// This list also excludes Screenshot since there's not really a mapping for that
|
||||||
ButtonBindings switch_to_sdl_button;
|
ButtonBindings switch_to_sdl_button;
|
||||||
|
|
||||||
if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) {
|
if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
|
||||||
|
SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
|
||||||
|
SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) {
|
||||||
switch_to_sdl_button = GetNintendoButtonBinding(joystick);
|
switch_to_sdl_button = GetNintendoButtonBinding(joystick);
|
||||||
} else {
|
} else {
|
||||||
switch_to_sdl_button = GetDefaultButtonBinding();
|
switch_to_sdl_button = GetDefaultButtonBinding();
|
||||||
|
|
|
@ -72,6 +72,7 @@ DriverResult JoyconDriver::InitializeDevice() {
|
||||||
nfc_enabled = false;
|
nfc_enabled = false;
|
||||||
passive_enabled = false;
|
passive_enabled = false;
|
||||||
irs_enabled = false;
|
irs_enabled = false;
|
||||||
|
input_only_device = false;
|
||||||
gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
|
gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
|
||||||
gyro_performance = Joycon::GyroPerformance::HZ833;
|
gyro_performance = Joycon::GyroPerformance::HZ833;
|
||||||
accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
|
accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
|
||||||
|
@ -86,7 +87,12 @@ DriverResult JoyconDriver::InitializeDevice() {
|
||||||
rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
|
rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
|
||||||
|
|
||||||
// Get fixed joycon info
|
// Get fixed joycon info
|
||||||
generic_protocol->GetVersionNumber(version);
|
if (generic_protocol->GetVersionNumber(version) != DriverResult::Success) {
|
||||||
|
// If this command fails the device doesn't accept configuration commands
|
||||||
|
input_only_device = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input_only_device) {
|
||||||
generic_protocol->SetLowPowerMode(false);
|
generic_protocol->SetLowPowerMode(false);
|
||||||
generic_protocol->GetColor(color);
|
generic_protocol->GetColor(color);
|
||||||
if (handle_device_type == ControllerType::Pro) {
|
if (handle_device_type == ControllerType::Pro) {
|
||||||
|
@ -96,6 +102,8 @@ DriverResult JoyconDriver::InitializeDevice() {
|
||||||
device_type = handle_device_type;
|
device_type = handle_device_type;
|
||||||
}
|
}
|
||||||
generic_protocol->GetSerialNumber(serial_number);
|
generic_protocol->GetSerialNumber(serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
supported_features = GetSupportedFeatures();
|
supported_features = GetSupportedFeatures();
|
||||||
|
|
||||||
// Get Calibration data
|
// Get Calibration data
|
||||||
|
@ -261,6 +269,10 @@ DriverResult JoyconDriver::SetPollingMode() {
|
||||||
generic_protocol->EnableImu(false);
|
generic_protocol->EnableImu(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input_only_device) {
|
||||||
|
return DriverResult::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
if (irs_protocol->IsEnabled()) {
|
if (irs_protocol->IsEnabled()) {
|
||||||
irs_protocol->DisableIrs();
|
irs_protocol->DisableIrs();
|
||||||
}
|
}
|
||||||
|
@ -282,6 +294,7 @@ DriverResult JoyconDriver::SetPollingMode() {
|
||||||
}
|
}
|
||||||
irs_protocol->DisableIrs();
|
irs_protocol->DisableIrs();
|
||||||
LOG_ERROR(Input, "Error enabling IRS");
|
LOG_ERROR(Input, "Error enabling IRS");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nfc_enabled && supported_features.nfc) {
|
if (nfc_enabled && supported_features.nfc) {
|
||||||
|
@ -291,6 +304,7 @@ DriverResult JoyconDriver::SetPollingMode() {
|
||||||
}
|
}
|
||||||
nfc_protocol->DisableNfc();
|
nfc_protocol->DisableNfc();
|
||||||
LOG_ERROR(Input, "Error enabling NFC");
|
LOG_ERROR(Input, "Error enabling NFC");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hidbus_enabled && supported_features.hidbus) {
|
if (hidbus_enabled && supported_features.hidbus) {
|
||||||
|
@ -305,6 +319,7 @@ DriverResult JoyconDriver::SetPollingMode() {
|
||||||
ring_connected = false;
|
ring_connected = false;
|
||||||
ring_protocol->DisableRingCon();
|
ring_protocol->DisableRingCon();
|
||||||
LOG_ERROR(Input, "Error enabling Ringcon");
|
LOG_ERROR(Input, "Error enabling Ringcon");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (passive_enabled && supported_features.passive) {
|
if (passive_enabled && supported_features.passive) {
|
||||||
|
@ -333,6 +348,10 @@ JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
|
||||||
.vibration = true,
|
.vibration = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (input_only_device) {
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
if (device_type == ControllerType::Right) {
|
if (device_type == ControllerType::Right) {
|
||||||
features.nfc = true;
|
features.nfc = true;
|
||||||
features.irs = true;
|
features.irs = true;
|
||||||
|
@ -517,6 +536,11 @@ DriverResult JoyconDriver::StopNfcPolling() {
|
||||||
const auto result = nfc_protocol->StopNFCPollingMode();
|
const auto result = nfc_protocol->StopNFCPollingMode();
|
||||||
disable_input_thread = false;
|
disable_input_thread = false;
|
||||||
|
|
||||||
|
if (amiibo_detected) {
|
||||||
|
amiibo_detected = false;
|
||||||
|
joycon_poller->UpdateAmiibo({});
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,7 @@ private:
|
||||||
// Hardware configuration
|
// Hardware configuration
|
||||||
u8 leds{};
|
u8 leds{};
|
||||||
ReportMode mode{};
|
ReportMode mode{};
|
||||||
|
bool input_only_device{};
|
||||||
bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
|
bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
|
||||||
bool hidbus_enabled{}; // External device support
|
bool hidbus_enabled{}; // External device support
|
||||||
bool irs_enabled{}; // Infrared camera input
|
bool irs_enabled{}; // Infrared camera input
|
||||||
|
|
|
@ -73,7 +73,7 @@ DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
|
||||||
DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
|
DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
|
||||||
SubCommandResponse& output) {
|
SubCommandResponse& output) {
|
||||||
constexpr int timeout_mili = 66;
|
constexpr int timeout_mili = 66;
|
||||||
constexpr int MaxTries = 15;
|
constexpr int MaxTries = 3;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -113,9 +113,7 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = GetSubCommandResponse(sc, output);
|
return GetSubCommandResponse(sc, output);
|
||||||
|
|
||||||
return DriverResult::Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
|
DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
|
||||||
|
@ -158,7 +156,7 @@ DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffe
|
||||||
|
|
||||||
DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
|
DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
|
||||||
constexpr std::size_t HeaderSize = 5;
|
constexpr std::size_t HeaderSize = 5;
|
||||||
constexpr std::size_t MaxTries = 10;
|
constexpr std::size_t MaxTries = 5;
|
||||||
std::size_t tries = 0;
|
std::size_t tries = 0;
|
||||||
SubCommandResponse response{};
|
SubCommandResponse response{};
|
||||||
std::array<u8, sizeof(ReadSpiPacket)> buffer{};
|
std::array<u8, sizeof(ReadSpiPacket)> buffer{};
|
||||||
|
|
|
@ -461,7 +461,7 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
|
||||||
header += fmt::format("R{},", index);
|
header += fmt::format("R{},", index);
|
||||||
}
|
}
|
||||||
if (program.local_memory_size > 0) {
|
if (program.local_memory_size > 0) {
|
||||||
header += fmt::format("lmem[{}],", program.local_memory_size);
|
header += fmt::format("lmem[{}],", Common::DivCeil(program.local_memory_size, 4U));
|
||||||
}
|
}
|
||||||
if (program.info.uses_fswzadd) {
|
if (program.info.uses_fswzadd) {
|
||||||
header += "FSWZA[4],FSWZB[4],";
|
header += "FSWZA[4],FSWZB[4],";
|
||||||
|
|
|
@ -424,6 +424,10 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||||
info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2;
|
info.used_constant_buffer_types |= IR::Type::U32 | IR::Type::U32x2;
|
||||||
info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4;
|
info.used_storage_buffer_types |= IR::Type::U32 | IR::Type::U32x2 | IR::Type::U32x4;
|
||||||
break;
|
break;
|
||||||
|
case IR::Opcode::LoadLocal:
|
||||||
|
case IR::Opcode::WriteLocal:
|
||||||
|
info.uses_local_memory = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,6 +172,7 @@ struct Info {
|
||||||
bool stores_indexed_attributes{};
|
bool stores_indexed_attributes{};
|
||||||
|
|
||||||
bool stores_global_memory{};
|
bool stores_global_memory{};
|
||||||
|
bool uses_local_memory{};
|
||||||
|
|
||||||
bool uses_fp16{};
|
bool uses_fp16{};
|
||||||
bool uses_fp64{};
|
bool uses_fp64{};
|
||||||
|
|
|
@ -130,7 +130,7 @@ void MaxwellDMA::Launch() {
|
||||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||||
read_buffer.resize_destructive(16);
|
read_buffer.resize_destructive(16);
|
||||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||||
memory_manager.ReadBlockUnsafe(
|
memory_manager.ReadBlock(
|
||||||
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
|
convert_linear_2_blocklinear_addr(regs.offset_in + offset),
|
||||||
read_buffer.data(), read_buffer.size());
|
read_buffer.data(), read_buffer.size());
|
||||||
memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(),
|
memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(),
|
||||||
|
@ -142,7 +142,7 @@ void MaxwellDMA::Launch() {
|
||||||
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
UNIMPLEMENTED_IF(regs.offset_out % 16 != 0);
|
||||||
read_buffer.resize_destructive(16);
|
read_buffer.resize_destructive(16);
|
||||||
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
for (u32 offset = 0; offset < regs.line_length_in; offset += 16) {
|
||||||
memory_manager.ReadBlockUnsafe(regs.offset_in + offset, read_buffer.data(),
|
memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(),
|
||||||
read_buffer.size());
|
read_buffer.size());
|
||||||
memory_manager.WriteBlockCached(
|
memory_manager.WriteBlockCached(
|
||||||
convert_linear_2_blocklinear_addr(regs.offset_out + offset),
|
convert_linear_2_blocklinear_addr(regs.offset_out + offset),
|
||||||
|
@ -151,8 +151,9 @@ void MaxwellDMA::Launch() {
|
||||||
} else {
|
} else {
|
||||||
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
|
if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) {
|
||||||
read_buffer.resize_destructive(regs.line_length_in);
|
read_buffer.resize_destructive(regs.line_length_in);
|
||||||
memory_manager.ReadBlockUnsafe(regs.offset_in, read_buffer.data(),
|
memory_manager.ReadBlock(regs.offset_in, read_buffer.data(),
|
||||||
regs.line_length_in);
|
regs.line_length_in,
|
||||||
|
VideoCommon::CacheType::NoBufferCache);
|
||||||
memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
|
memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(),
|
||||||
regs.line_length_in);
|
regs.line_length_in);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ set(SHADER_FILES
|
||||||
opengl_fidelityfx_fsr.frag
|
opengl_fidelityfx_fsr.frag
|
||||||
opengl_fidelityfx_fsr_easu.frag
|
opengl_fidelityfx_fsr_easu.frag
|
||||||
opengl_fidelityfx_fsr_rcas.frag
|
opengl_fidelityfx_fsr_rcas.frag
|
||||||
|
opengl_lmem_warmup.comp
|
||||||
opengl_present.frag
|
opengl_present.frag
|
||||||
opengl_present.vert
|
opengl_present.vert
|
||||||
opengl_present_scaleforce.frag
|
opengl_present_scaleforce.frag
|
||||||
|
|
47
src/video_core/host_shaders/opengl_lmem_warmup.comp
Executable file
47
src/video_core/host_shaders/opengl_lmem_warmup.comp
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
// This shader is a workaround for a quirk in NVIDIA OpenGL drivers
|
||||||
|
// Shaders using local memory see a great performance benefit if a shader that was dispatched
|
||||||
|
// before it had more local memory allocated.
|
||||||
|
// This shader allocates the maximum local memory allowed on NVIDIA drivers to ensure that
|
||||||
|
// subsequent shaders see the performance boost.
|
||||||
|
|
||||||
|
// NOTE: This shader does no actual meaningful work and returns immediately,
|
||||||
|
// it is simply a means to have the driver expect a shader using lots of local memory.
|
||||||
|
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||||
|
|
||||||
|
layout(location = 0) uniform uint uniform_data;
|
||||||
|
|
||||||
|
layout(binding = 0, rgba8) uniform writeonly restrict image2DArray dest_image;
|
||||||
|
|
||||||
|
#define MAX_LMEM_SIZE 4080 // Size chosen to avoid errors in Nvidia's GLSL compiler
|
||||||
|
#define NUM_LMEM_CONSTANTS 1
|
||||||
|
#define ARRAY_SIZE MAX_LMEM_SIZE - NUM_LMEM_CONSTANTS
|
||||||
|
|
||||||
|
uint lmem_0[ARRAY_SIZE];
|
||||||
|
const uvec4 constant_values[NUM_LMEM_CONSTANTS] = uvec4[](uvec4(0));
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const uint global_id = gl_GlobalInvocationID.x;
|
||||||
|
if (global_id <= 128) {
|
||||||
|
// Since the shader is called with a dispatch of 1x1x1
|
||||||
|
// This should always be the case, and this shader will not actually execute
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (uint t = 0; t < uniform_data; t++) {
|
||||||
|
const uint offset = (t * uniform_data);
|
||||||
|
lmem_0[offset] = t;
|
||||||
|
}
|
||||||
|
const uint offset = (gl_GlobalInvocationID.y * uniform_data + gl_GlobalInvocationID.x);
|
||||||
|
const uint value = lmem_0[offset];
|
||||||
|
const uint const_value = constant_values[offset / 4][offset % 4];
|
||||||
|
const uvec4 color = uvec4(value + const_value);
|
||||||
|
|
||||||
|
// A "side-effect" is needed so the variables don't get optimized out,
|
||||||
|
// but this should never execute so there should be no clobbering of previously bound state.
|
||||||
|
imageStore(dest_image, ivec3(gl_GlobalInvocationID), color);
|
||||||
|
}
|
|
@ -63,6 +63,7 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
|
||||||
writes_global_memory = !use_storage_buffers &&
|
writes_global_memory = !use_storage_buffers &&
|
||||||
std::ranges::any_of(info.storage_buffers_descriptors,
|
std::ranges::any_of(info.storage_buffers_descriptors,
|
||||||
[](const auto& desc) { return desc.is_written; });
|
[](const auto& desc) { return desc.is_written; });
|
||||||
|
uses_local_memory = info.uses_local_memory;
|
||||||
if (force_context_flush) {
|
if (force_context_flush) {
|
||||||
std::scoped_lock lock{built_mutex};
|
std::scoped_lock lock{built_mutex};
|
||||||
built_fence.Create();
|
built_fence.Create();
|
||||||
|
|
|
@ -59,6 +59,10 @@ public:
|
||||||
return writes_global_memory;
|
return writes_global_memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool UsesLocalMemory() const noexcept {
|
||||||
|
return uses_local_memory;
|
||||||
|
}
|
||||||
|
|
||||||
void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_,
|
void SetEngine(Tegra::Engines::KeplerCompute* kepler_compute_,
|
||||||
Tegra::MemoryManager* gpu_memory_) {
|
Tegra::MemoryManager* gpu_memory_) {
|
||||||
kepler_compute = kepler_compute_;
|
kepler_compute = kepler_compute_;
|
||||||
|
@ -84,6 +88,7 @@ private:
|
||||||
|
|
||||||
bool use_storage_buffers{};
|
bool use_storage_buffers{};
|
||||||
bool writes_global_memory{};
|
bool writes_global_memory{};
|
||||||
|
bool uses_local_memory{};
|
||||||
|
|
||||||
std::mutex built_mutex;
|
std::mutex built_mutex;
|
||||||
std::condition_variable built_condvar;
|
std::condition_variable built_condvar;
|
||||||
|
|
|
@ -194,6 +194,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
|
||||||
has_bool_ref_bug = true;
|
has_bool_ref_bug = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
has_lmem_perf_bug = is_nvidia;
|
||||||
|
|
||||||
strict_context_required = emu_window.StrictContextRequired();
|
strict_context_required = emu_window.StrictContextRequired();
|
||||||
// Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
|
// Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation.
|
||||||
|
|
|
@ -192,6 +192,10 @@ public:
|
||||||
return supports_conditional_barriers;
|
return supports_conditional_barriers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasLmemPerfBug() const {
|
||||||
|
return has_lmem_perf_bug;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool TestVariableAoffi();
|
static bool TestVariableAoffi();
|
||||||
static bool TestPreciseBug();
|
static bool TestPreciseBug();
|
||||||
|
@ -238,6 +242,7 @@ private:
|
||||||
bool can_report_memory{};
|
bool can_report_memory{};
|
||||||
bool strict_context_required{};
|
bool strict_context_required{};
|
||||||
bool supports_conditional_barriers{};
|
bool supports_conditional_barriers{};
|
||||||
|
bool has_lmem_perf_bug{};
|
||||||
|
|
||||||
std::string vendor_name;
|
std::string vendor_name;
|
||||||
};
|
};
|
||||||
|
|
|
@ -215,6 +215,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
|
||||||
|
|
||||||
writes_global_memory |= std::ranges::any_of(
|
writes_global_memory |= std::ranges::any_of(
|
||||||
info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; });
|
info.storage_buffers_descriptors, [](const auto& desc) { return desc.is_written; });
|
||||||
|
uses_local_memory |= info.uses_local_memory;
|
||||||
}
|
}
|
||||||
ASSERT(num_textures <= MAX_TEXTURES);
|
ASSERT(num_textures <= MAX_TEXTURES);
|
||||||
ASSERT(num_images <= MAX_IMAGES);
|
ASSERT(num_images <= MAX_IMAGES);
|
||||||
|
|
|
@ -98,6 +98,10 @@ public:
|
||||||
return writes_global_memory;
|
return writes_global_memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool UsesLocalMemory() const noexcept {
|
||||||
|
return uses_local_memory;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool IsBuilt() noexcept;
|
[[nodiscard]] bool IsBuilt() noexcept;
|
||||||
|
|
||||||
template <typename Spec>
|
template <typename Spec>
|
||||||
|
@ -146,6 +150,7 @@ private:
|
||||||
|
|
||||||
bool use_storage_buffers{};
|
bool use_storage_buffers{};
|
||||||
bool writes_global_memory{};
|
bool writes_global_memory{};
|
||||||
|
bool uses_local_memory{};
|
||||||
|
|
||||||
static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
|
static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
|
||||||
GLsizei num_xfb_attribs{};
|
GLsizei num_xfb_attribs{};
|
||||||
|
|
|
@ -222,6 +222,9 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||||
gpu.TickWork();
|
gpu.TickWork();
|
||||||
|
|
||||||
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
|
||||||
|
if (pipeline->UsesLocalMemory()) {
|
||||||
|
program_manager.LocalMemoryWarmup();
|
||||||
|
}
|
||||||
pipeline->SetEngine(maxwell3d, gpu_memory);
|
pipeline->SetEngine(maxwell3d, gpu_memory);
|
||||||
pipeline->Configure(is_indexed);
|
pipeline->Configure(is_indexed);
|
||||||
|
|
||||||
|
@ -371,6 +374,9 @@ void RasterizerOpenGL::DispatchCompute() {
|
||||||
if (!pipeline) {
|
if (!pipeline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (pipeline->UsesLocalMemory()) {
|
||||||
|
program_manager.LocalMemoryWarmup();
|
||||||
|
}
|
||||||
pipeline->SetEngine(kepler_compute, gpu_memory);
|
pipeline->SetEngine(kepler_compute, gpu_memory);
|
||||||
pipeline->Configure();
|
pipeline->Configure();
|
||||||
const auto& qmd{kepler_compute->launch_description};
|
const auto& qmd{kepler_compute->launch_description};
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include "video_core/host_shaders/opengl_lmem_warmup_comp.h"
|
||||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||||
|
#include "video_core/renderer_opengl/gl_shader_util.h"
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
|
@ -17,6 +19,10 @@ ProgramManager::ProgramManager(const Device& device) {
|
||||||
if (device.UseAssemblyShaders()) {
|
if (device.UseAssemblyShaders()) {
|
||||||
glEnable(GL_COMPUTE_PROGRAM_NV);
|
glEnable(GL_COMPUTE_PROGRAM_NV);
|
||||||
}
|
}
|
||||||
|
if (device.HasLmemPerfBug()) {
|
||||||
|
lmem_warmup_program =
|
||||||
|
CreateProgram(HostShaders::OPENGL_LMEM_WARMUP_COMP, GL_COMPUTE_SHADER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgramManager::BindComputeProgram(GLuint program) {
|
void ProgramManager::BindComputeProgram(GLuint program) {
|
||||||
|
@ -98,6 +104,13 @@ void ProgramManager::BindAssemblyPrograms(std::span<const OGLAssemblyProgram, NU
|
||||||
|
|
||||||
void ProgramManager::RestoreGuestCompute() {}
|
void ProgramManager::RestoreGuestCompute() {}
|
||||||
|
|
||||||
|
void ProgramManager::LocalMemoryWarmup() {
|
||||||
|
if (lmem_warmup_program.handle != 0) {
|
||||||
|
BindComputeProgram(lmem_warmup_program.handle);
|
||||||
|
glDispatchCompute(1, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProgramManager::BindPipeline() {
|
void ProgramManager::BindPipeline() {
|
||||||
if (!is_pipeline_bound) {
|
if (!is_pipeline_bound) {
|
||||||
is_pipeline_bound = true;
|
is_pipeline_bound = true;
|
||||||
|
|
|
@ -30,6 +30,8 @@ public:
|
||||||
|
|
||||||
void RestoreGuestCompute();
|
void RestoreGuestCompute();
|
||||||
|
|
||||||
|
void LocalMemoryWarmup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BindPipeline();
|
void BindPipeline();
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ private:
|
||||||
u32 current_stage_mask = 0;
|
u32 current_stage_mask = 0;
|
||||||
std::array<GLuint, NUM_STAGES> current_programs{};
|
std::array<GLuint, NUM_STAGES> current_programs{};
|
||||||
GLuint current_assembly_compute_program = 0;
|
GLuint current_assembly_compute_program = 0;
|
||||||
|
OGLProgram lmem_warmup_program;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace OpenGL
|
} // namespace OpenGL
|
||||||
|
|
|
@ -22,8 +22,6 @@
|
||||||
#include <adrenotools/bcenabler.h>
|
#include <adrenotools/bcenabler.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||||
|
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
|
|
||||||
namespace Vulkan::vk {
|
namespace Vulkan::vk {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string>
|
<string>To use Ring-Con, configure player 1 as right Joy-Con (both physical and emulated), and player 2 as left Joy-Con (left physical and dual emulated) before starting the game.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
|
Loading…
Reference in a new issue