Compare commits
20 Commits
android-52
...
android-55
Author | SHA1 | Date | |
---|---|---|---|
48d16031e9 | |||
2f03fac9a0 | |||
3d5ecc1f08 | |||
52c71e7eb6 | |||
1f04a3dd55 | |||
d360abadba | |||
2dbe067d74 | |||
2f18fa5cd1 | |||
1615a86375 | |||
199de26995 | |||
6c68b07a67 | |||
1d201c71dc | |||
4077ff6851 | |||
164f880f23 | |||
954144e22b | |||
c2f827b85e | |||
037f82025c | |||
338d6f29b1 | |||
ada4697300 | |||
acc99433c7 |
@ -524,7 +524,7 @@ if (ENABLE_SDL2)
|
|||||||
if (YUZU_USE_BUNDLED_SDL2)
|
if (YUZU_USE_BUNDLED_SDL2)
|
||||||
# Detect toolchain and platform
|
# Detect toolchain and platform
|
||||||
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
|
||||||
set(SDL2_VER "SDL2-2.28.1")
|
set(SDL2_VER "SDL2-2.28.2")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|
|----|----|----|----|----|
|
||||||
|
|
||||||
|
|
||||||
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
2
externals/SDL
vendored
2
externals/SDL
vendored
Submodule externals/SDL updated: 116a5344ff...031912c4b6
4
externals/ffmpeg/CMakeLists.txt
vendored
4
externals/ffmpeg/CMakeLists.txt
vendored
@ -164,7 +164,7 @@ if (NOT WIN32 AND NOT ANDROID)
|
|||||||
--enable-decoder=h264
|
--enable-decoder=h264
|
||||||
--enable-decoder=vp8
|
--enable-decoder=vp8
|
||||||
--enable-decoder=vp9
|
--enable-decoder=vp9
|
||||||
--enable-filter=yadif
|
--enable-filter=yadif,scale
|
||||||
--cc="${FFmpeg_CC}"
|
--cc="${FFmpeg_CC}"
|
||||||
--cxx="${FFmpeg_CXX}"
|
--cxx="${FFmpeg_CXX}"
|
||||||
${FFmpeg_HWACCEL_FLAGS}
|
${FFmpeg_HWACCEL_FLAGS}
|
||||||
@ -254,7 +254,7 @@ elseif(ANDROID)
|
|||||||
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
# Use yuzu FFmpeg binaries
|
# Use yuzu FFmpeg binaries
|
||||||
set(FFmpeg_EXT_NAME "ffmpeg-5.1.3")
|
set(FFmpeg_EXT_NAME "ffmpeg-6.0")
|
||||||
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
set(FFmpeg_PATH "${CMAKE_BINARY_DIR}/externals/${FFmpeg_EXT_NAME}")
|
||||||
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
download_bundled_external("ffmpeg/" ${FFmpeg_EXT_NAME} "")
|
||||||
set(FFmpeg_FOUND YES)
|
set(FFmpeg_FOUND YES)
|
||||||
|
@ -56,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
|
||||||
android:theme="@style/Theme.Yuzu.Main"
|
android:theme="@style/Theme.Yuzu.Main"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop"
|
||||||
android:screenOrientation="userLandscape"
|
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
@ -67,6 +66,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<data android:mimeType="application/octet-stream" />
|
<data android:mimeType="application/octet-stream" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data
|
||||||
|
android:mimeType="application/octet-stream"
|
||||||
|
android:scheme="content"/>
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.nfc.action.TECH_DISCOVERED"
|
android:name="android.nfc.action.TECH_DISCOVERED"
|
||||||
android:resource="@xml/nfc_tech_filter" />
|
android:resource="@xml/nfc_tech_filter" />
|
||||||
|
@ -11,7 +11,6 @@ import android.view.View
|
|||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
@ -246,17 +245,5 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
|||||||
settings.putExtra(ARG_GAME_ID, gameId)
|
settings.putExtra(ARG_GAME_ID, gameId)
|
||||||
context.startActivity(settings)
|
context.startActivity(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launch(
|
|
||||||
context: Context,
|
|
||||||
launcher: ActivityResultLauncher<Intent>,
|
|
||||||
menuTag: String?,
|
|
||||||
gameId: String?
|
|
||||||
) {
|
|
||||||
val settings = Intent(context, SettingsActivity::class.java)
|
|
||||||
settings.putExtra(ARG_MENU_TAG, menuTag)
|
|
||||||
settings.putExtra(ARG_GAME_ID, gameId)
|
|
||||||
launcher.launch(settings)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@ -19,8 +19,6 @@ import android.util.Rational
|
|||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
@ -50,6 +48,7 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
|||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||||
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
|
|
||||||
@ -62,12 +61,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
private var _binding: FragmentEmulationBinding? = null
|
private var _binding: FragmentEmulationBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
val args by navArgs<EmulationFragmentArgs>()
|
private val args by navArgs<EmulationFragmentArgs>()
|
||||||
|
|
||||||
|
private lateinit var game: Game
|
||||||
|
|
||||||
private var isInFoldableLayout = false
|
private var isInFoldableLayout = false
|
||||||
|
|
||||||
private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
super.onAttach(context)
|
super.onAttach(context)
|
||||||
if (context is EmulationActivity) {
|
if (context is EmulationActivity) {
|
||||||
@ -81,11 +80,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
.collect { updateFoldableLayout(context, it) }
|
.collect { updateFoldableLayout(context, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReturnFromSettings = context.activityResultRegistry.register(
|
|
||||||
"SettingsResult",
|
|
||||||
ActivityResultContracts.StartActivityForResult()
|
|
||||||
) { updateScreenLayout() }
|
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
|
||||||
}
|
}
|
||||||
@ -97,10 +91,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val intentUri: Uri? = requireActivity().intent.data
|
||||||
|
var intentGame: Game? = null
|
||||||
|
if (intentUri != null) {
|
||||||
|
intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
|
||||||
|
GameHelper.getGame(requireActivity().intent.data!!, false)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
game = if (args.game != null) {
|
||||||
|
args.game!!
|
||||||
|
} else {
|
||||||
|
intentGame ?: error("[EmulationFragment] No bootable game present!")
|
||||||
|
}
|
||||||
|
|
||||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||||
emulationState = EmulationState(args.game.path)
|
emulationState = EmulationState(game.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,7 +133,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
updateShowFpsOverlay()
|
updateShowFpsOverlay()
|
||||||
|
|
||||||
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
||||||
args.game.title
|
game.title
|
||||||
binding.inGameMenu.setNavigationItemSelectedListener {
|
binding.inGameMenu.setNavigationItemSelectedListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.menu_pause_emulation -> {
|
R.id.menu_pause_emulation -> {
|
||||||
@ -149,12 +158,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_settings -> {
|
R.id.menu_settings -> {
|
||||||
SettingsActivity.launch(
|
SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
|
||||||
requireContext(),
|
|
||||||
onReturnFromSettings,
|
|
||||||
SettingsFile.FILE_NAME_CONFIG,
|
|
||||||
""
|
|
||||||
)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,13 +63,13 @@ object GameHelper {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
|
if (Game.extensions.contains(FileUtil.getExtension(it.uri))) {
|
||||||
games.add(getGame(it.uri))
|
games.add(getGame(it.uri, true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGame(uri: Uri): Game {
|
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
|
||||||
val filePath = uri.toString()
|
val filePath = uri.toString()
|
||||||
var name = NativeLibrary.getTitle(filePath)
|
var name = NativeLibrary.getTitle(filePath)
|
||||||
|
|
||||||
@ -94,11 +94,13 @@ object GameHelper {
|
|||||||
NativeLibrary.isHomebrew(filePath)
|
NativeLibrary.isHomebrew(filePath)
|
||||||
)
|
)
|
||||||
|
|
||||||
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
|
if (addedToLibrary) {
|
||||||
if (addedTime == 0L) {
|
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)
|
||||||
preferences.edit()
|
if (addedTime == 0L) {
|
||||||
.putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis())
|
preferences.edit()
|
||||||
.apply()
|
.putLong(newGame.keyAddedToLibraryTime, System.currentTimeMillis())
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newGame
|
return newGame
|
||||||
|
@ -7,7 +7,6 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Rational
|
import android.util.Rational
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
class FixedRatioSurfaceView @JvmOverloads constructor(
|
class FixedRatioSurfaceView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor(
|
|||||||
*/
|
*/
|
||||||
fun setAspectRatio(ratio: Rational?) {
|
fun setAspectRatio(ratio: Rational?) {
|
||||||
aspectRatio = ratio?.toFloat() ?: 0f
|
aspectRatio = ratio?.toFloat() ?: 0f
|
||||||
|
requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat()
|
||||||
val width = MeasureSpec.getSize(widthMeasureSpec)
|
val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat()
|
||||||
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
||||||
if (aspectRatio != 0f) {
|
if (aspectRatio != 0f) {
|
||||||
val newWidth: Int
|
val displayAspect = displayWidth / displayHeight
|
||||||
val newHeight: Int
|
if (displayAspect < aspectRatio) {
|
||||||
if (height * aspectRatio < width) {
|
// Max out width
|
||||||
newWidth = (height * aspectRatio).roundToInt()
|
val halfHeight = displayHeight / 2
|
||||||
newHeight = height
|
val surfaceHeight = displayWidth / aspectRatio
|
||||||
|
val newTop: Float = halfHeight - (surfaceHeight / 2)
|
||||||
|
val newBottom: Float = halfHeight + (surfaceHeight / 2)
|
||||||
|
super.onMeasure(
|
||||||
|
widthMeasureSpec,
|
||||||
|
MeasureSpec.makeMeasureSpec(
|
||||||
|
newBottom.toInt() - newTop.toInt(),
|
||||||
|
MeasureSpec.EXACTLY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
newWidth = width
|
// Max out height
|
||||||
newHeight = (width / aspectRatio).roundToInt()
|
val halfWidth = displayWidth / 2
|
||||||
|
val surfaceWidth = displayHeight * aspectRatio
|
||||||
|
val newLeft: Float = halfWidth - (surfaceWidth / 2)
|
||||||
|
val newRight: Float = halfWidth + (surfaceWidth / 2)
|
||||||
|
super.onMeasure(
|
||||||
|
MeasureSpec.makeMeasureSpec(
|
||||||
|
newRight.toInt() - newLeft.toInt(),
|
||||||
|
MeasureSpec.EXACTLY
|
||||||
|
),
|
||||||
|
heightMeasureSpec
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
val left = (width - newWidth) / 2
|
|
||||||
val top = (height - newHeight) / 2
|
|
||||||
setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
|
|
||||||
} else {
|
|
||||||
setLeftTopRightBottom(0, 0, width, height)
|
|
||||||
}
|
}
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
tools:layout="@layout/fragment_emulation" >
|
tools:layout="@layout/fragment_emulation" >
|
||||||
<argument
|
<argument
|
||||||
android:name="game"
|
android:name="game"
|
||||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||||
|
app:nullable="true"
|
||||||
|
android:defaultValue="@null" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
@ -62,7 +62,9 @@
|
|||||||
android:label="EmulationActivity">
|
android:label="EmulationActivity">
|
||||||
<argument
|
<argument
|
||||||
android:name="game"
|
android:name="game"
|
||||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||||
|
app:nullable="true"
|
||||||
|
android:defaultValue="@null" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<action
|
<action
|
||||||
|
@ -778,7 +778,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time
|
|||||||
while (i < command_buffer.count) {
|
while (i < command_buffer.count) {
|
||||||
const auto node_id{cmd->node_id};
|
const auto node_id{cmd->node_id};
|
||||||
const auto node_id_type{cmd->node_id >> 28};
|
const auto node_id_type{cmd->node_id >> 28};
|
||||||
const auto node_id_base{cmd->node_id & 0xFFF};
|
const auto node_id_base{(cmd->node_id >> 16) & 0xFFF};
|
||||||
|
|
||||||
// If the new estimated process time falls below the limit, we're done dropping.
|
// If the new estimated process time falls below the limit, we're done dropping.
|
||||||
if (estimated_process_time <= time_limit) {
|
if (estimated_process_time <= time_limit) {
|
||||||
|
@ -364,7 +364,7 @@ bool IsCubebSuitable() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Test min latency
|
// Get min latency
|
||||||
cubeb_stream_params params{};
|
cubeb_stream_params params{};
|
||||||
params.rate = TargetSampleRate;
|
params.rate = TargetSampleRate;
|
||||||
params.channels = 2;
|
params.channels = 2;
|
||||||
@ -380,11 +380,6 @@ bool IsCubebSuitable() {
|
|||||||
}
|
}
|
||||||
latency = std::max(latency, TargetSampleCount * 2);
|
latency = std::max(latency, TargetSampleCount * 2);
|
||||||
|
|
||||||
if (latency > TargetSampleCount * 3) {
|
|
||||||
LOG_ERROR(Audio_Sink, "Cubeb latency is too high, it is not suitable.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test opening a device with standard parameters
|
// Test opening a device with standard parameters
|
||||||
cubeb_devid output_device{0};
|
cubeb_devid output_device{0};
|
||||||
cubeb_devid input_device{0};
|
cubeb_devid input_device{0};
|
||||||
|
@ -83,8 +83,8 @@ const SinkDetails& GetOutputSinkDetails(Settings::AudioEngine sink_id) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_ERROR(Service_Audio, "Auto-selecting the {} backend",
|
LOG_INFO(Service_Audio, "Auto-selecting the {} backend",
|
||||||
Settings::CanonicalizeEnum(iter->id));
|
Settings::CanonicalizeEnum(iter->id));
|
||||||
} else {
|
} else {
|
||||||
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
if (iter != std::end(sink_details) && !iter->is_suitable()) {
|
||||||
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
LOG_ERROR(Service_Audio, "Selected backend {} is not suitable, falling back to null",
|
||||||
|
@ -174,7 +174,7 @@ public:
|
|||||||
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
|
{6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"},
|
||||||
{7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
{7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"},
|
||||||
{8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
{8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
|
||||||
{9, nullptr, "DecodeInterleavedForMultiStream"},
|
{9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
@ -206,6 +206,16 @@ private:
|
|||||||
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DecodeInterleavedForMultiStream(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Audio, "called");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
|
||||||
|
: OpusDecoderState::ExtraBehavior::None;
|
||||||
|
|
||||||
|
decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
|
||||||
|
}
|
||||||
|
|
||||||
OpusDecoderState decoder_state;
|
OpusDecoderState decoder_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -354,6 +364,40 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
|
|||||||
system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
|
||||||
|
OpusMultiStreamParametersEx params;
|
||||||
|
std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
|
||||||
|
|
||||||
|
const auto& sample_rate = params.sample_rate;
|
||||||
|
const auto& channel_count = params.channel_count;
|
||||||
|
|
||||||
|
LOG_INFO(
|
||||||
|
Audio,
|
||||||
|
"called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
|
||||||
|
sample_rate, channel_count, params.number_streams, params.number_stereo_streams);
|
||||||
|
|
||||||
|
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
|
||||||
|
sample_rate == 12000 || sample_rate == 8000,
|
||||||
|
"Invalid sample rate");
|
||||||
|
|
||||||
|
int error = 0;
|
||||||
|
OpusDecoderPtr decoder{opus_multistream_decoder_create(
|
||||||
|
sample_rate, static_cast<int>(channel_count), params.number_streams,
|
||||||
|
params.number_stereo_streams, params.channel_mappings.data(), &error)};
|
||||||
|
if (error != OPUS_OK || decoder == nullptr) {
|
||||||
|
LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
// TODO(ogniK): Use correct error code
|
||||||
|
rb.Push(ResultUnknown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushIpcInterface<IHardwareOpusDecoderManager>(
|
||||||
|
system, OpusDecoderState{std::move(decoder), sample_rate, channel_count});
|
||||||
|
}
|
||||||
|
|
||||||
HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
|
HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
|
{0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"},
|
||||||
@ -362,7 +406,8 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
|
|||||||
{3, nullptr, "GetWorkBufferSizeForMultiStream"},
|
{3, nullptr, "GetWorkBufferSizeForMultiStream"},
|
||||||
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
|
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
|
||||||
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
|
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
|
||||||
{6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
|
{6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx,
|
||||||
|
"OpenHardwareOpusDecoderForMultiStreamEx"},
|
||||||
{7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
|
{7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
|
||||||
{8, nullptr, "GetWorkBufferSizeExEx"},
|
{8, nullptr, "GetWorkBufferSizeExEx"},
|
||||||
{9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"},
|
{9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"},
|
||||||
|
@ -18,8 +18,10 @@ struct OpusMultiStreamParametersEx {
|
|||||||
u32 number_stereo_streams;
|
u32 number_stereo_streams;
|
||||||
u32 use_large_frame_size;
|
u32 use_large_frame_size;
|
||||||
u32 padding;
|
u32 padding;
|
||||||
std::array<u32, 64> channel_mappings;
|
std::array<u8, 0x100> channel_mappings;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
|
||||||
|
"OpusMultiStreamParametersEx has incorrect size");
|
||||||
|
|
||||||
class HwOpus final : public ServiceFramework<HwOpus> {
|
class HwOpus final : public ServiceFramework<HwOpus> {
|
||||||
public:
|
public:
|
||||||
@ -29,6 +31,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void OpenHardwareOpusDecoder(HLERequestContext& ctx);
|
void OpenHardwareOpusDecoder(HLERequestContext& ctx);
|
||||||
void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
|
void OpenHardwareOpusDecoderEx(HLERequestContext& ctx);
|
||||||
|
void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx);
|
||||||
void GetWorkBufferSize(HLERequestContext& ctx);
|
void GetWorkBufferSize(HLERequestContext& ctx);
|
||||||
void GetWorkBufferSizeEx(HLERequestContext& ctx);
|
void GetWorkBufferSizeEx(HLERequestContext& ctx);
|
||||||
void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
|
void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx);
|
||||||
|
@ -19,6 +19,12 @@ enum class ServerEnvironmentType : u8 {
|
|||||||
Dp,
|
Dp,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is nn::nsd::EnvironmentIdentifier
|
||||||
|
struct EnvironmentIdentifier {
|
||||||
|
std::array<u8, 8> identifier;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(EnvironmentIdentifier) == 0x8);
|
||||||
|
|
||||||
NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
@ -101,8 +107,9 @@ void NSD::ResolveEx(HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
|
void NSD::GetEnvironmentIdentifier(HLERequestContext& ctx) {
|
||||||
const std::string environment_identifier = "lp1";
|
constexpr EnvironmentIdentifier lp1 = {
|
||||||
ctx.WriteBuffer(environment_identifier);
|
.identifier = {'l', 'p', '1', '\0', '\0', '\0', '\0', '\0'}};
|
||||||
|
ctx.WriteBuffer(lp1);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
@ -150,6 +150,12 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
|||||||
const std::string host = Common::StringFromBuffer(host_buffer);
|
const std::string host = Common::StringFromBuffer(host_buffer);
|
||||||
// For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
|
// For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions.
|
||||||
|
|
||||||
|
// Prevent resolution of Nintendo servers
|
||||||
|
if (host.find("srv.nintendo.net") != std::string::npos) {
|
||||||
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
|
return {0, GetAddrInfoError::AGAIN};
|
||||||
|
}
|
||||||
|
|
||||||
auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||||
if (!res.has_value()) {
|
if (!res.has_value()) {
|
||||||
return {0, Translate(res.error())};
|
return {0, Translate(res.error())};
|
||||||
@ -261,6 +267,12 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
|||||||
const auto host_buffer = ctx.ReadBuffer(0);
|
const auto host_buffer = ctx.ReadBuffer(0);
|
||||||
const std::string host = Common::StringFromBuffer(host_buffer);
|
const std::string host = Common::StringFromBuffer(host_buffer);
|
||||||
|
|
||||||
|
// Prevent resolution of Nintendo servers
|
||||||
|
if (host.find("srv.nintendo.net") != std::string::npos) {
|
||||||
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
|
return {0, GetAddrInfoError::AGAIN};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<std::string> service = std::nullopt;
|
std::optional<std::string> service = std::nullopt;
|
||||||
if (ctx.CanReadBuffer(1)) {
|
if (ctx.CanReadBuffer(1)) {
|
||||||
const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
|
const std::span<const u8> service_buffer = ctx.ReadBuffer(1);
|
||||||
|
@ -289,8 +289,11 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainBuffer(GPUVAddr gpu_ad
|
|||||||
MarkWrittenBuffer(buffer_id, *cpu_addr, size);
|
MarkWrittenBuffer(buffer_id, *cpu_addr, size);
|
||||||
break;
|
break;
|
||||||
case ObtainBufferOperation::DiscardWrite: {
|
case ObtainBufferOperation::DiscardWrite: {
|
||||||
IntervalType interval{*cpu_addr, size};
|
VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
|
||||||
|
VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
|
||||||
|
IntervalType interval{cpu_addr_start, cpu_addr_end};
|
||||||
ClearDownload(interval);
|
ClearDownload(interval);
|
||||||
|
common_ranges.subtract(interval);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -1159,6 +1162,11 @@ void BufferCache<P>::UpdateDrawIndirect() {
|
|||||||
.size = static_cast<u32>(size),
|
.size = static_cast<u32>(size),
|
||||||
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
|
.buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
|
||||||
};
|
};
|
||||||
|
VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
|
||||||
|
VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
|
||||||
|
IntervalType interval{cpu_addr_start, cpu_addr_end};
|
||||||
|
ClearDownload(interval);
|
||||||
|
common_ranges.subtract(interval);
|
||||||
};
|
};
|
||||||
if (current_draw_indirect->include_count) {
|
if (current_draw_indirect->include_count) {
|
||||||
update(current_draw_indirect->count_start_address, sizeof(u32),
|
update(current_draw_indirect->count_start_address, sizeof(u32),
|
||||||
|
@ -319,6 +319,7 @@ void Codec::Decode() {
|
|||||||
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
LOG_WARNING(Service_NVDRV, "Zero width or height in frame");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
bool is_interlaced = initial_frame->interlaced_frame != 0;
|
||||||
if (av_codec_ctx->hw_device_ctx) {
|
if (av_codec_ctx->hw_device_ctx) {
|
||||||
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
final_frame = AVFramePtr{av_frame_alloc(), AVFrameDeleter};
|
||||||
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
ASSERT_MSG(final_frame, "av_frame_alloc final_frame failed");
|
||||||
@ -334,7 +335,7 @@ void Codec::Decode() {
|
|||||||
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
UNIMPLEMENTED_MSG("Unexpected video format: {}", final_frame->format);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!final_frame->interlaced_frame) {
|
if (!is_interlaced) {
|
||||||
av_frames.push(std::move(final_frame));
|
av_frames.push(std::move(final_frame));
|
||||||
} else {
|
} else {
|
||||||
if (!filters_initialized) {
|
if (!filters_initialized) {
|
||||||
|
Reference in New Issue
Block a user