mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-23 16:06:30 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99e6466f00 | |||
| 2cba99b6f5 | |||
| 4e31d84b93 | |||
| ea99df11e6 | |||
| a49d885e40 | |||
| 058d43d39e | |||
| ee70a288a8 | |||
| 2a0f945b48 | |||
| ce0b278fd8 | |||
| 3ff405a151 | |||
| 1f2d7888b0 | |||
| e3c526d80b | |||
| 4e4d61f9c3 | |||
| 8fbf92d6b3 | |||
| a0212ad661 | |||
| 2c19c4658a | |||
| 5a35f45140 | |||
| 2838141f21 | |||
| 757cfdc7fd | |||
| 3cdb6d560b | |||
| 94a65a9186 | |||
| 82b79fe2e2 | |||
| 2c8c10ab9e | |||
| 2b97d13c85 | |||
| 8ceecf6259 | |||
| cee3a37ecb | |||
| df587e1a95 | |||
| df1fcbd6b1 | |||
| af2ee98c1b | |||
| cfbea3a2d7 | |||
| c61947e66a | |||
| cf65654d1e | |||
| f736637e1f | |||
| ac389fb2a5 | |||
| 697cb7920d | |||
| d258152e2a | |||
| b77d23c232 | |||
| ba61c82838 | |||
| 43cc03e72f | |||
| 46858f48c7 | |||
| f52d3f57f6 | |||
| 1bacb8e318 | |||
| a00b7b65fa | |||
| c50b160955 | |||
| 0d767e7194 | |||
| 5335810245 | |||
| 088c5d136e | |||
| 63a0e68225 | |||
| 8e35edca62 | |||
| 0b751c01de | |||
| 8bdf2f3e9b | |||
| ecc6926988 |
+3
-7
@@ -99,12 +99,7 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
||||
if (IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(
|
||||
getNeedsGlobalForKey(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
||||
) == 0
|
||||
) {
|
||||
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key)
|
||||
}
|
||||
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key)
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
||||
if (!BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(
|
||||
@@ -312,6 +307,8 @@ class SettingsFragmentPresenter(
|
||||
// TODO(crueter): sub-submenus?
|
||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
// add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||
|
||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||
add(IntSetting.RENDERER_VSYNC.key)
|
||||
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||
@@ -328,7 +325,6 @@ class SettingsFragmentPresenter(
|
||||
add(IntSetting.MAX_ANISOTROPY.key)
|
||||
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
|
||||
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
|
||||
add(IntSetting.RENDERER_NVDEC_EMULATION.key)
|
||||
|
||||
add(BooleanSetting.SYNC_MEMORY_OPERATIONS.key)
|
||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.fragment.app.Fragment
|
||||
@@ -60,10 +59,6 @@ class GamesFragment : Fragment() {
|
||||
|
||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||
private var fallbackBottomInset: Int = 0
|
||||
private var pendingPostReloadListSettle = false
|
||||
private var pendingPostReloadListSettleGeneration = 0
|
||||
private var gameListSubmitGeneration = 0
|
||||
private var committedGameListSubmitGeneration = 0
|
||||
|
||||
companion object {
|
||||
private const val SEARCH_TEXT = "SearchText"
|
||||
@@ -173,9 +168,10 @@ class GamesFragment : Fragment() {
|
||||
|
||||
gamesViewModel.shouldScrollAfterReload.collect(viewLifecycleOwner) { shouldScroll ->
|
||||
if (shouldScroll) {
|
||||
pendingPostReloadListSettle = true
|
||||
pendingPostReloadListSettleGeneration = gameListSubmitGeneration
|
||||
schedulePostReloadListSettle()
|
||||
binding.gridGames.post {
|
||||
(binding.gridGames as? CarouselRecyclerView)?.pendingScrollAfterReload = true
|
||||
gameAdapter.notifyDataSetChanged()
|
||||
}
|
||||
gamesViewModel.setShouldScrollAfterReload(false)
|
||||
}
|
||||
}
|
||||
@@ -277,42 +273,11 @@ class GamesFragment : Fragment() {
|
||||
lastSearchText = currentSearchText
|
||||
lastFilter = currentFilter
|
||||
} else {
|
||||
submitGameList(games)
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(games)
|
||||
gamesViewModel.setFilteredGames(games)
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitGameList(games: List<Game>) {
|
||||
val adapter = (binding.gridGames as? RecyclerView)?.adapter as? GameAdapter
|
||||
if (adapter == null) {
|
||||
schedulePostReloadListSettle()
|
||||
return
|
||||
}
|
||||
|
||||
val submitGeneration = ++gameListSubmitGeneration
|
||||
adapter.submitList(games) {
|
||||
if (committedGameListSubmitGeneration < submitGeneration) {
|
||||
committedGameListSubmitGeneration = submitGeneration
|
||||
}
|
||||
schedulePostReloadListSettle()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun schedulePostReloadListSettle() {
|
||||
if (!pendingPostReloadListSettle || _binding == null) return
|
||||
|
||||
binding.gridGames.doOnPreDraw {
|
||||
if (!pendingPostReloadListSettle || _binding == null) return@doOnPreDraw
|
||||
if (committedGameListSubmitGeneration < pendingPostReloadListSettleGeneration) {
|
||||
schedulePostReloadListSettle()
|
||||
return@doOnPreDraw
|
||||
}
|
||||
pendingPostReloadListSettle = false
|
||||
|
||||
(binding.gridGames as? CarouselRecyclerView)?.refreshView()
|
||||
}
|
||||
}
|
||||
private fun setupTopView() {
|
||||
binding.searchText.doOnTextChanged() { text: CharSequence?, _: Int, _: Int, _: Int ->
|
||||
if (text.toString().isNotEmpty()) {
|
||||
@@ -449,7 +414,9 @@ class GamesFragment : Fragment() {
|
||||
|
||||
val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
|
||||
if (searchTerm.isEmpty()) {
|
||||
submitGameList(filteredList)
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(
|
||||
filteredList
|
||||
)
|
||||
gamesViewModel.setFilteredGames(filteredList)
|
||||
return
|
||||
}
|
||||
@@ -465,7 +432,7 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
}.sortedByDescending { it.score }.map { it.item }
|
||||
|
||||
submitGameList(sortedList)
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(sortedList)
|
||||
gamesViewModel.setFilteredGames(sortedList)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ object GameHelper {
|
||||
|
||||
fun getGames(): List<Game> {
|
||||
val games = mutableListOf<Game>()
|
||||
val gamesByProgramId = mutableMapOf<String, Game>()
|
||||
val context = YuzuApplication.appContext
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@@ -64,7 +63,6 @@ object GameHelper {
|
||||
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(gameDirUri),
|
||||
scanDepth,
|
||||
mountedContainerUris
|
||||
@@ -138,7 +136,6 @@ object GameHelper {
|
||||
|
||||
private fun addGamesRecursive(
|
||||
games: MutableList<Game>,
|
||||
gamesByProgramId: MutableMap<String, Game>,
|
||||
files: Array<MinimalDocumentFile>,
|
||||
depth: Int,
|
||||
mountedContainerUris: MutableSet<String>
|
||||
@@ -151,7 +148,6 @@ object GameHelper {
|
||||
if (it.isDirectory) {
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(it.uri),
|
||||
depth - 1,
|
||||
mountedContainerUris
|
||||
@@ -160,9 +156,8 @@ object GameHelper {
|
||||
val extension = FileUtil.getExtension(it.uri).lowercase()
|
||||
val filePath = it.uri.toString()
|
||||
|
||||
val mountedContainer = externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)
|
||||
if (mountedContainer) {
|
||||
if (externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)) {
|
||||
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
|
||||
}
|
||||
|
||||
@@ -170,20 +165,6 @@ object GameHelper {
|
||||
val game = getGame(it.uri, true, false)
|
||||
if (game != null) {
|
||||
games.add(game)
|
||||
if (game.programId != "0") {
|
||||
gamesByProgramId[game.programId] = game
|
||||
}
|
||||
} else if (mountedContainer) {
|
||||
GameMetadata.getProgramId(filePath).toLongOrNull()?.let { programId ->
|
||||
gamesByProgramId[(programId and 0x800L.inv()).toString()]
|
||||
}?.let { existingGame ->
|
||||
NativeLibrary.getPatchesForFile(existingGame.path, existingGame.programId)
|
||||
existingGame.version = GameMetadata.getVersion(
|
||||
existingGame.path,
|
||||
true
|
||||
)
|
||||
GameIconUtils.refreshGameIcon(existingGame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -27,15 +24,6 @@ import coil.request.Options
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import java.util.Collections
|
||||
import java.util.WeakHashMap
|
||||
|
||||
private val gameIconHashes = Collections.synchronizedMap(mutableMapOf<String, Int>())
|
||||
private val gameIconTargets = Collections.synchronizedMap(WeakHashMap<ImageView, GameIconTarget>())
|
||||
|
||||
private fun Game.iconCacheKey(): String = "$path|$version"
|
||||
|
||||
private data class GameIconTarget(val game: Game, var iconHash: Int? = null)
|
||||
|
||||
class GameIconFetcher(
|
||||
private val game: Game,
|
||||
@@ -43,15 +31,14 @@ class GameIconFetcher(
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
return DrawableResult(
|
||||
drawable = decodeGameIcon(game)!!.toDrawable(options.context.resources),
|
||||
drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
}
|
||||
|
||||
private fun decodeGameIcon(game: Game): Bitmap? {
|
||||
val data = GameMetadata.getIcon(game.path)
|
||||
gameIconHashes[game.iconCacheKey()] = data.contentHashCode()
|
||||
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||
val data = GameMetadata.getIcon(uri)
|
||||
return BitmapFactory.decodeByteArray(
|
||||
data,
|
||||
0,
|
||||
@@ -67,7 +54,7 @@ class GameIconFetcher(
|
||||
}
|
||||
|
||||
class GameIconKeyer : Keyer<Game> {
|
||||
override fun key(data: Game, options: Options): String = data.iconCacheKey()
|
||||
override fun key(data: Game, options: Options): String = data.path
|
||||
}
|
||||
|
||||
object GameIconUtils {
|
||||
@@ -84,58 +71,14 @@ object GameIconUtils {
|
||||
.build()
|
||||
|
||||
fun loadGameIcon(game: Game, imageView: ImageView) {
|
||||
gameIconTargets[imageView] = GameIconTarget(game)
|
||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||
.data(game)
|
||||
.target(imageView)
|
||||
.error(R.drawable.default_icon)
|
||||
.listener(
|
||||
onSuccess = { _, _ ->
|
||||
val target = gameIconTargets[imageView]
|
||||
if (target?.game?.iconCacheKey() == game.iconCacheKey()) {
|
||||
gameIconHashes[game.iconCacheKey()]?.let {
|
||||
target.iconHash = it
|
||||
}
|
||||
}
|
||||
},
|
||||
onError = { _, _ ->
|
||||
gameIconTargets[imageView]?.iconHash = null
|
||||
}
|
||||
)
|
||||
.build()
|
||||
imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
fun refreshGameIcon(game: Game) {
|
||||
val targets = synchronized(gameIconTargets) {
|
||||
gameIconTargets
|
||||
.filterValues { it.game.path == game.path && it.game.programId == game.programId }
|
||||
.keys
|
||||
.toList()
|
||||
}
|
||||
if (targets.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val iconHash = GameMetadata.getIcon(game.path).contentHashCode()
|
||||
val targetsToRefresh = targets.filter { gameIconTargets[it]?.iconHash != iconHash }
|
||||
if (targetsToRefresh.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
imageLoader.memoryCache?.remove(MemoryCache.Key(game.iconCacheKey()))
|
||||
targetsToRefresh.forEach { imageView ->
|
||||
imageView.post {
|
||||
val target = gameIconTargets[imageView] ?: return@post
|
||||
if (target.game.path == game.path && target.game.programId == game.programId) {
|
||||
if (target.iconHash != iconHash) {
|
||||
loadGameIcon(game, imageView)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
|
||||
val request = ImageRequest.Builder(YuzuApplication.appContext)
|
||||
.data(game)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.ui
|
||||
@@ -11,8 +11,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||
import androidx.core.view.doOnNextLayout
|
||||
@@ -36,7 +34,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
private var overlapDecoration: OverlappingDecoration? = null
|
||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||
private var scalingScrollListener: OnScrollListener? = null
|
||||
private var savedItemAnimator: RecyclerView.ItemAnimator? = null
|
||||
|
||||
companion object {
|
||||
private const val CAROUSEL_CARD_SIZE_FACTOR = "CarouselCardSizeMultiplier"
|
||||
@@ -45,13 +42,8 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
private const val CAROUSEL_OVERLAP_FACTOR = "CarouselOverlapFactor"
|
||||
private const val CAROUSEL_MAX_FLING_COUNT = "CarouselMaxFlingCount"
|
||||
private const val CAROUSEL_FLING_MULTIPLIER = "CarouselFlingMultiplier"
|
||||
private const val CAROUSEL_ARC_ANGLE_STEP_DEGREES = 15.0
|
||||
private const val CAROUSEL_ARC_MAX_ANGLE_DEGREES = 165.0
|
||||
private const val CAROUSEL_ARC_DEPTH_MAX_ANGLE_DEGREES = 85.0
|
||||
private const val CAROUSEL_ARC_DEPTH_STRETCH = 5.0f
|
||||
private const val CAROUSEL_ARC_X_DEPTH_FACTOR = 0.55f
|
||||
private const val CAROUSEL_ARC_FADE_OUT_START_DEGREES = 60.0
|
||||
private const val CAROUSEL_ARC_FADE_OUT_END_DEGREES = 95.0
|
||||
private const val CAROUSEL_CARDS_SCALING_SHAPE = "CarouselCardsScalingShape"
|
||||
private const val CAROUSEL_CARDS_ALPHA_SHAPE = "CarouselCardsAlphaShape"
|
||||
const val CAROUSEL_LAST_SCROLL_POSITION = "CarouselLastScrollPosition"
|
||||
const val CAROUSEL_VIEW_TYPE_PORTRAIT = "GamesViewTypePortrait"
|
||||
const val CAROUSEL_VIEW_TYPE_LANDSCAPE = "GamesViewTypeLandscape"
|
||||
@@ -168,52 +160,46 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun shapingFunction(x: Float, option: Int = 0): Float {
|
||||
return when (option) {
|
||||
0 -> 1f // Off
|
||||
1 -> 1f - x // linear descending
|
||||
2 -> (1f - x) * (1f - x) // Ease out
|
||||
3 -> if (x < 0.05f) 1f else (1f - x) * 0.8f
|
||||
4 -> kotlin.math.cos(x * Math.PI).toFloat() // Cosine
|
||||
5 -> kotlin.math.cos((1.5f * x).coerceIn(0f, 1f) * Math.PI).toFloat() // Cosine 1.5x trimmed
|
||||
else -> 1f // Default to Off
|
||||
}
|
||||
}
|
||||
|
||||
fun updateChildScaleAndAlphaForPosition(child: View) {
|
||||
val cardSize = (adapter as? GameAdapter ?: return).cardSize
|
||||
val position = getChildViewHolder(child).bindingAdapterPosition
|
||||
if (position == RecyclerView.NO_POSITION || cardSize <= 0) {
|
||||
return // No valid position or card size
|
||||
}
|
||||
val layoutParams = child.layoutParams
|
||||
if (layoutParams.width != cardSize || layoutParams.height != cardSize) {
|
||||
child.layoutParams = layoutParams.apply {
|
||||
width = cardSize
|
||||
height = cardSize
|
||||
}
|
||||
}
|
||||
|
||||
val signedDistance = getChildDistanceToCenter(child)
|
||||
val itemStep = (cardSize - overlapPx).toFloat().coerceAtLeast(1f)
|
||||
val angleStep = Math.toRadians(CAROUSEL_ARC_ANGLE_STEP_DEGREES).toFloat()
|
||||
val maxAngle = Math.toRadians(CAROUSEL_ARC_MAX_ANGLE_DEGREES).toFloat()
|
||||
val depthMaxAngle = Math.toRadians(CAROUSEL_ARC_DEPTH_MAX_ANGLE_DEGREES).toFloat()
|
||||
val fadeOutStartAngle = Math.toRadians(CAROUSEL_ARC_FADE_OUT_START_DEGREES).toFloat()
|
||||
val fadeOutEndAngle = Math.toRadians(CAROUSEL_ARC_FADE_OUT_END_DEGREES).toFloat()
|
||||
val angle = (signedDistance / itemStep * angleStep).coerceIn(-maxAngle, maxAngle)
|
||||
val arcRadius = itemStep / angleStep
|
||||
val arcX = sin(angle) * arcRadius
|
||||
val absoluteAngle = abs(angle)
|
||||
val rawDepthInput = ((1f - cos(absoluteAngle)) / (1f - cos(depthMaxAngle)))
|
||||
.coerceIn(0f, 1f)
|
||||
val easedDepthTail = Math.pow(
|
||||
(1f - rawDepthInput).toDouble(),
|
||||
CAROUSEL_ARC_DEPTH_STRETCH.toDouble()
|
||||
).toFloat()
|
||||
val depthInput = (1f - easedDepthTail).coerceIn(0f, 1f)
|
||||
val projectedArcX = arcX * (1f - rawDepthInput * CAROUSEL_ARC_X_DEPTH_FACTOR)
|
||||
|
||||
child.animate().cancel()
|
||||
child.translationX = projectedArcX - signedDistance
|
||||
child.layoutParams.width = cardSize
|
||||
child.layoutParams.height = cardSize
|
||||
|
||||
val center = getRecyclerViewCenter()
|
||||
val distance = abs(getChildDistanceToCenter(child))
|
||||
val internalBorderScale = resources.getFraction(R.fraction.carousel_bordercards_scale, 1, 1)
|
||||
val borderScale = preferences.getFloat(CAROUSEL_BORDERCARDS_SCALE, internalBorderScale).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
|
||||
val shapedScaling = 1f - depthInput
|
||||
val shapeInput = (distance / center).coerceIn(0f, 1f)
|
||||
val internalShapeSetting = resources.getInteger(R.integer.carousel_cards_scaling_shape)
|
||||
val scalingShapeSetting = preferences.getInt(
|
||||
CAROUSEL_CARDS_SCALING_SHAPE,
|
||||
internalShapeSetting
|
||||
)
|
||||
val shapedScaling = shapingFunction(shapeInput, scalingShapeSetting)
|
||||
val scale = (borderScale + (1f - borderScale) * shapedScaling).coerceIn(0f, 1f)
|
||||
|
||||
val maxDistance = width / 2f
|
||||
val alphaInput = (distance / maxDistance).coerceIn(0f, 1f)
|
||||
val internalBordersAlpha = resources.getFraction(
|
||||
R.fraction.carousel_bordercards_alpha,
|
||||
1,
|
||||
@@ -223,12 +209,15 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val shapedAlpha = cos(depthInput * Math.PI).toFloat()
|
||||
val baseAlpha = (borderAlpha + (1f - borderAlpha) * shapedAlpha).coerceIn(0f, 1f)
|
||||
val rearPresence = (1f - (absoluteAngle - fadeOutStartAngle) /
|
||||
(fadeOutEndAngle - fadeOutStartAngle)).coerceIn(0f, 1f)
|
||||
val alpha = (baseAlpha * rearPresence).coerceIn(0f, 1f)
|
||||
val internalAlphaShapeSetting = resources.getInteger(R.integer.carousel_cards_alpha_shape)
|
||||
val alphaShapeSetting = preferences.getInt(
|
||||
CAROUSEL_CARDS_ALPHA_SHAPE,
|
||||
internalAlphaShapeSetting
|
||||
)
|
||||
val shapedAlpha = shapingFunction(alphaInput, alphaShapeSetting)
|
||||
val alpha = (borderAlpha + (1f - borderAlpha) * shapedAlpha).coerceIn(0f, 1f)
|
||||
|
||||
child.animate().cancel()
|
||||
child.alpha = alpha
|
||||
child.scaleX = scale
|
||||
child.scaleY = scale
|
||||
@@ -284,9 +273,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val scaledHeight = height * userFactor
|
||||
val availableHeight = height - bottomInset
|
||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||
return (userFactor * (height - bottomInset)).toInt()
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
@@ -295,13 +282,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
if (gameAdapter.cardSize == 0) return
|
||||
if (bottomInset < 0) return
|
||||
|
||||
itemAnimator?.let {
|
||||
if (savedItemAnimator == null) {
|
||||
savedItemAnimator = it
|
||||
}
|
||||
itemAnimator = null
|
||||
}
|
||||
|
||||
useCustomDrawingOrder = true
|
||||
val cardSize = gameAdapter.cardSize
|
||||
|
||||
@@ -356,12 +336,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
// Detach PagerSnapHelper
|
||||
pagerSnapHelper?.attachToRecyclerView(null)
|
||||
pagerSnapHelper = null
|
||||
savedItemAnimator?.let {
|
||||
if (itemAnimator == null) {
|
||||
itemAnimator = it
|
||||
}
|
||||
savedItemAnimator = null
|
||||
}
|
||||
useCustomDrawingOrder = false
|
||||
// Reset padding and fling
|
||||
setPadding(0, 0, 0, 0)
|
||||
@@ -370,7 +344,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
// Reset scaling
|
||||
for (i in 0 until childCount) {
|
||||
val child = getChildAt(i)
|
||||
child?.translationX = 0f
|
||||
child?.scaleX = 1f
|
||||
child?.scaleY = 1f
|
||||
child?.alpha = 1f
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="?attr/colorControlHighlight" />
|
||||
<item android:state_focused="true" android:color="@android:color/transparent" />
|
||||
<item android:state_selected="true" android:color="@android:color/transparent" />
|
||||
<item android:state_hovered="true" android:color="@android:color/transparent" />
|
||||
<item android:color="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -10,7 +10,6 @@
|
||||
android:clipChildren="true"
|
||||
android:layout_margin="0dp"
|
||||
app:cardBackgroundColor="@color/eden_card_background"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
app:strokeWidth="1dp"
|
||||
app:strokeColor="@color/eden_border">
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:clipChildren="true"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
android:focusable="true"
|
||||
android:transitionName="card_game"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:foreground="@color/eden_border_gradient_start">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
android:focusable="true"
|
||||
android:transitionName="card_game_compact"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:foreground="@color/eden_border_gradient_start">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="0dp"
|
||||
app:cardBackgroundColor="@android:color/transparent"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
<integer name="game_columns_grid">2</integer>
|
||||
<integer name="carousel_max_fling_count">4</integer>
|
||||
<integer name="carousel_focus_search_repeat_threshold_ms">100</integer>
|
||||
<integer name="carousel_cards_scaling_shape">1</integer>
|
||||
<integer name="carousel_cards_alpha_shape">4</integer>
|
||||
|
||||
<!-- Default SWITCH landscape layout -->
|
||||
<integer name="BUTTON_A_X">760</integer>
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
<!-- NVDEC Emulation -->
|
||||
<string name="nvdec_emulation">NVDEC Emulation</string>
|
||||
<string name="nvdec_emulation_description">Change to CPU if a crash occurs on cinematics.</string>
|
||||
<string name="nvdec_emulation_description">Select how video decoding (NVDEC) is handled during cutscenes and intros.</string>
|
||||
<string name="nvdec_emulation_cpu" translatable="false">CPU</string>
|
||||
<string name="nvdec_emulation_gpu" translatable="false">GPU</string>
|
||||
<string name="nvdec_emulation_none">None</string>
|
||||
|
||||
@@ -108,7 +108,6 @@ add_library(
|
||||
settings_input.h
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "web_service/web_result.h"
|
||||
|
||||
namespace AnnounceMultiplayerRoom {
|
||||
|
||||
@@ -241,7 +241,7 @@ std::optional<std::string> MakeRequest(const std::string& url, const std::string
|
||||
response.status);
|
||||
return {};
|
||||
}
|
||||
if (!response.has_header("content-type")) {
|
||||
if (!response.headers.contains("content-type")) {
|
||||
LOG_ERROR(Common, "GET to {}{} returned no content", url, path);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
/// Address families
|
||||
enum class Domain : u8 {
|
||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
||||
INET, ///< Address family for IPv4
|
||||
};
|
||||
|
||||
/// Socket types
|
||||
enum class Type {
|
||||
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
||||
STREAM,
|
||||
DGRAM,
|
||||
RAW,
|
||||
SEQPACKET,
|
||||
};
|
||||
|
||||
/// Protocol values for sockets
|
||||
enum class Protocol : u8 {
|
||||
Unspecified, ///< Represents 0, usable in various places
|
||||
IP,
|
||||
ICMP,
|
||||
TCP,
|
||||
UDP,
|
||||
IPV6,
|
||||
RAW,
|
||||
IGMP,
|
||||
GGP,
|
||||
IPV4,
|
||||
ST,
|
||||
EGP,
|
||||
PIGP,
|
||||
RCCMON,
|
||||
NVPII,
|
||||
PUP,
|
||||
ARGUS,
|
||||
EMCON,
|
||||
XNET,
|
||||
CHAOS,
|
||||
MUX,
|
||||
MEAS,
|
||||
HMP,
|
||||
PRM,
|
||||
IDP,
|
||||
TRUNK1,
|
||||
TRUNK2,
|
||||
LEAF1,
|
||||
LEAF2,
|
||||
RDP,
|
||||
IRTP,
|
||||
TP,
|
||||
BLT,
|
||||
NSP,
|
||||
INP,
|
||||
DCCP,
|
||||
//TODO: 3PC,
|
||||
IDPR,
|
||||
XTP,
|
||||
DDP,
|
||||
CMTP,
|
||||
TPXX,
|
||||
IL,
|
||||
SDRP,
|
||||
ROUTING,
|
||||
FRAGMENT,
|
||||
IDRP,
|
||||
RSVP,
|
||||
GRE,
|
||||
MHRP,
|
||||
BHA,
|
||||
ESP,
|
||||
AH,
|
||||
INLSP,
|
||||
SWIPE,
|
||||
NHRP,
|
||||
MOBILE,
|
||||
TLSP,
|
||||
SKIP,
|
||||
ICMPV6,
|
||||
NONE,
|
||||
DSTOPTS,
|
||||
AHIP,
|
||||
CFTP,
|
||||
HELLO,
|
||||
SATEXPAK,
|
||||
KRYPTOLAN,
|
||||
RVD,
|
||||
IPPC,
|
||||
ADFS,
|
||||
SATMON,
|
||||
VISA,
|
||||
IPCV,
|
||||
CPNX,
|
||||
CPHB,
|
||||
WSN,
|
||||
PVP,
|
||||
BRSATMON,
|
||||
ND,
|
||||
WBMON,
|
||||
WBEXPAK,
|
||||
EON,
|
||||
VMTP,
|
||||
SVMTP,
|
||||
VINES,
|
||||
TTP,
|
||||
IGP,
|
||||
DGP,
|
||||
TCF,
|
||||
IGRP,
|
||||
OSPFIGP,
|
||||
SRPC,
|
||||
LARP,
|
||||
MTP,
|
||||
AX25,
|
||||
IPEIP,
|
||||
MICP,
|
||||
SCCSP,
|
||||
ETHERIP,
|
||||
ENCAP,
|
||||
APES,
|
||||
GMTP,
|
||||
IPCOMP,
|
||||
SCTP,
|
||||
MH,
|
||||
UDPLITE,
|
||||
HIP,
|
||||
SHIM6,
|
||||
PIM,
|
||||
CARP,
|
||||
PGM,
|
||||
MPLS,
|
||||
PFSYNC
|
||||
};
|
||||
|
||||
/// Shutdown mode
|
||||
enum class ShutdownHow {
|
||||
RD,
|
||||
WR,
|
||||
RDWR,
|
||||
};
|
||||
|
||||
/// Array of IPv4 address
|
||||
using IPv4Address = std::array<u8, 4>;
|
||||
|
||||
/// Cross-platform sockaddr structure
|
||||
struct SockAddrIn {
|
||||
Domain family;
|
||||
IPv4Address ip;
|
||||
u16 portno;
|
||||
};
|
||||
|
||||
constexpr u32 FLAG_MSG_PEEK = 0x2;
|
||||
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
|
||||
constexpr u32 FLAG_O_NONBLOCK = 0x800;
|
||||
|
||||
/// Cross-platform addrinfo structure
|
||||
struct AddrInfo {
|
||||
Domain family;
|
||||
Type socket_type;
|
||||
Protocol protocol;
|
||||
SockAddrIn addr;
|
||||
std::optional<std::string> canon_name;
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
@@ -1129,6 +1129,7 @@ add_library(core STATIC
|
||||
internal_network/network_interface.h
|
||||
internal_network/socket_proxy.cpp
|
||||
internal_network/socket_proxy.h
|
||||
internal_network/socket_types.h
|
||||
internal_network/sockets.h
|
||||
internal_network/wifi_scanner.h
|
||||
launch_timestamp_cache.cpp
|
||||
@@ -1167,6 +1168,12 @@ add_library(core STATIC
|
||||
tools/renderdoc.cpp
|
||||
tools/renderdoc.h)
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_sources(core PRIVATE
|
||||
internal_network/socket_icmp.cpp
|
||||
internal_network/socket_icmp.h)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WIFI_SCAN)
|
||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||
if (LINUX)
|
||||
|
||||
@@ -17,12 +17,56 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||
const auto MAX_MSG_SIZE = 0x1000;
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||
static struct DebugFlusher {
|
||||
std::string msg_buffer;
|
||||
std::mutex msg_mutex;
|
||||
std::condition_variable msg_cv;
|
||||
std::chrono::steady_clock::time_point last_msg_time;
|
||||
std::optional<std::jthread> thread;
|
||||
} flusher_data;
|
||||
R_SUCCEED_IF(len == 0);
|
||||
std::string msg_buffer(len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||
// Only start the thread the very first time this function is called
|
||||
if (!flusher_data.thread) {
|
||||
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
||||
while (!stop_token.stop_requested()) {
|
||||
std::unique_lock lock(flusher_data.msg_mutex);
|
||||
flusher_data.msg_cv.wait(lock, [&stop_token] {
|
||||
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
|
||||
});
|
||||
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
|
||||
break;
|
||||
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
|
||||
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
|
||||
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
|
||||
});
|
||||
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
|
||||
if (!flusher_data.msg_buffer.empty()) {
|
||||
// Remove trailing newline as LOG_INFO adds that anyways
|
||||
if (flusher_data.msg_buffer.back() == '\n')
|
||||
flusher_data.msg_buffer.pop_back();
|
||||
|
||||
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
|
||||
flusher_data.msg_buffer.clear();
|
||||
}
|
||||
if (stop_token.stop_requested()) break;
|
||||
}
|
||||
}
|
||||
flusher_data.msg_cv.notify_all();
|
||||
});
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(flusher_data.msg_mutex);
|
||||
const auto old_size = flusher_data.msg_buffer.size();
|
||||
flusher_data.msg_buffer.resize(old_size + len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
|
||||
flusher_data.last_msg_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
flusher_data.msg_cv.notify_one();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/ldn/ldn_results.h"
|
||||
#include "core/hle/service/ldn/ldn_types.h"
|
||||
|
||||
@@ -5,136 +5,33 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <ctime>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NIM {
|
||||
|
||||
class IShopServiceAsync final : public ServiceFramework<IShopServiceAsync> {
|
||||
public:
|
||||
explicit IShopServiceAsync(Core::System& system_)
|
||||
: ServiceFramework{system_, "IShopServiceAsync"},
|
||||
service_context{system_, "IShopServiceAsync"} {
|
||||
: ServiceFramework{system_, "IShopServiceAsync"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, D<&IShopServiceAsync::Cancel>, "Cancel"},
|
||||
{1, D<&IShopServiceAsync::GetSize>, "GetSize"},
|
||||
{2, D<&IShopServiceAsync::Read>, "Read"},
|
||||
{3, D<&IShopServiceAsync::GetErrorCode>, "GetErrorCode"},
|
||||
{4, D<&IShopServiceAsync::Request>, "Request"},
|
||||
{5, D<&IShopServiceAsync::Prepare>, "Prepare"},
|
||||
{0, nullptr, "Cancel"},
|
||||
{1, nullptr, "GetSize"},
|
||||
{2, nullptr, "Read"},
|
||||
{3, nullptr, "GetErrorCode"},
|
||||
{4, nullptr, "Request"},
|
||||
{5, nullptr, "Prepare"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
completion_event = service_context.CreateEvent("IShopServiceAsync:Completion");
|
||||
}
|
||||
|
||||
~IShopServiceAsync() override {
|
||||
CancelImpl();
|
||||
service_context.CloseEvent(completion_event);
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent* GetEvent() const {
|
||||
return &completion_event->GetReadableEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
Kernel::KEvent* completion_event;
|
||||
|
||||
std::jthread worker;
|
||||
std::atomic<u32> error_code{0};
|
||||
|
||||
std::mutex data_mutex;
|
||||
std::vector<u8> download_data;
|
||||
|
||||
void CancelImpl() {
|
||||
worker.request_stop();
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
|
||||
Result Cancel() {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
CancelImpl();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSize(Out<u64> out_size) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
std::scoped_lock lock{data_mutex};
|
||||
*out_size = download_data.size();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Read(Out<u64> out_size, u64 offset, OutBuffer<BufferAttr_HipcAutoSelect> out_buffer) {
|
||||
std::scoped_lock lock{data_mutex};
|
||||
|
||||
u64 actual_read = 0;
|
||||
if (offset < download_data.size()) {
|
||||
actual_read = std::min<u64>(out_buffer.size(), download_data.size() - offset);
|
||||
std::memcpy(out_buffer.data(), download_data.data() + offset, actual_read);
|
||||
}
|
||||
|
||||
*out_size = actual_read;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetErrorCode(Out<u32> out_error_code) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
*out_error_code = error_code.load();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Request() {
|
||||
LOG_DEBUG(Service_NIM, "(STUBBED) called");
|
||||
CancelImpl();
|
||||
|
||||
error_code.store(0);
|
||||
completion_event->Clear(system.Kernel());
|
||||
|
||||
{
|
||||
std::scoped_lock lock{data_mutex};
|
||||
download_data.clear();
|
||||
}
|
||||
|
||||
worker = std::jthread([this](const std::stop_token& stop_token) {
|
||||
if (stop_token.stop_requested()) {
|
||||
error_code.store(1);
|
||||
} else {
|
||||
std::scoped_lock lock{data_mutex};
|
||||
// Dummy JSON response, else it fails...
|
||||
const std::string dummy_response = "{}";
|
||||
download_data.assign(dummy_response.begin(), dummy_response.end());
|
||||
error_code.store(0);
|
||||
}
|
||||
completion_event->Signal(system.Kernel());
|
||||
});
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Prepare(InArray<char, BufferAttr_HipcMapAlias> in_path, InArray<char, BufferAttr_HipcMapAlias> in_post) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
if (!in_path.empty()) {
|
||||
std::string url(in_path.data(), in_path.size());
|
||||
LOG_INFO(Service_NIM, "Preparing request for URL: {}", url);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -152,13 +49,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateAsyncInterface(HLERequestContext& ctx) {LOG_DEBUG(Service_NIM, "called");
|
||||
auto async_interface = std::make_shared<IShopServiceAsync>(system);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
void CreateAsyncInterface(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushCopyObjects(ctx, async_interface->GetEvent());
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, std::move(async_interface));
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -129,6 +129,13 @@ ServerManager::~ServerManager() {
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||
for (size_t i = 0; i < num_threads; i++) {
|
||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||
server_manager->m_system.RunServer(std::move(server_manager));
|
||||
}
|
||||
@@ -245,14 +252,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||
for (size_t i = 0; i < num_threads; i++) {
|
||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
|
||||
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||
}
|
||||
}
|
||||
|
||||
Result ServerManager::LoopProcess() {
|
||||
SCOPE_EXIT {
|
||||
m_stopped.Set();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -48,8 +51,8 @@ public:
|
||||
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||
|
||||
Result LoopProcess();
|
||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||
|
||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||
|
||||
private:
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "common/logging.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
@@ -20,6 +20,9 @@
|
||||
#include "core/hle/service/sockets/sockets_translate.h"
|
||||
#include "core/internal_network/network.h"
|
||||
#include "core/internal_network/socket_proxy.h"
|
||||
#if defined(__unix__) && !defined(__APPLE__)
|
||||
#include "core/internal_network/socket_icmp.h"
|
||||
#endif
|
||||
#include "core/internal_network/sockets.h"
|
||||
#include "network/network.h"
|
||||
#include <common/settings.h>
|
||||
@@ -28,15 +31,18 @@ namespace Service::Sockets {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsConnectionBased(Type type) {
|
||||
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
|
||||
switch (type) {
|
||||
case Type::STREAM:
|
||||
case Network::Type::STREAM:
|
||||
case Network::Type::SEQPACKET:
|
||||
return true;
|
||||
case Type::DGRAM:
|
||||
case Network::Type::RAW:
|
||||
case Network::Type::DGRAM:
|
||||
case Network::Type::RDM:
|
||||
case Network::Type::Unspecified:
|
||||
return false;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||
return false;
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +98,7 @@ void BSD::ConnectWork::Execute(BSD* bsd) {
|
||||
void BSD::ConnectWork::Response(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
||||
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
@@ -170,10 +176,9 @@ void BSD::Socket(HLERequestContext& ctx) {
|
||||
const u32 domain = rp.Pop<u32>();
|
||||
const u32 type = rp.Pop<u32>();
|
||||
const u32 protocol = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||
|
||||
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
||||
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -189,8 +194,8 @@ void BSD::SocketExempt(HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||
|
||||
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
||||
if (bsd_errno == Errno::SUCCESS) {
|
||||
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
||||
if (bsd_errno == Network::Errno::E_SUCCESS) {
|
||||
bsd_errno = ShutdownImpl(fd, 0);
|
||||
}
|
||||
|
||||
@@ -264,13 +269,13 @@ void BSD::GetPeerName(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||
|
||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
||||
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||
rb.PushEnum(bsd_errno);
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
@@ -282,13 +287,13 @@ void BSD::GetSockName(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||
|
||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||
|
||||
ctx.WriteBuffer(write_buffer);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
||||
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
||||
rb.PushEnum(bsd_errno);
|
||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||
}
|
||||
@@ -296,21 +301,19 @@ void BSD::GetSockName(HLERequestContext& ctx) {
|
||||
void BSD::GetSockOpt(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const u32 level = rp.Pop<u32>();
|
||||
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
||||
const auto level = Network::SocketLevel(rp.Pop<u32>());
|
||||
const auto optname = Network::OptName(rp.Pop<u32>());
|
||||
|
||||
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
||||
|
||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
|
||||
optval.size());
|
||||
|
||||
const Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
|
||||
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||
|
||||
ctx.WriteBuffer(optval);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 5};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
|
||||
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||
rb.PushEnum(err);
|
||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||
}
|
||||
@@ -333,7 +336,7 @@ void BSD::Fcntl(HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
||||
|
||||
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
|
||||
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
@@ -345,13 +348,11 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
const s32 fd = rp.Pop<s32>();
|
||||
const u32 level = rp.Pop<u32>();
|
||||
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
|
||||
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
|
||||
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
|
||||
const auto optval = ctx.ReadBuffer();
|
||||
|
||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
|
||||
static_cast<u32>(optname), optval.size());
|
||||
|
||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
|
||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||
}
|
||||
|
||||
@@ -473,7 +474,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
|
||||
struct OutputParameters {
|
||||
s32 ret;
|
||||
Errno bsd_errno;
|
||||
Network::Errno bsd_errno;
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0x8);
|
||||
|
||||
@@ -485,7 +486,7 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
if (is_user) {
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = 0,
|
||||
.bsd_errno = Errno::INVAL,
|
||||
.bsd_errno = Network::Errno::E_INVAL,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -494,10 +495,10 @@ void BSD::DuplicateSocket(HLERequestContext& ctx) {
|
||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = *res,
|
||||
.bsd_errno = Errno::SUCCESS,
|
||||
.bsd_errno = Network::Errno::E_SUCCESS,
|
||||
});
|
||||
} else {
|
||||
auto* err = std::get_if<Errno>(&res_v);
|
||||
auto* err = std::get_if<Network::Errno>(&res_v);
|
||||
rb.PushRaw(OutputParameters{
|
||||
.ret = 0,
|
||||
.bsd_errno = *err,
|
||||
@@ -512,7 +513,7 @@ void BSD::EventFd(HLERequestContext& ctx) {
|
||||
|
||||
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
||||
|
||||
BuildErrnoResponse(ctx, Errno::SUCCESS);
|
||||
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
|
||||
}
|
||||
|
||||
template <typename Work>
|
||||
@@ -521,132 +522,143 @@ void BSD::ExecuteWork(HLERequestContext& ctx, Work work) {
|
||||
work.Response(ctx);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||
std::pair<s32, Network::Errno> BSD::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
|
||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
||||
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
||||
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
|
||||
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
|
||||
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
|
||||
// fine, can use on bsd:s and bsd:u
|
||||
} else {
|
||||
return {-1, Errno::INVAL};
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
|
||||
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
|
||||
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
||||
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
|
||||
type = Network::Type(u32(type) & ~0x20000000);
|
||||
|
||||
const s32 fd = FindFreeFileDescriptorHandle();
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(Service, "No more file descriptors available");
|
||||
return {-1, Errno::MFILE};
|
||||
return {-1, Network::Errno::E_MFILE};
|
||||
}
|
||||
|
||||
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
|
||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||
file_descriptors[fd].reset();
|
||||
return {-1, Network::Errno::E_NOTCONN};
|
||||
}
|
||||
|
||||
file_descriptors[fd] = FileDescriptor{};
|
||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||
|
||||
// ENONMEM might be thrown here
|
||||
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
|
||||
|
||||
LOG_INFO(Service, "New socket fd={}", fd);
|
||||
|
||||
// While room is important -- we need to remember ICMP takes priority over **everything else**
|
||||
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
|
||||
// to browse the internet while playing LDN or something stupid like that?
|
||||
auto room_member = Network::GetRoomMember().lock();
|
||||
if (room_member && room_member->IsConnected()) {
|
||||
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
|
||||
&& (room_member && room_member->IsConnected())) {
|
||||
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
||||
descriptor.socket->fd = fd;
|
||||
} else {
|
||||
descriptor.socket = std::make_shared<Network::Socket>();
|
||||
}
|
||||
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
|
||||
|
||||
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
||||
descriptor.is_connection_based = IsConnectionBased(type);
|
||||
|
||||
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
|
||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||
return {-1, Errno::NOTCONN};
|
||||
#if defined(__unix__) && !defined(__APPLE__)
|
||||
// ...only unix has this issue it seems, ICMP works otherwise fine on win
|
||||
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
|
||||
&& bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
LOG_WARNING(Network, "Using ICMP emulated socket");
|
||||
descriptor.socket = std::make_shared<Network::IcmpSocket>();
|
||||
descriptor.socket->fd = fd;
|
||||
}
|
||||
|
||||
return {fd, Errno::SUCCESS};
|
||||
#endif
|
||||
descriptor.is_connection_based = IsConnectionBased(type);
|
||||
#ifdef _WIN32
|
||||
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
|
||||
#else
|
||||
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
|
||||
#endif
|
||||
file_descriptors[fd].reset();
|
||||
return {-1, bsd_errno};
|
||||
}
|
||||
return {fd, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
||||
s32 nfds, s32 timeout) {
|
||||
std::pair<s32, Network::Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
|
||||
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
|
||||
if (nfds <= 0) {
|
||||
// When no entries are provided, -1 is returned with errno zero
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
if (read_buffer.size() < nfds * sizeof(PollFD)) {
|
||||
return {-1, Errno::INVAL};
|
||||
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
if (write_buffer.size() < nfds * sizeof(PollFD)) {
|
||||
return {-1, Errno::INVAL};
|
||||
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
|
||||
std::vector<PollFD> fds(nfds);
|
||||
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
|
||||
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
|
||||
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
|
||||
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
|
||||
|
||||
if (timeout >= 0) {
|
||||
const s64 seconds = timeout / 1000;
|
||||
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
|
||||
|
||||
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
|
||||
if (seconds < 0) {
|
||||
return {-1, Errno::INVAL};
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
if (nanoseconds > 999'999'999) {
|
||||
return {-1, Errno::INVAL};
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
} else if (timeout != -1) {
|
||||
return {-1, Errno::INVAL};
|
||||
return {-1, Network::Errno::E_INVAL};
|
||||
}
|
||||
|
||||
for (PollFD& pollfd : fds) {
|
||||
ASSERT(False(pollfd.revents));
|
||||
|
||||
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
|
||||
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
|
||||
pollfd.revents = PollEvents{};
|
||||
return {0, Errno::SUCCESS};
|
||||
}
|
||||
|
||||
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
|
||||
if (!descriptor) {
|
||||
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
|
||||
pollfd.revents = PollEvents::Nval;
|
||||
return {0, Errno::SUCCESS};
|
||||
for (size_t i = 0; i < in_fds.size(); ++i) {
|
||||
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
|
||||
if (!IsFileDescriptorValid(in_fds[i].fd)) {
|
||||
out_fds[i].revents = {};
|
||||
if (!file_descriptors[in_fds[i].fd])
|
||||
out_fds[i].revents = Network::PollEvents::NVAL;
|
||||
return {0, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Network::PollFD> host_pollfds(fds.size());
|
||||
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
|
||||
Network::PollFD result;
|
||||
result.socket = file_descriptors[pollfd.fd]->socket.get();
|
||||
result.events = Translate(pollfd.events);
|
||||
result.revents = Network::PollEvents{};
|
||||
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
|
||||
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
|
||||
Network::HostPollFD result{};
|
||||
result.socket = file_descriptors[e.fd]->socket.get();
|
||||
result.events = e.events;
|
||||
result.revents = {};
|
||||
return result;
|
||||
});
|
||||
|
||||
const auto result = Network::Poll(host_pollfds, timeout);
|
||||
|
||||
const size_t num = host_pollfds.size();
|
||||
for (size_t i = 0; i < num; ++i) {
|
||||
fds[i].revents = Translate(host_pollfds[i].revents);
|
||||
}
|
||||
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
|
||||
|
||||
return Translate(result);
|
||||
auto const res = Network::Poll(host_pollfds, timeout);
|
||||
for (size_t i = 0; i < in_fds.size(); ++i)
|
||||
out_fds[i].revents = host_pollfds[i].revents;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
std::pair<s32, Network::Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
|
||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||
if (new_fd < 0) {
|
||||
LOG_ERROR(Service, "No more file descriptors available");
|
||||
return {-1, Errno::MFILE};
|
||||
return {-1, Network::Errno::E_MFILE};
|
||||
}
|
||||
|
||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||
auto [result, bsd_errno] = descriptor.socket->Accept();
|
||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||
return {-1, Translate(bsd_errno)};
|
||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
return {-1, bsd_errno};
|
||||
}
|
||||
|
||||
file_descriptors[new_fd] = FileDescriptor{};
|
||||
@@ -654,267 +666,218 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
new_descriptor.socket = std::move(result.socket);
|
||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||
|
||||
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
||||
PutValue(write_buffer, guest_addr_in);
|
||||
|
||||
return {new_fd, Errno::SUCCESS};
|
||||
PutValue(write_buffer, result.sockaddr_in);
|
||||
return {new_fd, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
Network::Errno BSD::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
ASSERT(addr.size() >= 16);
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||
|
||||
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
||||
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||
return file_descriptors[fd]->socket->Bind(addr_in);
|
||||
}
|
||||
|
||||
Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
Network::Errno BSD::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
ASSERT(addr.size() >= 16);
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||
|
||||
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
||||
|
||||
if (result == Errno::ISCONN) {
|
||||
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
|
||||
if (result == Network::Errno::E_ISCONN) {
|
||||
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
||||
return Errno::SUCCESS;
|
||||
return Network::Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
Network::Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||
return Translate(bsd_errno);
|
||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
return bsd_errno;
|
||||
}
|
||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||
|
||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||
write_buffer.resize(sizeof(guest_addrin));
|
||||
PutValue(write_buffer, guest_addrin);
|
||||
return Translate(bsd_errno);
|
||||
ASSERT(write_buffer.size() >= addr_in.len);
|
||||
write_buffer.resize(addr_in.len);
|
||||
PutValue(write_buffer, addr_in);
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
Network::Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
||||
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||
return Translate(bsd_errno);
|
||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
return bsd_errno;
|
||||
}
|
||||
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||
|
||||
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||
write_buffer.resize(sizeof(guest_addrin));
|
||||
PutValue(write_buffer, guest_addrin);
|
||||
return Translate(bsd_errno);
|
||||
ASSERT(write_buffer.size() >= addr_in.len);
|
||||
write_buffer.resize(addr_in.len);
|
||||
PutValue(write_buffer, addr_in);
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
||||
Network::Errno BSD::ListenImpl(s32 fd, s32 backlog) {
|
||||
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
||||
return file_descriptors[fd]->socket->Listen(backlog);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
||||
std::pair<s32, Network::Errno> BSD::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
|
||||
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
|
||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||
|
||||
switch (cmd) {
|
||||
case FcntlCmd::GETFL:
|
||||
case Network::FcntlCmd::GETFL:
|
||||
ASSERT(arg == 0);
|
||||
return {descriptor.flags, Errno::SUCCESS};
|
||||
case FcntlCmd::SETFL: {
|
||||
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
|
||||
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
|
||||
if (bsd_errno != Errno::SUCCESS) {
|
||||
return {descriptor.flags, Network::Errno::E_SUCCESS};
|
||||
case Network::FcntlCmd::SETFL: {
|
||||
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
|
||||
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
|
||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
return {-1, bsd_errno};
|
||||
}
|
||||
descriptor.flags = arg;
|
||||
return {0, Errno::SUCCESS};
|
||||
return {0, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
||||
return {-1, Errno::SUCCESS};
|
||||
return {-1, Network::Errno::E_SUCCESS};
|
||||
}
|
||||
}
|
||||
|
||||
Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
||||
Network::Errno BSD::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
|
||||
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
||||
UNIMPLEMENTED_MSG("Unknown getsockopt level");
|
||||
return Errno::SUCCESS;
|
||||
if (level != Network::SocketLevel::SOCKET) {
|
||||
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
|
||||
}
|
||||
|
||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||
|
||||
switch (optname) {
|
||||
case OptName::ERROR_: {
|
||||
case Network::OptName::ERROR_: {
|
||||
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
||||
if (getsockopt_err == Network::Errno::SUCCESS) {
|
||||
Errno translated_pending_err = Translate(pending_err);
|
||||
if (getsockopt_err == Network::Errno::E_SUCCESS) {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
optval.size() == sizeof(Errno), { return Errno::INVAL; },
|
||||
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
|
||||
"Incorrect getsockopt option size");
|
||||
optval.resize(sizeof(Errno));
|
||||
PutValue(optval, translated_pending_err);
|
||||
optval.resize(sizeof(Network::Errno));
|
||||
PutValue(optval, pending_err);
|
||||
}
|
||||
return Translate(getsockopt_err);
|
||||
return getsockopt_err;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||
return Errno::SUCCESS;
|
||||
return Network::Errno::E_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
||||
Network::Errno BSD::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
}
|
||||
|
||||
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
||||
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
|
||||
return Errno::SUCCESS;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||
|
||||
if (optname == OptName::LINGER) {
|
||||
ASSERT(optval.size() == sizeof(Linger));
|
||||
auto linger = GetValue<Linger>(optval);
|
||||
ASSERT(linger.onoff == 0 || linger.onoff == 1);
|
||||
|
||||
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
|
||||
}
|
||||
|
||||
ASSERT(optval.size() == sizeof(u32));
|
||||
auto value = GetValue<u32>(optval);
|
||||
|
||||
switch (optname) {
|
||||
case OptName::REUSEADDR:
|
||||
ASSERT(value == 0 || value == 1);
|
||||
return Translate(socket->SetReuseAddr(value != 0));
|
||||
case OptName::KEEPALIVE:
|
||||
ASSERT(value == 0 || value == 1);
|
||||
return Translate(socket->SetKeepAlive(value != 0));
|
||||
case OptName::BROADCAST:
|
||||
ASSERT(value == 0 || value == 1);
|
||||
return Translate(socket->SetBroadcast(value != 0));
|
||||
case OptName::SNDBUF:
|
||||
return Translate(socket->SetSndBuf(value));
|
||||
case OptName::RCVBUF:
|
||||
return Translate(socket->SetRcvBuf(value));
|
||||
case OptName::SNDTIMEO:
|
||||
return Translate(socket->SetSndTimeo(value));
|
||||
case OptName::RCVTIMEO:
|
||||
return Translate(socket->SetRcvTimeo(value));
|
||||
case OptName::NOSIGPIPE:
|
||||
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
|
||||
return Errno::SUCCESS;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
return socket->SetSockOpt(level, optname, optval);
|
||||
}
|
||||
|
||||
Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
||||
Network::Errno BSD::ShutdownImpl(s32 fd, s32 how) {
|
||||
LOG_DEBUG(Network, "fd={},how={}", fd, how);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
|
||||
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
|
||||
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||
std::pair<s32, Network::Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
|
||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||
|
||||
// Apply flags
|
||||
using Network::FLAG_MSG_DONTWAIT;
|
||||
using Network::FLAG_O_NONBLOCK;
|
||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
||||
flags &= ~FLAG_MSG_DONTWAIT;
|
||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||
descriptor.socket->SetNonBlock(true);
|
||||
}
|
||||
}
|
||||
|
||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
|
||||
|
||||
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
|
||||
// Restore original state
|
||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
|
||||
descriptor.socket->SetNonBlock(false);
|
||||
}
|
||||
|
||||
return {ret, bsd_errno};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||
std::vector<u8>& addr) {
|
||||
std::pair<s32, Network::Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
|
||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
|
||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||
@@ -929,19 +892,17 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
||||
}
|
||||
|
||||
// Apply flags
|
||||
using Network::FLAG_MSG_DONTWAIT;
|
||||
using Network::FLAG_O_NONBLOCK;
|
||||
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
||||
flags &= ~FLAG_MSG_DONTWAIT;
|
||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
||||
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||
descriptor.socket->SetNonBlock(true);
|
||||
}
|
||||
}
|
||||
|
||||
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
|
||||
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
|
||||
|
||||
// Restore original state
|
||||
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
||||
descriptor.socket->SetNonBlock(false);
|
||||
}
|
||||
|
||||
@@ -950,58 +911,59 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
|
||||
addr.clear();
|
||||
} else {
|
||||
ASSERT(addr.size() >= 16);
|
||||
const SockAddrIn result = Translate(addr_in);
|
||||
PutValue(addr, result);
|
||||
PutValue(addr, addr_in);
|
||||
}
|
||||
}
|
||||
|
||||
return {ret, bsd_errno};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||
std::pair<s32, Network::Errno> BSD::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
||||
return file_descriptors[fd]->socket->Send(message, flags);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||
std::span<const u8> addr) {
|
||||
std::pair<s32, Network::Errno> BSD::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
|
||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return {-1, Errno::BADF};
|
||||
return {-1, Network::Errno::E_BADF};
|
||||
}
|
||||
|
||||
Network::SockAddrIn addr_in;
|
||||
Network::SockAddrIn addr_in{};
|
||||
Network::SockAddrIn* p_addr_in = nullptr;
|
||||
if (!addr.empty()) {
|
||||
ASSERT(addr.size() >= 16);
|
||||
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
||||
addr_in = Translate(guest_addr_in);
|
||||
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
|
||||
addr_in = guest_addr_in;
|
||||
p_addr_in = &addr_in;
|
||||
}
|
||||
|
||||
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
||||
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
|
||||
}
|
||||
|
||||
Errno BSD::CloseImpl(s32 fd) {
|
||||
Network::Errno BSD::CloseImpl(s32 fd) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
if (!file_descriptors[fd]->socket) {
|
||||
LOG_WARNING(Service, "Uninitialized socket");
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
||||
if (bsd_errno != Errno::SUCCESS) {
|
||||
auto const bsd_errno = file_descriptors[fd]->socket->Close();
|
||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
@@ -1011,15 +973,16 @@ Errno BSD::CloseImpl(s32 fd) {
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
std::variant<s32, Network::Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return Errno::BADF;
|
||||
return Network::Errno::E_BADF;
|
||||
}
|
||||
|
||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||
if (new_fd < 0) {
|
||||
if (!IsFileDescriptorValid(new_fd)) {
|
||||
LOG_ERROR(Service, "No more file descriptors available");
|
||||
return Errno::MFILE;
|
||||
return Network::Errno::E_MFILE;
|
||||
}
|
||||
|
||||
file_descriptors[new_fd] = FileDescriptor{
|
||||
@@ -1031,6 +994,7 @@ std::variant<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) {
|
||||
}
|
||||
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
||||
LOG_DEBUG(Network, "fd={}", fd);
|
||||
if (!IsFileDescriptorValid(fd)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -1042,41 +1006,40 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) {
|
||||
}
|
||||
|
||||
s32 BSD::FindFreeFileDescriptorHandle() noexcept {
|
||||
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
||||
if (!file_descriptors[fd]) {
|
||||
// first three file descriptors are reserved for:
|
||||
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
|
||||
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
|
||||
if (!file_descriptors[fd])
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
||||
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
||||
if (fd < 0 || fd >= s32(file_descriptors.size())) {
|
||||
LOG_ERROR(Service, "Invalid handle={}", fd);
|
||||
return false;
|
||||
}
|
||||
if (!file_descriptors[fd]) {
|
||||
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
|
||||
LOG_ERROR(Service, "handle={} is not allocated", fd);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||
void BSD::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
||||
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
||||
rb.PushEnum(bsd_errno);
|
||||
}
|
||||
|
||||
void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||
for (auto& optional_descriptor : file_descriptors) {
|
||||
if (!optional_descriptor.has_value()) {
|
||||
continue;
|
||||
if (optional_descriptor.has_value()) {
|
||||
FileDescriptor& descriptor = *optional_descriptor;
|
||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||
}
|
||||
FileDescriptor& descriptor = *optional_descriptor;
|
||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <variant>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sockets/sockets.h"
|
||||
#include "network/network.h"
|
||||
@@ -35,8 +35,8 @@ public:
|
||||
// These methods are called from SSL; the first two are also called from
|
||||
// this class for the corresponding IPC methods.
|
||||
// On the real device, the SSL service makes IPC calls to this service.
|
||||
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
||||
Errno CloseImpl(s32 fd);
|
||||
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
|
||||
Network::Errno CloseImpl(s32 fd);
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||
|
||||
private:
|
||||
@@ -58,7 +58,7 @@ private:
|
||||
std::span<const u8> read_buffer;
|
||||
std::vector<u8> write_buffer;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct AcceptWork {
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
s32 fd;
|
||||
std::vector<u8> write_buffer;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct ConnectWork {
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
|
||||
s32 fd;
|
||||
std::span<const u8> addr;
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct RecvWork {
|
||||
@@ -88,7 +88,7 @@ private:
|
||||
u32 flags;
|
||||
std::vector<u8> message;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct RecvFromWork {
|
||||
@@ -100,7 +100,7 @@ private:
|
||||
std::vector<u8> message;
|
||||
std::vector<u8> addr;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct SendWork {
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
u32 flags;
|
||||
std::span<const u8> message;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
struct SendToWork {
|
||||
@@ -123,7 +123,7 @@ private:
|
||||
std::span<const u8> message;
|
||||
std::span<const u8> addr;
|
||||
s32 ret{};
|
||||
Errno bsd_errno{};
|
||||
Network::Errno bsd_errno{};
|
||||
};
|
||||
|
||||
void RegisterClient(HLERequestContext& ctx);
|
||||
@@ -155,29 +155,29 @@ private:
|
||||
template <typename Work>
|
||||
void ExecuteWork(HLERequestContext& ctx, Work work);
|
||||
|
||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Errno ListenImpl(s32 fd, s32 backlog);
|
||||
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
|
||||
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
|
||||
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
|
||||
Errno ShutdownImpl(s32 fd, s32 how);
|
||||
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
|
||||
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||
Network::Errno ListenImpl(s32 fd, s32 backlog);
|
||||
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
|
||||
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
|
||||
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
|
||||
Network::Errno ShutdownImpl(s32 fd, s32 how);
|
||||
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||
std::vector<u8>& addr);
|
||||
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||
std::span<const u8> addr);
|
||||
|
||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||
|
||||
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
||||
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
|
||||
|
||||
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
||||
|
||||
|
||||
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
|
||||
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
||||
}
|
||||
|
||||
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
||||
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
|
||||
// These combinations have been verified on console (but are not
|
||||
// exhaustive).
|
||||
switch (result) {
|
||||
case GetAddrInfoError::SUCCESS:
|
||||
case Network::GetAddrInfoError::SUCCESS:
|
||||
return NetDbError::Success;
|
||||
case GetAddrInfoError::AGAIN:
|
||||
case Network::GetAddrInfoError::AGAIN:
|
||||
return NetDbError::TryAgain;
|
||||
case GetAddrInfoError::NODATA:
|
||||
case Network::GetAddrInfoError::NODATA:
|
||||
return NetDbError::HostNotFound;
|
||||
case GetAddrInfoError::SERVICE:
|
||||
case Network::GetAddrInfoError::SERVICE:
|
||||
return NetDbError::Success;
|
||||
default:
|
||||
return NetDbError::HostNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
||||
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
|
||||
// These combinations have been verified on console (but are not
|
||||
// exhaustive).
|
||||
switch (result) {
|
||||
case GetAddrInfoError::SUCCESS:
|
||||
case Network::GetAddrInfoError::SUCCESS:
|
||||
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
||||
// some reason, but that doesn't seem useful to implement.
|
||||
return Errno::SUCCESS;
|
||||
case GetAddrInfoError::AGAIN:
|
||||
return Errno::SUCCESS;
|
||||
case GetAddrInfoError::NODATA:
|
||||
return Errno::SUCCESS;
|
||||
case GetAddrInfoError::SERVICE:
|
||||
return Errno::INVAL;
|
||||
return Network::Errno::E_SUCCESS;
|
||||
case Network::GetAddrInfoError::AGAIN:
|
||||
return Network::Errno::E_SUCCESS;
|
||||
case Network::GetAddrInfoError::NODATA:
|
||||
return Network::Errno::E_SUCCESS;
|
||||
case Network::GetAddrInfoError::SERVICE:
|
||||
return Network::Errno::E_INVAL;
|
||||
default:
|
||||
return Errno::SUCCESS;
|
||||
return Network::Errno::E_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,7 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
||||
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
||||
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
||||
// doesn't implement h_errno.
|
||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
||||
std::string_view host) {
|
||||
|
||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||
std::vector<u8> data;
|
||||
// h_name: use the input hostname (append nul-terminated)
|
||||
AppendNulTerminated(data, host);
|
||||
@@ -165,12 +163,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
||||
|
||||
Append<u32_be>(data, 0); // count of h_aliases
|
||||
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
||||
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
||||
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
|
||||
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
||||
// h_addr_list:
|
||||
size_t count = vec.size();
|
||||
ASSERT(count <= UINT32_MAX);
|
||||
Append<u32_be>(data, static_cast<uint32_t>(count));
|
||||
Append<u32_be>(data, u32(count));
|
||||
for (const Network::AddrInfo& addrinfo : vec) {
|
||||
// On the Switch, this is passed through htonl despite already being
|
||||
// big-endian, so it ends up as little-endian.
|
||||
@@ -182,7 +180,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::Add
|
||||
return data;
|
||||
}
|
||||
|
||||
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||
struct InputParameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 cancel_handle;
|
||||
@@ -205,7 +203,7 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
||||
// Prevent resolution of Nintendo servers
|
||||
if (IsBlockedHost(host)) {
|
||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||
return {0, GetAddrInfoError::AGAIN};
|
||||
return {0, Network::GetAddrInfoError::AGAIN};
|
||||
}
|
||||
|
||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||
@@ -213,10 +211,10 @@ static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestConte
|
||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||
const u32 data_size = u32(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||
}
|
||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||
return {0, Translate(*err)};
|
||||
return {0, *err};
|
||||
}
|
||||
|
||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||
@@ -224,7 +222,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||
|
||||
struct OutputParameters {
|
||||
NetDbError netdb_error;
|
||||
Errno bsd_errno;
|
||||
Network::Errno bsd_errno;
|
||||
u32 data_size;
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0xc);
|
||||
@@ -244,7 +242,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
||||
struct OutputParameters {
|
||||
u32 data_size;
|
||||
NetDbError netdb_error;
|
||||
Errno bsd_errno;
|
||||
Network::Errno bsd_errno;
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0xc);
|
||||
|
||||
@@ -257,24 +255,23 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
||||
std::string_view host) {
|
||||
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
||||
// Adapted from
|
||||
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
||||
std::vector<u8> data;
|
||||
|
||||
for (const Network::AddrInfo& addrinfo : vec) {
|
||||
// serialized addrinfo:
|
||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||
Append<u32_be>(data, 0); // ai_flags
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
||||
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||
Append<u32_be>(data, 0); // ai_flags
|
||||
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
|
||||
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
|
||||
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
|
||||
Append<u32_be>(data, 16); // ai_addrlen
|
||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||
|
||||
// ai_addr:
|
||||
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
||||
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
|
||||
// On the Switch, the following fields are passed through htonl despite
|
||||
// already being big-endian, so they end up as little-endian.
|
||||
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
||||
@@ -296,7 +293,7 @@ static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& v
|
||||
return data;
|
||||
}
|
||||
|
||||
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||
struct InputParameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 cancel_handle;
|
||||
@@ -321,7 +318,7 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
||||
// Prevent resolution of Nintendo servers
|
||||
if (IsBlockedHost(host)) {
|
||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||
return {0, GetAddrInfoError::AGAIN};
|
||||
return {0, Network::GetAddrInfoError::AGAIN};
|
||||
}
|
||||
|
||||
std::optional<std::string> service = std::nullopt;
|
||||
@@ -331,24 +328,23 @@ static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext
|
||||
}
|
||||
|
||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||
|
||||
auto res_v = Network::GetAddressInfo(host, service);
|
||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||
const u32 data_size = u32(data.size());
|
||||
ctx.WriteBuffer(data, 0);
|
||||
return {data_size, GetAddrInfoError::SUCCESS};
|
||||
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
||||
}
|
||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||
return {0, Translate(*err)};
|
||||
return {0, *err};
|
||||
}
|
||||
|
||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
||||
|
||||
struct OutputParameters {
|
||||
Errno bsd_errno;
|
||||
GetAddrInfoError gai_error;
|
||||
Network::Errno bsd_errno;
|
||||
Network::GetAddrInfoError gai_error;
|
||||
u32 data_size;
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0xc);
|
||||
@@ -364,7 +360,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||
|
||||
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
||||
struct InputParameters {
|
||||
GetAddrInfoError gai_errno;
|
||||
Network::GetAddrInfoError gai_errno;
|
||||
};
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto input = rp.PopRaw<InputParameters>();
|
||||
@@ -382,9 +378,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
||||
|
||||
struct OutputParameters {
|
||||
u32 data_size;
|
||||
GetAddrInfoError gai_error;
|
||||
Network::GetAddrInfoError gai_error;
|
||||
NetDbError netdb_error;
|
||||
Errno bsd_errno;
|
||||
Network::Errno bsd_errno;
|
||||
};
|
||||
static_assert(sizeof(OutputParameters) == 0x10);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@@ -15,242 +16,6 @@ class System;
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
enum class Errno : u32 {
|
||||
SUCCESS = 0,
|
||||
BADF = 9,
|
||||
AGAIN = 11,
|
||||
INVAL = 22,
|
||||
MFILE = 24,
|
||||
PIPE = 32,
|
||||
MSGSIZE = 90,
|
||||
CONNABORTED = 103,
|
||||
CONNRESET = 104,
|
||||
NOTCONN = 107,
|
||||
TIMEDOUT = 110,
|
||||
CONNREFUSED = 111,
|
||||
INPROGRESS = 115,
|
||||
ISCONN = 106,
|
||||
};
|
||||
|
||||
enum class GetAddrInfoError : s32 {
|
||||
SUCCESS = 0,
|
||||
ADDRFAMILY = 1,
|
||||
AGAIN = 2,
|
||||
BADFLAGS = 3,
|
||||
FAIL = 4,
|
||||
FAMILY = 5,
|
||||
MEMORY = 6,
|
||||
NODATA = 7,
|
||||
NONAME = 8,
|
||||
SERVICE = 9,
|
||||
SOCKTYPE = 10,
|
||||
SYSTEM = 11,
|
||||
BADHINTS = 12,
|
||||
PROTOCOL = 13,
|
||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
||||
OTHER = 15,
|
||||
};
|
||||
|
||||
enum class Domain : u32 {
|
||||
Unspecified = 0,
|
||||
INET = 2,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
Unspecified = 0,
|
||||
STREAM = 1,
|
||||
DGRAM = 2,
|
||||
RAW = 3,
|
||||
SEQPACKET = 5,
|
||||
};
|
||||
|
||||
enum class Protocol : u32 {
|
||||
IP = 0,
|
||||
ICMP = 1,
|
||||
TCP = 6,
|
||||
UDP = 17,
|
||||
//
|
||||
IPV6 = 41,
|
||||
RAW = 255,
|
||||
//
|
||||
HOPOPTS = 0,
|
||||
IGMP = 2,
|
||||
GGP = 3,
|
||||
IPV4 = 4,
|
||||
ST = 7,
|
||||
EGP = 8,
|
||||
PIGP = 9,
|
||||
RCCMON = 10,
|
||||
NVPII = 11,
|
||||
PUP = 12,
|
||||
ARGUS = 13,
|
||||
EMCON = 14,
|
||||
XNET = 15,
|
||||
CHAOS = 16,
|
||||
MUX = 18,
|
||||
MEAS = 19,
|
||||
HMP = 20,
|
||||
PRM = 21,
|
||||
IDP = 22,
|
||||
TRUNK1 = 23,
|
||||
TRUNK2 = 24,
|
||||
LEAF1 = 25,
|
||||
LEAF2 = 26,
|
||||
RDP = 27,
|
||||
IRTP = 28,
|
||||
TP = 29,
|
||||
BLT = 30,
|
||||
NSP = 31,
|
||||
INP = 32,
|
||||
DCCP = 33,
|
||||
//3PC = 34,
|
||||
IDPR = 35,
|
||||
XTP = 36,
|
||||
DDP = 37,
|
||||
CMTP = 38,
|
||||
TPXX = 39,
|
||||
IL = 40,
|
||||
SDRP = 42,
|
||||
ROUTING = 43,
|
||||
FRAGMENT = 44,
|
||||
IDRP = 45,
|
||||
RSVP = 46,
|
||||
GRE = 47,
|
||||
MHRP = 48,
|
||||
BHA = 49,
|
||||
ESP = 50,
|
||||
AH = 51,
|
||||
INLSP = 52,
|
||||
SWIPE = 53,
|
||||
NHRP = 54,
|
||||
MOBILE = 55,
|
||||
TLSP = 56,
|
||||
SKIP = 57,
|
||||
ICMPV6 = 58,
|
||||
NONE = 59,
|
||||
DSTOPTS = 60,
|
||||
AHIP = 61,
|
||||
CFTP = 62,
|
||||
HELLO = 63,
|
||||
SATEXPAK = 64,
|
||||
KRYPTOLAN = 65,
|
||||
RVD = 66,
|
||||
IPPC = 67,
|
||||
ADFS = 68,
|
||||
SATMON = 69,
|
||||
VISA = 70,
|
||||
IPCV = 71,
|
||||
CPNX = 72,
|
||||
CPHB = 73,
|
||||
WSN = 74,
|
||||
PVP = 75,
|
||||
BRSATMON = 76,
|
||||
ND = 77,
|
||||
WBMON = 78,
|
||||
WBEXPAK = 79,
|
||||
EON = 80,
|
||||
VMTP = 81,
|
||||
SVMTP = 82,
|
||||
VINES = 83,
|
||||
TTP = 84,
|
||||
IGP = 85,
|
||||
DGP = 86,
|
||||
TCF = 87,
|
||||
IGRP = 88,
|
||||
OSPFIGP = 89,
|
||||
SRPC = 90,
|
||||
LARP = 91,
|
||||
MTP = 92,
|
||||
AX25 = 93,
|
||||
IPEIP = 94,
|
||||
MICP = 95,
|
||||
SCCSP = 96,
|
||||
ETHERIP = 97,
|
||||
ENCAP = 98,
|
||||
APES = 99,
|
||||
GMTP = 100,
|
||||
IPCOMP = 108,
|
||||
SCTP = 132,
|
||||
MH = 135,
|
||||
UDPLITE = 136,
|
||||
HIP = 139,
|
||||
SHIM6 = 140,
|
||||
PIM = 103,
|
||||
CARP = 112,
|
||||
PGM = 113,
|
||||
MPLS = 137,
|
||||
PFSYNC = 240,
|
||||
};
|
||||
|
||||
enum class SocketLevel : u32 {
|
||||
IP = 0,
|
||||
TCP = 6,
|
||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
||||
};
|
||||
|
||||
enum class OptName : u32 {
|
||||
REUSEADDR = 0x4,
|
||||
KEEPALIVE = 0x8,
|
||||
BROADCAST = 0x20,
|
||||
LINGER = 0x80,
|
||||
SNDBUF = 0x1001,
|
||||
RCVBUF = 0x1002,
|
||||
SNDTIMEO = 0x1005,
|
||||
RCVTIMEO = 0x1006,
|
||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
||||
NOSIGPIPE = 0x800, // at least according to libnx
|
||||
ACCEPTFILTER = 0x1000,
|
||||
BINTIME = 0x2000,
|
||||
NO_OFFLOAD = 0x4000,
|
||||
NO_DDP = 0x8000,
|
||||
};
|
||||
|
||||
enum class ShutdownHow : s32 {
|
||||
RD = 0,
|
||||
WR = 1,
|
||||
RDWR = 2,
|
||||
};
|
||||
|
||||
enum class FcntlCmd : s32 {
|
||||
GETFL = 3,
|
||||
SETFL = 4,
|
||||
};
|
||||
|
||||
struct SockAddrIn {
|
||||
u8 len;
|
||||
u8 family;
|
||||
u16 portno;
|
||||
std::array<u8, 4> ip;
|
||||
std::array<u8, 248> zeroes;
|
||||
};
|
||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||
|
||||
enum class PollEvents : u16 {
|
||||
// Using Pascal case because IN is a macro on Windows.
|
||||
In = 1 << 0,
|
||||
Pri = 1 << 1,
|
||||
Out = 1 << 2,
|
||||
Err = 1 << 3,
|
||||
Hup = 1 << 4,
|
||||
Nval = 1 << 5,
|
||||
RdNorm = 1 << 6,
|
||||
RdBand = 1 << 7,
|
||||
WrBand = 1 << 8,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||
|
||||
struct PollFD {
|
||||
s32 fd;
|
||||
PollEvents events;
|
||||
PollEvents revents;
|
||||
};
|
||||
|
||||
struct Linger {
|
||||
u32 onoff;
|
||||
u32 linger;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -15,388 +15,42 @@
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
Errno Translate(Network::Errno value) {
|
||||
switch (value) {
|
||||
case Network::Errno::SUCCESS:
|
||||
return Errno::SUCCESS;
|
||||
case Network::Errno::BADF:
|
||||
return Errno::BADF;
|
||||
case Network::Errno::AGAIN:
|
||||
return Errno::AGAIN;
|
||||
case Network::Errno::INVAL:
|
||||
return Errno::INVAL;
|
||||
case Network::Errno::MFILE:
|
||||
return Errno::MFILE;
|
||||
case Network::Errno::PIPE:
|
||||
return Errno::PIPE;
|
||||
case Network::Errno::CONNREFUSED:
|
||||
return Errno::CONNREFUSED;
|
||||
case Network::Errno::NOTCONN:
|
||||
return Errno::NOTCONN;
|
||||
case Network::Errno::TIMEDOUT:
|
||||
return Errno::TIMEDOUT;
|
||||
case Network::Errno::CONNABORTED:
|
||||
return Errno::CONNABORTED;
|
||||
case Network::Errno::CONNRESET:
|
||||
return Errno::CONNRESET;
|
||||
case Network::Errno::INPROGRESS:
|
||||
return Errno::INPROGRESS;
|
||||
case Network::Errno::ISCONN:
|
||||
return Errno::ISCONN;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
||||
return {value.first, Translate(value.second)};
|
||||
}
|
||||
|
||||
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
|
||||
switch (error) {
|
||||
case Network::GetAddrInfoError::SUCCESS:
|
||||
return GetAddrInfoError::SUCCESS;
|
||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||
return GetAddrInfoError::ADDRFAMILY;
|
||||
case Network::GetAddrInfoError::AGAIN:
|
||||
return GetAddrInfoError::AGAIN;
|
||||
case Network::GetAddrInfoError::BADFLAGS:
|
||||
return GetAddrInfoError::BADFLAGS;
|
||||
case Network::GetAddrInfoError::FAIL:
|
||||
return GetAddrInfoError::FAIL;
|
||||
case Network::GetAddrInfoError::FAMILY:
|
||||
return GetAddrInfoError::FAMILY;
|
||||
case Network::GetAddrInfoError::MEMORY:
|
||||
return GetAddrInfoError::MEMORY;
|
||||
case Network::GetAddrInfoError::NODATA:
|
||||
return GetAddrInfoError::NODATA;
|
||||
case Network::GetAddrInfoError::NONAME:
|
||||
return GetAddrInfoError::NONAME;
|
||||
case Network::GetAddrInfoError::SERVICE:
|
||||
return GetAddrInfoError::SERVICE;
|
||||
case Network::GetAddrInfoError::SOCKTYPE:
|
||||
return GetAddrInfoError::SOCKTYPE;
|
||||
case Network::GetAddrInfoError::SYSTEM:
|
||||
return GetAddrInfoError::SYSTEM;
|
||||
case Network::GetAddrInfoError::BADHINTS:
|
||||
return GetAddrInfoError::BADHINTS;
|
||||
case Network::GetAddrInfoError::PROTOCOL:
|
||||
return GetAddrInfoError::PROTOCOL;
|
||||
case Network::GetAddrInfoError::OVERFLOW_:
|
||||
return GetAddrInfoError::OVERFLOW_;
|
||||
case Network::GetAddrInfoError::OTHER:
|
||||
return GetAddrInfoError::OTHER;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
|
||||
return GetAddrInfoError::OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
const char* Translate(GetAddrInfoError error) {
|
||||
const char* Translate(Network::GetAddrInfoError error) {
|
||||
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
||||
switch (error) {
|
||||
case GetAddrInfoError::SUCCESS:
|
||||
case Network::GetAddrInfoError::SUCCESS:
|
||||
return "Success";
|
||||
case GetAddrInfoError::ADDRFAMILY:
|
||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||
return "Address family for hostname not supported";
|
||||
case GetAddrInfoError::AGAIN:
|
||||
case Network::GetAddrInfoError::AGAIN:
|
||||
return "Temporary failure in name resolution";
|
||||
case GetAddrInfoError::BADFLAGS:
|
||||
case Network::GetAddrInfoError::BADFLAGS:
|
||||
return "Invalid value for ai_flags";
|
||||
case GetAddrInfoError::FAIL:
|
||||
case Network::GetAddrInfoError::FAIL:
|
||||
return "Non-recoverable failure in name resolution";
|
||||
case GetAddrInfoError::FAMILY:
|
||||
case Network::GetAddrInfoError::FAMILY:
|
||||
return "ai_family not supported";
|
||||
case GetAddrInfoError::MEMORY:
|
||||
case Network::GetAddrInfoError::MEMORY:
|
||||
return "Memory allocation failure";
|
||||
case GetAddrInfoError::NODATA:
|
||||
case Network::GetAddrInfoError::NODATA:
|
||||
return "No address associated with hostname";
|
||||
case GetAddrInfoError::NONAME:
|
||||
case Network::GetAddrInfoError::NONAME:
|
||||
return "hostname nor servname provided, or not known";
|
||||
case GetAddrInfoError::SERVICE:
|
||||
case Network::GetAddrInfoError::SERVICE:
|
||||
return "servname not supported for ai_socktype";
|
||||
case GetAddrInfoError::SOCKTYPE:
|
||||
case Network::GetAddrInfoError::SOCKTYPE:
|
||||
return "ai_socktype not supported";
|
||||
case GetAddrInfoError::SYSTEM:
|
||||
case Network::GetAddrInfoError::SYSTEM:
|
||||
return "System error returned in errno";
|
||||
case GetAddrInfoError::BADHINTS:
|
||||
case Network::GetAddrInfoError::BADHINTS:
|
||||
return "Invalid value for hints";
|
||||
case GetAddrInfoError::PROTOCOL:
|
||||
case Network::GetAddrInfoError::PROTOCOL:
|
||||
return "Resolved protocol is unknown";
|
||||
case GetAddrInfoError::OVERFLOW_:
|
||||
case Network::GetAddrInfoError::OVERFLOW_:
|
||||
return "Argument buffer overflow";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
Network::Domain Translate(Domain domain) {
|
||||
switch (domain) {
|
||||
case Domain::Unspecified:
|
||||
return Network::Domain::Unspecified;
|
||||
case Domain::INET:
|
||||
return Network::Domain::INET;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Domain Translate(Network::Domain domain) {
|
||||
switch (domain) {
|
||||
case Network::Domain::Unspecified:
|
||||
return Domain::Unspecified;
|
||||
case Network::Domain::INET:
|
||||
return Domain::INET;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Network::Type Translate(Type type) {
|
||||
switch (type) {
|
||||
case Type::Unspecified:
|
||||
return Network::Type::Unspecified;
|
||||
case Type::STREAM:
|
||||
return Network::Type::STREAM;
|
||||
case Type::DGRAM:
|
||||
return Network::Type::DGRAM;
|
||||
case Type::RAW:
|
||||
return Network::Type::RAW;
|
||||
case Type::SEQPACKET:
|
||||
return Network::Type::SEQPACKET;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||
return Network::Type{};
|
||||
}
|
||||
}
|
||||
|
||||
Type Translate(Network::Type type) {
|
||||
switch (type) {
|
||||
case Network::Type::Unspecified: return Type::Unspecified;
|
||||
case Network::Type::STREAM: return Type::STREAM;
|
||||
case Network::Type::DGRAM: return Type::DGRAM;
|
||||
case Network::Type::RAW: return Type::RAW;
|
||||
case Network::Type::SEQPACKET: return Type::SEQPACKET;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||
return Type{};
|
||||
}
|
||||
}
|
||||
|
||||
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
|
||||
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
|
||||
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
|
||||
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
|
||||
switch (protocol) {
|
||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
|
||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
|
||||
switch (protocol) {
|
||||
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
|
||||
NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||
|
||||
Network::PollEvents Translate(PollEvents flags) {
|
||||
Network::PollEvents result{};
|
||||
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
|
||||
if (True(flags & from)) {
|
||||
flags &= ~from;
|
||||
result |= to;
|
||||
}
|
||||
};
|
||||
translate(PollEvents::In, Network::PollEvents::In);
|
||||
translate(PollEvents::Pri, Network::PollEvents::Pri);
|
||||
translate(PollEvents::Out, Network::PollEvents::Out);
|
||||
translate(PollEvents::Err, Network::PollEvents::Err);
|
||||
translate(PollEvents::Hup, Network::PollEvents::Hup);
|
||||
translate(PollEvents::Nval, Network::PollEvents::Nval);
|
||||
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
|
||||
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
|
||||
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
|
||||
|
||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
PollEvents Translate(Network::PollEvents flags) {
|
||||
PollEvents result{};
|
||||
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
|
||||
if (True(flags & from)) {
|
||||
flags &= ~from;
|
||||
result |= to;
|
||||
}
|
||||
};
|
||||
|
||||
translate(Network::PollEvents::In, PollEvents::In);
|
||||
translate(Network::PollEvents::Pri, PollEvents::Pri);
|
||||
translate(Network::PollEvents::Out, PollEvents::Out);
|
||||
translate(Network::PollEvents::Err, PollEvents::Err);
|
||||
translate(Network::PollEvents::Hup, PollEvents::Hup);
|
||||
translate(Network::PollEvents::Nval, PollEvents::Nval);
|
||||
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
|
||||
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
|
||||
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
|
||||
|
||||
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
Network::SockAddrIn Translate(SockAddrIn value) {
|
||||
// All lengths are valid, from [0 upto 256]
|
||||
return {
|
||||
.family = Translate(Domain(value.family)),
|
||||
.ip = value.ip,
|
||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||
};
|
||||
}
|
||||
|
||||
SockAddrIn Translate(Network::SockAddrIn value) {
|
||||
return {
|
||||
.len = 16,
|
||||
.family = static_cast<u8>(Translate(value.family)),
|
||||
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||
.ip = value.ip,
|
||||
.zeroes = {},
|
||||
};
|
||||
}
|
||||
|
||||
Network::ShutdownHow Translate(ShutdownHow how) {
|
||||
switch (how) {
|
||||
case ShutdownHow::RD:
|
||||
return Network::ShutdownHow::RD;
|
||||
case ShutdownHow::WR:
|
||||
return Network::ShutdownHow::WR;
|
||||
case ShutdownHow::RDWR:
|
||||
return Network::ShutdownHow::RDWR;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,49 +14,7 @@
|
||||
|
||||
namespace Service::Sockets {
|
||||
|
||||
/// Translate abstract errno to guest errno
|
||||
Errno Translate(Network::Errno value);
|
||||
|
||||
/// Translate abstract return value errno pair to guest return value errno pair
|
||||
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
||||
|
||||
/// Translate abstract getaddrinfo error to guest getaddrinfo error
|
||||
GetAddrInfoError Translate(Network::GetAddrInfoError value);
|
||||
|
||||
/// Translate guest error to string
|
||||
const char* Translate(GetAddrInfoError value);
|
||||
|
||||
/// Translate guest domain to abstract domain
|
||||
Network::Domain Translate(Domain domain);
|
||||
|
||||
/// Translate abstract domain to guest domain
|
||||
Domain Translate(Network::Domain domain);
|
||||
|
||||
/// Translate guest type to abstract type
|
||||
Network::Type Translate(Type type);
|
||||
|
||||
/// Translate abstract type to guest type
|
||||
Type Translate(Network::Type type);
|
||||
|
||||
/// Translate guest protocol to abstract protocol
|
||||
Network::Protocol Translate(Protocol protocol);
|
||||
|
||||
/// Translate abstract protocol to guest protocol
|
||||
Protocol Translate(Network::Protocol protocol);
|
||||
|
||||
/// Translate guest poll event flags to abstract poll event flags
|
||||
Network::PollEvents Translate(PollEvents flags);
|
||||
|
||||
/// Translate abstract poll event flags to guest poll event flags
|
||||
PollEvents Translate(Network::PollEvents flags);
|
||||
|
||||
/// Translate guest socket address structure to abstract socket address structure
|
||||
Network::SockAddrIn Translate(SockAddrIn value);
|
||||
|
||||
/// Translate abstract socket address structure to guest socket address structure
|
||||
SockAddrIn Translate(Network::SockAddrIn value);
|
||||
|
||||
/// Translate guest shutdown mode to abstract shutdown mode
|
||||
Network::ShutdownHow Translate(ShutdownHow how);
|
||||
const char* Translate(Network::GetAddrInfoError value);
|
||||
|
||||
} // namespace Service::Sockets
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u");
|
||||
if (bsd) {
|
||||
auto err = bsd->CloseImpl(fd);
|
||||
if (err != Service::Sockets::Errno::SUCCESS) {
|
||||
if (err != Network::Errno::E_SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
|
||||
const bool non_block = mode == IoMode::NonBlocking;
|
||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||
if (error != Network::Errno::SUCCESS) {
|
||||
if (error != Network::Errno::E_SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||
}
|
||||
return ResultSuccess;
|
||||
|
||||
@@ -290,10 +290,10 @@ public:
|
||||
BIO_clear_retry_flags(bio);
|
||||
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
||||
switch (err) {
|
||||
case Network::Errno::SUCCESS:
|
||||
case Network::Errno::E_SUCCESS:
|
||||
*actual_p = actual;
|
||||
return 1;
|
||||
case Network::Errno::AGAIN:
|
||||
case Network::Errno::E_AGAIN:
|
||||
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
||||
return 0;
|
||||
default:
|
||||
@@ -309,13 +309,13 @@ public:
|
||||
BIO_clear_retry_flags(bio);
|
||||
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
||||
switch (err) {
|
||||
case Network::Errno::SUCCESS:
|
||||
case Network::Errno::E_SUCCESS:
|
||||
*actual_p = actual;
|
||||
if (actual == 0) {
|
||||
self->got_read_eof = true;
|
||||
}
|
||||
return actual ? 1 : 0;
|
||||
case Network::Errno::AGAIN:
|
||||
case Network::Errno::E_AGAIN:
|
||||
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
||||
return 0;
|
||||
default:
|
||||
|
||||
@@ -146,11 +146,11 @@ public:
|
||||
const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size);
|
||||
const auto [actual, err] = socket->Recv(0, read_span);
|
||||
switch (err) {
|
||||
case Network::Errno::SUCCESS:
|
||||
case Network::Errno::E_SUCCESS:
|
||||
ASSERT(static_cast<size_t>(actual) <= fill_size);
|
||||
ciphertext_read_buf.resize(offset + actual);
|
||||
return ResultSuccess;
|
||||
case Network::Errno::AGAIN:
|
||||
case Network::Errno::E_AGAIN:
|
||||
ciphertext_read_buf.resize(offset);
|
||||
return ResultWouldBlock;
|
||||
default:
|
||||
@@ -165,12 +165,12 @@ public:
|
||||
while (!ciphertext_write_buf.empty()) {
|
||||
const auto [actual, err] = socket->Send(ciphertext_write_buf, 0);
|
||||
switch (err) {
|
||||
case Network::Errno::SUCCESS:
|
||||
case Network::Errno::E_SUCCESS:
|
||||
ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size());
|
||||
ciphertext_write_buf.erase(ciphertext_write_buf.begin(),
|
||||
ciphertext_write_buf.begin() + actual);
|
||||
break;
|
||||
case Network::Errno::AGAIN:
|
||||
case Network::Errno::E_AGAIN:
|
||||
return ResultWouldBlock;
|
||||
default:
|
||||
LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err);
|
||||
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset,
|
||||
actual, cur.size(), static_cast<s32>(err));
|
||||
switch (err) {
|
||||
case Network::Errno::SUCCESS:
|
||||
case Network::Errno::E_SUCCESS:
|
||||
offset += actual;
|
||||
if (actual == 0) {
|
||||
ASSERT(is_read);
|
||||
@@ -203,7 +203,7 @@ public:
|
||||
return errSecEndOfData;
|
||||
}
|
||||
break;
|
||||
case Network::Errno::AGAIN:
|
||||
case Network::Errno::E_AGAIN:
|
||||
*dataLength = offset;
|
||||
return errSSLWouldBlock;
|
||||
default:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <common/socket_types.h>
|
||||
#include <mutex>
|
||||
#include "core/internal_network/socket_types.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@@ -31,68 +31,10 @@ namespace Network {
|
||||
class SocketBase;
|
||||
class Socket;
|
||||
|
||||
/// Error code for network functions
|
||||
enum class Errno {
|
||||
SUCCESS,
|
||||
BADF,
|
||||
INVAL,
|
||||
MFILE,
|
||||
PIPE,
|
||||
NOTCONN,
|
||||
AGAIN,
|
||||
CONNREFUSED,
|
||||
CONNRESET,
|
||||
CONNABORTED,
|
||||
HOSTUNREACH,
|
||||
NETDOWN,
|
||||
NETUNREACH,
|
||||
TIMEDOUT,
|
||||
MSGSIZE,
|
||||
INPROGRESS,
|
||||
ISCONN,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
enum class GetAddrInfoError {
|
||||
SUCCESS,
|
||||
ADDRFAMILY,
|
||||
AGAIN,
|
||||
BADFLAGS,
|
||||
FAIL,
|
||||
FAMILY,
|
||||
MEMORY,
|
||||
NODATA,
|
||||
NONAME,
|
||||
SERVICE,
|
||||
SOCKTYPE,
|
||||
SYSTEM,
|
||||
BADHINTS,
|
||||
PROTOCOL,
|
||||
OVERFLOW_,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
/// Cross-platform poll fd structure
|
||||
|
||||
enum class PollEvents : u16 {
|
||||
// Using Pascal case because IN is a macro on Windows.
|
||||
In = 1 << 0,
|
||||
Pri = 1 << 1,
|
||||
Out = 1 << 2,
|
||||
Err = 1 << 3,
|
||||
Hup = 1 << 4,
|
||||
Nval = 1 << 5,
|
||||
RdNorm = 1 << 6,
|
||||
RdBand = 1 << 7,
|
||||
WrBand = 1 << 8,
|
||||
};
|
||||
|
||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||
|
||||
struct PollFD {
|
||||
SocketBase* socket;
|
||||
PollEvents events;
|
||||
PollEvents revents;
|
||||
struct HostPollFD {
|
||||
SocketBase* socket = nullptr;
|
||||
Network::PollEvents events = {};
|
||||
Network::PollEvents revents = {};
|
||||
};
|
||||
|
||||
class NetworkInstance {
|
||||
@@ -101,6 +43,10 @@ public:
|
||||
~NetworkInstance();
|
||||
};
|
||||
|
||||
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
|
||||
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
|
||||
s32 TranslateMsgOptToNative(s32 flags);
|
||||
|
||||
void CancelPendingSocketOperations();
|
||||
void RestartSocketOperations();
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Network {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
|
||||
ULONG buf_size = 0;
|
||||
if (GetAdaptersAddresses(
|
||||
@@ -66,7 +66,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Network::NetworkInterface> result;
|
||||
std::vector<NetworkInterface> result;
|
||||
|
||||
for (auto* a = addrs; a; a = a->Next) {
|
||||
|
||||
@@ -89,7 +89,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
||||
->sin_addr;
|
||||
|
||||
result.emplace_back(Network::NetworkInterface{
|
||||
result.emplace_back(NetworkInterface{
|
||||
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
||||
.ip_address = ip,
|
||||
.subnet_mask = mask,
|
||||
@@ -103,7 +103,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
|
||||
#else
|
||||
|
||||
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
#if defined(__ANDROID__) || defined(__linux__)
|
||||
struct ifaddrs* ifaddr = nullptr;
|
||||
if (getifaddrs(&ifaddr) != 0) {
|
||||
@@ -135,7 +135,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
||||
}
|
||||
#endif
|
||||
std::vector<Network::NetworkInterface> ifaces;
|
||||
std::vector<NetworkInterface> ifaces;
|
||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
||||
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
||||
@@ -149,7 +149,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
});
|
||||
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
||||
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
||||
ifaces.emplace_back(Network::NetworkInterface{
|
||||
ifaces.emplace_back(NetworkInterface{
|
||||
.name = ifa->ifa_name,
|
||||
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
||||
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
||||
@@ -159,7 +159,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
freeifaddrs(ifaddr);
|
||||
return ifaces;
|
||||
#elif defined(__FreeBSD__)
|
||||
std::vector<Network::NetworkInterface> ifaces;
|
||||
std::vector<NetworkInterface> ifaces;
|
||||
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||
if (fd < 0) {
|
||||
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
||||
@@ -191,7 +191,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
||||
char const* p = (char const*)(ifm + 1);
|
||||
|
||||
Network::NetworkInterface iface{};
|
||||
NetworkInterface iface{};
|
||||
for (size_t i = 0; i < RTAX_MAX; i++)
|
||||
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
||||
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
||||
@@ -220,7 +220,7 @@ std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
|
||||
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
||||
auto const& sel_if = Settings::values.network_interface.GetValue();
|
||||
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
||||
if (sel_if.empty())
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#ifdef __unix__
|
||||
#include <spawn.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/internal_network/socket_icmp.h"
|
||||
|
||||
extern "C" {
|
||||
extern char **environ;
|
||||
}
|
||||
|
||||
namespace Network {
|
||||
|
||||
namespace {
|
||||
|
||||
u16 ComputeChecksum(std::span<const u8> data) {
|
||||
u32 sum = 0;
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
|
||||
sum += value;
|
||||
}
|
||||
if (data.size() % 2 != 0){
|
||||
sum += u16(data[data.size() - 1]) << 8;
|
||||
}
|
||||
while ((sum >> 16) != 0)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
return (~sum) & 0xffff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IcmpSocket::~IcmpSocket() {
|
||||
if (fd == INVALID_SOCKET) {
|
||||
return;
|
||||
}
|
||||
fd = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
|
||||
if (optname == Network::OptName::RCVTIMEO) {
|
||||
if (optval.size() < sizeof(Network::Timeval))
|
||||
return Errno::E_INVAL;
|
||||
std::memcpy(&rcv_timeo, optval.data(), sizeof(rcv_timeo));
|
||||
}
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
connected_addr = addr_in;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Listen(s32 backlog) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Shutdown(ShutdownHow how) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
|
||||
LOG_DEBUG(Network, "(stubbed) called");
|
||||
return connected_addr.has_value()
|
||||
? RecvFrom(flags, message, nullptr)
|
||||
: std::make_pair(s32(0), Errno::E_NOTCONN);
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||
LOG_DEBUG(Network, "(stubbed) called");
|
||||
ASSERT(flags == 0);
|
||||
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||
|
||||
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
|
||||
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||
const auto timestamp = std::chrono::steady_clock::now();
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard guard(pings_mutex);
|
||||
// find ping process that is finished running
|
||||
for (auto it = pings.begin(); it != pings.end();) {
|
||||
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
|
||||
// ping process is still running, go to next
|
||||
if (result != it->ping_pid) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// ping process is finished, remove and handle it
|
||||
it = pings.erase(it);
|
||||
if (it->ping_status == 0) {
|
||||
if (addr) {
|
||||
addr->family = it->family;
|
||||
addr->ip = it->ip;
|
||||
addr->portno = it->portno;
|
||||
addr->len = 16;
|
||||
addr->zeroes = {};
|
||||
}
|
||||
std::array<u8, 8> data{
|
||||
0,
|
||||
0,
|
||||
0, //checksum
|
||||
0,
|
||||
it->seq_ident[0],
|
||||
it->seq_ident[1],
|
||||
it->seq_ident[2],
|
||||
it->seq_ident[3]
|
||||
};
|
||||
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
|
||||
data[2] = u8(csum >> 8); //hi
|
||||
data[3] = u8(csum); //lo
|
||||
auto const n = std::min(data.size(), message.size());
|
||||
std::copy(data.begin(), data.begin() + n, message.begin());
|
||||
return {s32(n), Errno::E_SUCCESS};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!blocking)
|
||||
return {-1, Errno::E_AGAIN};
|
||||
|
||||
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||
if (time_diff_ms > rcv_timeout_ms)
|
||||
return {-1, Errno::E_TIMEDOUT};
|
||||
|
||||
std::this_thread::yield();
|
||||
}
|
||||
#endif
|
||||
return {-1, Errno::E_INVAL};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
|
||||
LOG_DEBUG(Network, "(stubbed) called");
|
||||
if (connected_addr.has_value())
|
||||
return SendTo(flags, message, std::addressof(connected_addr.value()));
|
||||
return {s32(0), Errno::E_NOTCONN};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||
LOG_DEBUG(Network, "(stubbed) called");
|
||||
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||
// 0 -> 8 (IPv4), 128 (IPv6)
|
||||
// 1 -> 0
|
||||
// 2..4 -> checksum
|
||||
// 4..6 -> ident
|
||||
// 6..8 -> seq
|
||||
|
||||
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
|
||||
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
|
||||
#if !(defined(__OPENORBIS__) || defined(__ANDROID__)) && (defined(__FreeBSD__) || defined(__linux__))
|
||||
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
||||
if (!addr)
|
||||
return {-1, Errno::E_DESTADDRREQ};
|
||||
|
||||
if (message.size() >= 8) {
|
||||
std::string ip_str = fmt::format(
|
||||
"{}.{}.{}.{}",
|
||||
addr->ip[0],
|
||||
addr->ip[1],
|
||||
addr->ip[2],
|
||||
addr->ip[3]
|
||||
);
|
||||
#ifdef __FreeBSD__
|
||||
// ping -W option is a nonfractional int (milliseconds)
|
||||
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
|
||||
std::vector<char*> argv = {
|
||||
const_cast<char*>("ping"),
|
||||
const_cast<char*>("-c"),
|
||||
const_cast<char*>("1"),
|
||||
const_cast<char*>("-W"),
|
||||
timeout_str.data(),
|
||||
ip_str.data(),
|
||||
nullptr
|
||||
};
|
||||
#elif defined(__linux__)
|
||||
// ping -W option is a fractional float (seconds)
|
||||
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
|
||||
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
|
||||
std::vector<char*> argv = {
|
||||
const_cast<char*>("ping"),
|
||||
const_cast<char*>("-c"),
|
||||
const_cast<char*>("1"),
|
||||
const_cast<char*>("-W"),
|
||||
timeout_str.data(),
|
||||
ip_str.data(),
|
||||
nullptr
|
||||
};
|
||||
#endif
|
||||
pid_t ping_pid;
|
||||
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
|
||||
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
|
||||
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
|
||||
return {-1, Errno::E_INVAL};
|
||||
}
|
||||
std::lock_guard guard(pings_mutex);
|
||||
if (pings.size() >= pings.max_size())
|
||||
pings.erase(pings.begin());
|
||||
pings.push_back(PingProcessData{
|
||||
.ip = addr->ip,
|
||||
.portno = addr->portno,
|
||||
.ping_pid = ping_pid,
|
||||
.ping_status = 0,
|
||||
.seq_ident = {
|
||||
message[4],
|
||||
message[5],
|
||||
message[6],
|
||||
message[7]
|
||||
},
|
||||
.family = addr->family,
|
||||
});
|
||||
return {s32(message.size()), Errno::E_SUCCESS};
|
||||
}
|
||||
#endif
|
||||
return {-1, Errno::E_INVAL};
|
||||
}
|
||||
|
||||
Errno IcmpSocket::Close() {
|
||||
LOG_DEBUG(Network, "called");
|
||||
fd = INVALID_SOCKET;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
|
||||
LOG_DEBUG(Network, "called");
|
||||
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
bool IcmpSocket::IsOpened() const {
|
||||
return fd != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
}
|
||||
Errno IcmpSocket::SetNonBlock(bool enable) {
|
||||
blocking = !enable;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <sys/types.h>
|
||||
#include <mutex>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "core/internal_network/sockets.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
struct PingProcessData {
|
||||
IPv4Address ip;
|
||||
u16 portno;
|
||||
pid_t ping_pid;
|
||||
pid_t ping_status;
|
||||
std::array<u8, 4> seq_ident;
|
||||
u8 family;
|
||||
};
|
||||
|
||||
class IcmpSocket : public Network::SocketBase {
|
||||
public:
|
||||
explicit IcmpSocket() noexcept = default;
|
||||
~IcmpSocket() override;
|
||||
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
||||
Errno Close() override;
|
||||
std::pair<AcceptResult, Errno> Accept() override;
|
||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||
Errno Bind(Network::SockAddrIn addr) override;
|
||||
Errno Listen(s32 backlog) override;
|
||||
Errno Shutdown(ShutdownHow how) override;
|
||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||
std::pair<Errno, Errno> GetPendingError() override;
|
||||
bool IsOpened() const override;
|
||||
void HandleProxyPacket(const ProxyPacket& packet) override;
|
||||
Errno SetNonBlock(bool enable) override;
|
||||
|
||||
boost::container::static_vector<PingProcessData, 128> pings;
|
||||
std::optional<SockAddrIn> connected_addr;
|
||||
std::mutex pings_mutex;
|
||||
Network::Timeval rcv_timeo;
|
||||
bool blocking = true;
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
@@ -47,71 +47,82 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||
received_packets.push(decompressed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
|
||||
LOG_DEBUG(Network, "(STUBBED) called");
|
||||
return Errno::SUCCESS;
|
||||
Errno ProxySocket::SetNonBlock(bool enable) {
|
||||
blocking = !enable;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
||||
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
|
||||
// numeric values?
|
||||
if (optval.size() >= sizeof(u32)) {
|
||||
u32 value;
|
||||
std::memcpy(&value, optval.data(), sizeof(value));
|
||||
if (optname == Network::OptName::BROADCAST)
|
||||
broadcast = bool(value);
|
||||
if (optname == Network::OptName::SNDTIMEO)
|
||||
send_timeout = value;
|
||||
if (optname == Network::OptName::RCVTIMEO)
|
||||
receive_timeout = value;
|
||||
}
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||
protocol = socket_protocol;
|
||||
SetSockOpt(fd, SO_TYPE, type);
|
||||
|
||||
return Errno::SUCCESS;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return {AcceptResult{}, Errno::SUCCESS};
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {AcceptResult{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno ProxySocket::Connect(SockAddrIn addr_in) {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return Errno::SUCCESS;
|
||||
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return {SockAddrIn{}, Errno::SUCCESS};
|
||||
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno ProxySocket::Bind(SockAddrIn addr) {
|
||||
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
|
||||
if (is_bound) {
|
||||
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
||||
return Errno::SUCCESS;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
local_endpoint = addr;
|
||||
is_bound = true;
|
||||
|
||||
return Errno::SUCCESS;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::Listen(s32 backlog) {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return Errno::SUCCESS;
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
return Errno::SUCCESS;
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
ASSERT(flags == 0);
|
||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||
|
||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
||||
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||
return {s32(0), Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
|
||||
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
||||
ASSERT(flags == 0);
|
||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
||||
|
||||
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
||||
const auto timestamp = std::chrono::steady_clock::now();
|
||||
@@ -128,88 +139,85 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
|
||||
}
|
||||
|
||||
if (!blocking) {
|
||||
return {-1, Errno::AGAIN};
|
||||
return {-1, Errno::E_AGAIN};
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
|
||||
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||
const auto time_diff_ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||
|
||||
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||
if (time_diff_ms > timeout) {
|
||||
return {-1, Errno::TIMEDOUT};
|
||||
return {-1, Errno::E_TIMEDOUT};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
||||
std::size_t max_length) {
|
||||
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
|
||||
LOG_DEBUG(Network, "called");
|
||||
ProxyPacket& packet = received_packets.front();
|
||||
if (addr) {
|
||||
addr->family = Domain::INET;
|
||||
addr->len = 16;
|
||||
addr->family = u8(Network::Domain::INET);
|
||||
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
||||
addr->portno = packet.local_endpoint.portno; // The senders port number
|
||||
addr->zeroes = {};
|
||||
}
|
||||
|
||||
bool peek = (flags & FLAG_MSG_PEEK) != 0;
|
||||
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
|
||||
std::size_t read_bytes;
|
||||
if (packet.data.size() > max_length) {
|
||||
read_bytes = max_length;
|
||||
memcpy(message.data(), packet.data.data(), max_length);
|
||||
std::memcpy(message.data(), packet.data.data(), max_length);
|
||||
|
||||
if (protocol == Protocol::UDP) {
|
||||
if (!peek) {
|
||||
received_packets.pop();
|
||||
}
|
||||
return {-1, Errno::MSGSIZE};
|
||||
return {-1, Errno::E_MSGSIZE};
|
||||
} else if (protocol == Protocol::TCP) {
|
||||
std::vector<u8> numArray(packet.data.size() - max_length);
|
||||
std::copy(packet.data.begin() + max_length, packet.data.end(),
|
||||
std::back_inserter(numArray));
|
||||
std::copy(packet.data.begin() + max_length, packet.data.end(), std::back_inserter(numArray));
|
||||
packet.data = numArray;
|
||||
}
|
||||
} else {
|
||||
read_bytes = packet.data.size();
|
||||
memcpy(message.data(), packet.data.data(), read_bytes);
|
||||
std::memcpy(message.data(), packet.data.data(), read_bytes);
|
||||
if (!peek) {
|
||||
received_packets.pop();
|
||||
}
|
||||
}
|
||||
|
||||
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
|
||||
return {u32(read_bytes), Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
||||
LOG_WARNING(Network, "(STUBBED) called");
|
||||
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||
LOG_WARNING(Network, "(stubbed) called");
|
||||
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
||||
ASSERT(flags == 0);
|
||||
|
||||
return {static_cast<s32>(0), Errno::SUCCESS};
|
||||
return {s32(0), Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
if (room_member->IsConnected()) {
|
||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
||||
packet.data.size());
|
||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
|
||||
room_member->SendProxyPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
|
||||
const SockAddrIn* addr) {
|
||||
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
||||
LOG_DEBUG(Network, "called");
|
||||
ASSERT(flags == 0);
|
||||
|
||||
if (!is_bound) {
|
||||
LOG_ERROR(Network, "ProxySocket is not bound!");
|
||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||
return {s32(message.size()), Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||
if (!room_member->IsConnected()) {
|
||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||
return {s32(message.size()), Errno::E_SUCCESS};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,66 +242,20 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
|
||||
|
||||
SendPacket(packet);
|
||||
|
||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||
return {s32(message.size()), Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
Errno ProxySocket::Close() {
|
||||
LOG_DEBUG(Network, "called");
|
||||
fd = INVALID_SOCKET;
|
||||
closed = true;
|
||||
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
|
||||
struct Linger {
|
||||
u16 linger_enable;
|
||||
u16 linger_time;
|
||||
} values;
|
||||
values.linger_enable = enable ? 1 : 0;
|
||||
values.linger_time = static_cast<u16>(linger);
|
||||
|
||||
return SetSockOpt(fd, SO_LINGER, values);
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetReuseAddr(bool enable) {
|
||||
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetBroadcast(bool enable) {
|
||||
broadcast = enable;
|
||||
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetSndBuf(u32 value) {
|
||||
return SetSockOpt(fd, SO_SNDBUF, value);
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetKeepAlive(bool enable) {
|
||||
return Errno::SUCCESS;
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetRcvBuf(u32 value) {
|
||||
return SetSockOpt(fd, SO_RCVBUF, value);
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetSndTimeo(u32 value) {
|
||||
send_timeout = value;
|
||||
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetRcvTimeo(u32 value) {
|
||||
receive_timeout = value;
|
||||
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
|
||||
}
|
||||
|
||||
Errno ProxySocket::SetNonBlock(bool enable) {
|
||||
blocking = !enable;
|
||||
return Errno::SUCCESS;
|
||||
return Errno::E_SUCCESS;
|
||||
}
|
||||
|
||||
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
||||
LOG_DEBUG(Network, "(STUBBED) called");
|
||||
return {Errno::SUCCESS, Errno::SUCCESS};
|
||||
LOG_DEBUG(Network, "called");
|
||||
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
||||
}
|
||||
|
||||
bool ProxySocket::IsOpened() const {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
@@ -29,13 +29,13 @@ public:
|
||||
|
||||
std::pair<AcceptResult, Errno> Accept() override;
|
||||
|
||||
Errno Connect(SockAddrIn addr_in) override;
|
||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||
|
||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||
|
||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||
|
||||
Errno Bind(SockAddrIn addr) override;
|
||||
Errno Bind(Network::SockAddrIn addr) override;
|
||||
|
||||
Errno Listen(s32 backlog) override;
|
||||
|
||||
@@ -43,9 +43,9 @@ public:
|
||||
|
||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||
|
||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||
|
||||
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
||||
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr,
|
||||
std::size_t max_length);
|
||||
|
||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||
@@ -53,28 +53,11 @@ public:
|
||||
void SendPacket(ProxyPacket& packet);
|
||||
|
||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||
const SockAddrIn* addr) override;
|
||||
|
||||
Errno SetLinger(bool enable, u32 linger) override;
|
||||
|
||||
Errno SetReuseAddr(bool enable) override;
|
||||
|
||||
Errno SetBroadcast(bool enable) override;
|
||||
|
||||
Errno SetKeepAlive(bool enable) override;
|
||||
|
||||
Errno SetSndBuf(u32 value) override;
|
||||
|
||||
Errno SetRcvBuf(u32 value) override;
|
||||
|
||||
Errno SetSndTimeo(u32 value) override;
|
||||
|
||||
Errno SetRcvTimeo(u32 value) override;
|
||||
const Network::SockAddrIn* addr) override;
|
||||
|
||||
Errno SetNonBlock(bool enable) override;
|
||||
|
||||
template <typename T>
|
||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||
|
||||
std::pair<Errno, Errno> GetPendingError() override;
|
||||
|
||||
@@ -86,7 +69,7 @@ private:
|
||||
u32 send_timeout = 0;
|
||||
u32 receive_timeout = 0;
|
||||
bool is_bound = false;
|
||||
SockAddrIn local_endpoint{};
|
||||
Network::SockAddrIn local_endpoint{};
|
||||
bool blocking = true;
|
||||
std::queue<ProxyPacket> received_packets;
|
||||
Protocol protocol;
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
// Most of these structures are direct mappings of guest's
|
||||
// expectations for these values, in other words, they're the
|
||||
// values that HOS is expected to use AND handle.
|
||||
|
||||
namespace Network {
|
||||
|
||||
enum class Errno : u32 {
|
||||
E_SUCCESS = 0,
|
||||
E_PERM = 1,
|
||||
E_NOENT = 2,
|
||||
E_SRCH = 3,
|
||||
E_INTR = 4,
|
||||
E_IO = 5,
|
||||
E_NXIO = 6,
|
||||
E_2BIG = 7,
|
||||
E_NOEXEC = 8,
|
||||
E_BADF = 9,
|
||||
E_CHILD = 10,
|
||||
E_AGAIN = 11,
|
||||
E_NOMEM = 12,
|
||||
E_ACCES = 13,
|
||||
E_FAULT = 14,
|
||||
E_NOTBLK = 15,
|
||||
E_BUSY = 16,
|
||||
E_EXIST = 17,
|
||||
E_XDEV = 18,
|
||||
E_NODEV = 19,
|
||||
E_NOTDIR = 20,
|
||||
E_ISDIR = 21,
|
||||
E_INVAL = 22,
|
||||
E_NFILE = 23,
|
||||
E_MFILE = 24,
|
||||
E_NOTTY = 25,
|
||||
E_TXTBSY = 26,
|
||||
E_FBIG = 27,
|
||||
E_NOSPC = 28,
|
||||
E_SPIPE = 29,
|
||||
E_ROFS = 30,
|
||||
E_MLINK = 31,
|
||||
E_PIPE = 32,
|
||||
E_DOM = 33,
|
||||
E_RANGE = 34,
|
||||
E_DEADLK = 35,
|
||||
E_NAMETOOLONG = 36,
|
||||
E_NOLCK = 37,
|
||||
E_NOSYS = 38,
|
||||
E_NOTEMPTY = 39,
|
||||
E_LOOP = 40,
|
||||
E_NOMSG = 42,
|
||||
E_IDRM = 43,
|
||||
E_CHRNG = 44,
|
||||
E_L2NSYNC = 45,
|
||||
E_L3HLT = 46,
|
||||
E_L3RST = 47,
|
||||
E_LNRNG = 48,
|
||||
E_UNATCH = 49,
|
||||
E_NOCSI = 50,
|
||||
E_L2HLT = 51,
|
||||
E_BADE = 52,
|
||||
E_BADR = 53,
|
||||
E_XFULL = 54,
|
||||
E_NOANO = 55,
|
||||
E_BADRQC = 56,
|
||||
E_BADSSL = 57,
|
||||
E_BFONT = 59,
|
||||
E_NOSTR = 60,
|
||||
E_NODATA = 61,
|
||||
E_TIME = 62,
|
||||
E_NOSR = 63,
|
||||
E_NONET = 64,
|
||||
E_NOPKG = 65,
|
||||
E_REMOTE = 66,
|
||||
E_NOLINK = 67,
|
||||
E_ADV = 68,
|
||||
E_SRMNT = 69,
|
||||
E_COMM = 70,
|
||||
E_PROTO = 71,
|
||||
E_MULTIHOP = 72,
|
||||
E_DOTDOT = 73,
|
||||
E_BADMSG = 74,
|
||||
E_OVERFLOW = 75,
|
||||
E_NOTUNUQ = 76,
|
||||
E_BADFD = 77,
|
||||
E_REMCHG = 78,
|
||||
E_LIBACC = 79,
|
||||
E_LIBBAD = 80,
|
||||
E_LIBSCN = 81,
|
||||
E_LIBMAX = 82,
|
||||
E_LIBEXEC = 83,
|
||||
E_ILSEQ = 84,
|
||||
E_RESTART = 85,
|
||||
E_STRPIPE = 86,
|
||||
E_USERS = 87,
|
||||
E_NOTSOCK = 88,
|
||||
E_DESTADDRREQ = 89,
|
||||
E_MSGSIZE = 90,
|
||||
E_PROTOTYPE = 91,
|
||||
E_NOPROTOOPT = 92,
|
||||
E_PROTONOSUPPORT = 93,
|
||||
E_SOCKTNOSUPPORT = 94,
|
||||
E_OPNOTSUPP = 95,
|
||||
E_PFNOSUPPORT = 96,
|
||||
E_AFNOSUPPORT = 97,
|
||||
E_ADDRINUSE = 98,
|
||||
E_ADDRNOTAVAIL = 99,
|
||||
E_NETDOWN = 100,
|
||||
E_NETUNREACH = 101,
|
||||
E_NETRESET = 102,
|
||||
E_CONNABORTED = 103,
|
||||
E_CONNRESET = 104,
|
||||
E_NOBUFS = 105,
|
||||
E_ISCONN = 106,
|
||||
E_NOTCONN = 107,
|
||||
E_SHUTDOWN = 108,
|
||||
E_TOOMANYREFS = 109,
|
||||
E_TIMEDOUT = 110,
|
||||
E_CONNREFUSED = 111,
|
||||
E_HOSTDOWN = 112,
|
||||
E_HOSTUNREACH = 113,
|
||||
E_ALREADY = 114,
|
||||
E_INPROGRESS = 115,
|
||||
E_STALE = 116,
|
||||
E_UCLEAN = 117,
|
||||
E_NOTNAM = 118,
|
||||
E_NAVAIL = 119,
|
||||
E_ISNAM = 120,
|
||||
E_REMOTEIO = 121,
|
||||
E_DQUOT = 122,
|
||||
E_NOMEDIUM = 123,
|
||||
E_MEDIUMTYPE = 124,
|
||||
E_CANCELED = 125,
|
||||
E_NOKEY = 126,
|
||||
E_KEYEXPIRED = 127,
|
||||
E_KEYREVOKED = 128,
|
||||
E_KEYREJECTED = 129,
|
||||
E_OWNERDEAD = 130,
|
||||
E_NOTRECOVERABLE = 131,
|
||||
E_RFKILL = 132,
|
||||
E_HWPOISON = 133,
|
||||
E_PROCLIM = 156,
|
||||
};
|
||||
|
||||
enum class GetAddrInfoError : s32 {
|
||||
SUCCESS = 0,
|
||||
ADDRFAMILY = 1,
|
||||
AGAIN = 2,
|
||||
BADFLAGS = 3,
|
||||
FAIL = 4,
|
||||
FAMILY = 5,
|
||||
MEMORY = 6,
|
||||
NODATA = 7,
|
||||
NONAME = 8,
|
||||
SERVICE = 9,
|
||||
SOCKTYPE = 10,
|
||||
SYSTEM = 11,
|
||||
BADHINTS = 12,
|
||||
PROTOCOL = 13,
|
||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
||||
OTHER = 15,
|
||||
};
|
||||
|
||||
enum class Domain : u32 {
|
||||
Unspecified = 0,
|
||||
UNIX = 1,
|
||||
INET = 2,
|
||||
IMPLINK = 3,
|
||||
PUP = 4,
|
||||
CHAOS = 5,
|
||||
NETBIOS = 6,
|
||||
ISO = 7,
|
||||
ECMA = 8,
|
||||
DATAKIT = 9,
|
||||
CCITT = 10,
|
||||
SNA = 11,
|
||||
DECnet = 12,
|
||||
DLI = 13,
|
||||
LAT = 14,
|
||||
HYLINK = 15,
|
||||
APPLETALK = 16,
|
||||
ROUTE = 17,
|
||||
LINK = 18,
|
||||
COIP = 20,
|
||||
CNT = 21,
|
||||
IPX = 23,
|
||||
SIP = 24,
|
||||
ISDN = 26,
|
||||
INET6 = 28,
|
||||
NATM = 29,
|
||||
ATM = 30,
|
||||
NETGRAPH = 32,
|
||||
SLOW = 33,
|
||||
SCLUSTER = 34,
|
||||
ARP = 35,
|
||||
BLUETOOTH = 36,
|
||||
IEEE80211 = 37,
|
||||
NETLINK = 38,
|
||||
INET_SDP = 40,
|
||||
INET6_SDP = 42,
|
||||
};
|
||||
|
||||
enum class Type : u32 {
|
||||
Unspecified = 0,
|
||||
STREAM = 1,
|
||||
DGRAM = 2,
|
||||
RAW = 3,
|
||||
RDM = 4,
|
||||
SEQPACKET = 5,
|
||||
};
|
||||
|
||||
enum class Protocol : u32 {
|
||||
IP = 0,
|
||||
ICMP = 1,
|
||||
TCP = 6,
|
||||
UDP = 17,
|
||||
//
|
||||
IPV6 = 41,
|
||||
RAW = 255,
|
||||
//
|
||||
HOPOPTS = 0,
|
||||
IGMP = 2,
|
||||
GGP = 3,
|
||||
IPV4 = 4,
|
||||
ST = 7,
|
||||
EGP = 8,
|
||||
PIGP = 9,
|
||||
RCCMON = 10,
|
||||
NVPII = 11,
|
||||
PUP = 12,
|
||||
ARGUS = 13,
|
||||
EMCON = 14,
|
||||
XNET = 15,
|
||||
CHAOS = 16,
|
||||
MUX = 18,
|
||||
MEAS = 19,
|
||||
HMP = 20,
|
||||
PRM = 21,
|
||||
IDP = 22,
|
||||
TRUNK1 = 23,
|
||||
TRUNK2 = 24,
|
||||
LEAF1 = 25,
|
||||
LEAF2 = 26,
|
||||
RDP = 27,
|
||||
IRTP = 28,
|
||||
TP = 29,
|
||||
BLT = 30,
|
||||
NSP = 31,
|
||||
INP = 32,
|
||||
DCCP = 33,
|
||||
//3PC = 34,
|
||||
IDPR = 35,
|
||||
XTP = 36,
|
||||
DDP = 37,
|
||||
CMTP = 38,
|
||||
TPXX = 39,
|
||||
IL = 40,
|
||||
SDRP = 42,
|
||||
ROUTING = 43,
|
||||
FRAGMENT = 44,
|
||||
IDRP = 45,
|
||||
RSVP = 46,
|
||||
GRE = 47,
|
||||
MHRP = 48,
|
||||
BHA = 49,
|
||||
ESP = 50,
|
||||
AH = 51,
|
||||
INLSP = 52,
|
||||
SWIPE = 53,
|
||||
NHRP = 54,
|
||||
MOBILE = 55,
|
||||
TLSP = 56,
|
||||
SKIP = 57,
|
||||
ICMPV6 = 58,
|
||||
NONE = 59,
|
||||
DSTOPTS = 60,
|
||||
AHIP = 61,
|
||||
CFTP = 62,
|
||||
HELLO = 63,
|
||||
SATEXPAK = 64,
|
||||
KRYPTOLAN = 65,
|
||||
RVD = 66,
|
||||
IPPC = 67,
|
||||
ADFS = 68,
|
||||
SATMON = 69,
|
||||
VISA = 70,
|
||||
IPCV = 71,
|
||||
CPNX = 72,
|
||||
CPHB = 73,
|
||||
WSN = 74,
|
||||
PVP = 75,
|
||||
BRSATMON = 76,
|
||||
ND = 77,
|
||||
WBMON = 78,
|
||||
WBEXPAK = 79,
|
||||
EON = 80,
|
||||
VMTP = 81,
|
||||
SVMTP = 82,
|
||||
VINES = 83,
|
||||
TTP = 84,
|
||||
IGP = 85,
|
||||
DGP = 86,
|
||||
TCF = 87,
|
||||
IGRP = 88,
|
||||
OSPFIGP = 89,
|
||||
SRPC = 90,
|
||||
LARP = 91,
|
||||
MTP = 92,
|
||||
AX25 = 93,
|
||||
IPEIP = 94,
|
||||
MICP = 95,
|
||||
SCCSP = 96,
|
||||
ETHERIP = 97,
|
||||
ENCAP = 98,
|
||||
APES = 99,
|
||||
GMTP = 100,
|
||||
IPCOMP = 108,
|
||||
SCTP = 132,
|
||||
MH = 135,
|
||||
UDPLITE = 136,
|
||||
HIP = 139,
|
||||
SHIM6 = 140,
|
||||
PIM = 103,
|
||||
CARP = 112,
|
||||
PGM = 113,
|
||||
MPLS = 137,
|
||||
PFSYNC = 240,
|
||||
};
|
||||
|
||||
enum class SocketLevel : u32 {
|
||||
IP = 0,
|
||||
ICMP = 1,
|
||||
TCP = 6,
|
||||
UDP = 17,
|
||||
CONFIG = 0xfffe,
|
||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
||||
};
|
||||
|
||||
enum class MsgOpt : u32 {
|
||||
OOB = 0x00001,
|
||||
PEEK = 0x00002,
|
||||
DONTROUTE = 0x00004,
|
||||
EOR_ = 0x00008,
|
||||
TRUNC = 0x00010,
|
||||
CTRUNC = 0x00020,
|
||||
WAITALL = 0x00040,
|
||||
DONTWAIT = 0x00080,
|
||||
EOF_ = 0x00100,
|
||||
NOSIGNAL = 0x20000,
|
||||
};
|
||||
|
||||
enum class OptName : u32 {
|
||||
DEBUG = 0x0001,
|
||||
ACCEPTCONN = 0x0002,
|
||||
REUSEADDR = 0x0004,
|
||||
KEEPALIVE = 0x0008,
|
||||
DONTROUTE = 0x0010,
|
||||
BROADCAST = 0x0020,
|
||||
USELOOPBACK = 0x0040,
|
||||
LINGER = 0x0080,
|
||||
OOBINLINE = 0x0100,
|
||||
REUSEPORT = 0x0200,
|
||||
TIMESTAMP = 0x0400,
|
||||
NOSIGPIPE = 0x0800, // at least according to libnx
|
||||
ACCEPTFILER = 0x1000,
|
||||
SNDBUF = 0x1001,
|
||||
RCVBUF = 0x1002,
|
||||
SNDTIMEO = 0x1005,
|
||||
RCVTIMEO = 0x1006,
|
||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
||||
ACCEPTFILTER = 0x1000,
|
||||
BINTIME = 0x2000,
|
||||
NO_OFFLOAD = 0x4000,
|
||||
NO_DDP = 0x8000,
|
||||
};
|
||||
enum class TcpOptName : u32 {
|
||||
NODELAY = 0x0001,
|
||||
MAXSEG = 0x0002,
|
||||
NOPUSH = 0x0004,
|
||||
NOOPT = 0x0008,
|
||||
MS5SIG = 0x0010,
|
||||
INFO = 0x0020
|
||||
};
|
||||
|
||||
enum class ShutdownHow : s32 {
|
||||
RD = 0,
|
||||
WR = 1,
|
||||
RDWR = 2,
|
||||
};
|
||||
|
||||
enum class FcntlCmd : s32 {
|
||||
GETFL = 3,
|
||||
SETFL = 4,
|
||||
};
|
||||
|
||||
enum class FcntlFlags : u32 {
|
||||
NONBLOCK = 0x004,
|
||||
NONBLOCK_NX = 0x800,
|
||||
// Provided for convenience
|
||||
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
|
||||
};
|
||||
|
||||
/// Array of IPv4 address
|
||||
using IPv4Address = std::array<u8, 4>;
|
||||
|
||||
struct SockAddrIn {
|
||||
u8 len;
|
||||
u8 family;
|
||||
u16 portno;
|
||||
IPv4Address ip;
|
||||
std::array<u8, 248> zeroes;
|
||||
};
|
||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||
|
||||
enum class PollEvents : u16 {
|
||||
// Using Pascal case because IN is a macro on Windows.
|
||||
IN_ = 0x0001,
|
||||
PRI_ = 0x0002,
|
||||
OUT_ = 0x0004,
|
||||
ERR_ = 0x0008,
|
||||
HUP_ = 0x0010,
|
||||
NVAL = 0x0020,
|
||||
RDNORM = 0x0040,
|
||||
RDBAND = 0x0080,
|
||||
WRBAND = 0x0100,
|
||||
IGNEOF = 0x2000,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||
|
||||
struct PollFD {
|
||||
s32 fd;
|
||||
Network::PollEvents events;
|
||||
Network::PollEvents revents;
|
||||
};
|
||||
static_assert(sizeof(PollFD) == 8);
|
||||
|
||||
struct Linger {
|
||||
s32 onoff;
|
||||
s32 linger;
|
||||
};
|
||||
static_assert(sizeof(Linger) == 8);
|
||||
|
||||
struct Timeval {
|
||||
u64 tv_sec;
|
||||
u64 tv_usec;
|
||||
};
|
||||
static_assert(sizeof(Timeval) == 16);
|
||||
|
||||
/// @brief Cross-platform addrinfo structure (not guest)
|
||||
struct AddrInfo {
|
||||
Domain family;
|
||||
Type socket_type;
|
||||
Protocol protocol;
|
||||
SockAddrIn addr;
|
||||
std::optional<std::string> canon_name;
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
|
||||
struct AcceptResult {
|
||||
std::unique_ptr<SocketBase> socket;
|
||||
SockAddrIn sockaddr_in;
|
||||
Network::SockAddrIn sockaddr_in;
|
||||
};
|
||||
|
||||
SocketBase() = default;
|
||||
@@ -46,13 +46,13 @@ public:
|
||||
|
||||
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
||||
|
||||
virtual Errno Connect(SockAddrIn addr_in) = 0;
|
||||
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
|
||||
|
||||
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
|
||||
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
|
||||
|
||||
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
|
||||
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
|
||||
|
||||
virtual Errno Bind(SockAddrIn addr) = 0;
|
||||
virtual Errno Bind(Network::SockAddrIn addr) = 0;
|
||||
|
||||
virtual Errno Listen(s32 backlog) = 0;
|
||||
|
||||
@@ -60,31 +60,16 @@ public:
|
||||
|
||||
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
||||
|
||||
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
|
||||
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
|
||||
|
||||
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
||||
|
||||
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||
const SockAddrIn* addr) = 0;
|
||||
|
||||
virtual Errno SetLinger(bool enable, u32 linger) = 0;
|
||||
|
||||
virtual Errno SetReuseAddr(bool enable) = 0;
|
||||
|
||||
virtual Errno SetKeepAlive(bool enable) = 0;
|
||||
|
||||
virtual Errno SetBroadcast(bool enable) = 0;
|
||||
|
||||
virtual Errno SetSndBuf(u32 value) = 0;
|
||||
|
||||
virtual Errno SetRcvBuf(u32 value) = 0;
|
||||
|
||||
virtual Errno SetSndTimeo(u32 value) = 0;
|
||||
|
||||
virtual Errno SetRcvTimeo(u32 value) = 0;
|
||||
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
|
||||
|
||||
virtual Errno SetNonBlock(bool enable) = 0;
|
||||
|
||||
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
|
||||
|
||||
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
||||
|
||||
virtual bool IsOpened() const = 0;
|
||||
@@ -95,7 +80,6 @@ public:
|
||||
return fd;
|
||||
}
|
||||
|
||||
protected:
|
||||
SOCKET fd = INVALID_SOCKET;
|
||||
};
|
||||
|
||||
@@ -114,13 +98,13 @@ public:
|
||||
|
||||
std::pair<AcceptResult, Errno> Accept() override;
|
||||
|
||||
Errno Connect(SockAddrIn addr_in) override;
|
||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
||||
|
||||
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
||||
|
||||
std::pair<SockAddrIn, Errno> GetSockName() override;
|
||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
||||
|
||||
Errno Bind(SockAddrIn addr) override;
|
||||
Errno Bind(Network::SockAddrIn addr) override;
|
||||
|
||||
Errno Listen(s32 backlog) override;
|
||||
|
||||
@@ -128,38 +112,19 @@ public:
|
||||
|
||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||
|
||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
||||
|
||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||
|
||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||
const SockAddrIn* addr) override;
|
||||
|
||||
Errno SetLinger(bool enable, u32 linger) override;
|
||||
|
||||
Errno SetReuseAddr(bool enable) override;
|
||||
|
||||
Errno SetKeepAlive(bool enable) override;
|
||||
|
||||
Errno SetBroadcast(bool enable) override;
|
||||
|
||||
Errno SetSndBuf(u32 value) override;
|
||||
|
||||
Errno SetRcvBuf(u32 value) override;
|
||||
|
||||
Errno SetSndTimeo(u32 value) override;
|
||||
|
||||
Errno SetRcvTimeo(u32 value) override;
|
||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
||||
|
||||
Errno SetNonBlock(bool enable) override;
|
||||
|
||||
template <typename T>
|
||||
Errno SetSockOpt(SOCKET fd, int option, T value);
|
||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
||||
|
||||
std::pair<Errno, Errno> GetPendingError() override;
|
||||
|
||||
template <typename T>
|
||||
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
|
||||
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
|
||||
|
||||
bool IsOpened() const override;
|
||||
|
||||
@@ -169,6 +134,6 @@ private:
|
||||
bool is_non_blocking = false;
|
||||
};
|
||||
|
||||
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
|
||||
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -114,13 +114,13 @@ std::vector<Network::ScanData> ScanWifiNetworks(std::chrono::milliseconds deadli
|
||||
char ifname[IFNAMSIZ] = {0};
|
||||
char *args[1] = {ifname};
|
||||
|
||||
iw_enum_devices(sock, [](int f_skfd, char* f_ifname, char* f_args[], int) -> int {
|
||||
iw_enum_devices(sock, [](int skfd, char* ifname, char* args[], int count) -> int {
|
||||
iwrange range;
|
||||
int res = iw_get_range_info(f_skfd, f_ifname, &range);
|
||||
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", f_ifname, res);
|
||||
int res = iw_get_range_info(skfd, ifname, &range);
|
||||
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res);
|
||||
if (res >= 0) {
|
||||
strncpy(f_args[0], f_ifname, IFNAMSIZ - 1);
|
||||
f_args[0][IFNAMSIZ - 1] = 0;
|
||||
strncpy(args[0], ifname, IFNAMSIZ - 1);
|
||||
args[0][IFNAMSIZ - 1] = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <vector>
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "network/verify_user.h"
|
||||
|
||||
namespace Network {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "enet/enet.h"
|
||||
#include "network/packet.h"
|
||||
#include "network/room_member.h"
|
||||
@@ -358,19 +358,23 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
|
||||
// Parse the ProxyPacket from the packet
|
||||
u8 local_family;
|
||||
packet.Read(local_family);
|
||||
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
|
||||
proxy_packet.local_endpoint.len = 16;
|
||||
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
|
||||
packet.Read(proxy_packet.local_endpoint.ip);
|
||||
packet.Read(proxy_packet.local_endpoint.portno);
|
||||
proxy_packet.local_endpoint.zeroes = {};
|
||||
|
||||
u8 remote_family;
|
||||
packet.Read(remote_family);
|
||||
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
|
||||
proxy_packet.remote_endpoint.len = 16;
|
||||
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
|
||||
packet.Read(proxy_packet.remote_endpoint.ip);
|
||||
packet.Read(proxy_packet.remote_endpoint.portno);
|
||||
proxy_packet.local_endpoint.zeroes = {};
|
||||
|
||||
u8 protocol_type;
|
||||
packet.Read(protocol_type);
|
||||
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
|
||||
proxy_packet.protocol = Network::Protocol(protocol_type);
|
||||
|
||||
packet.Read(proxy_packet.broadcast);
|
||||
packet.Read(proxy_packet.data);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <vector>
|
||||
#include "common/announce_multiplayer_room.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/socket_types.h"
|
||||
#include "core/internal_network/socket_types.h"
|
||||
#include "network/room.h"
|
||||
|
||||
namespace Network {
|
||||
@@ -39,8 +39,8 @@ struct LDNPacket {
|
||||
|
||||
/// Information about the received proxy packets.
|
||||
struct ProxyPacket {
|
||||
SockAddrIn local_endpoint;
|
||||
SockAddrIn remote_endpoint;
|
||||
Network::SockAddrIn local_endpoint;
|
||||
Network::SockAddrIn remote_endpoint;
|
||||
Protocol protocol;
|
||||
bool broadcast;
|
||||
std::vector<u8> data;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -12,16 +15,18 @@ TEST_CASE("Network::Errors", "[core]") {
|
||||
Network::Socket socks[2];
|
||||
for (Network::Socket& sock : socks) {
|
||||
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
||||
Network::Protocol::TCP) == Network::Errno::SUCCESS);
|
||||
Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
|
||||
}
|
||||
|
||||
Network::SockAddrIn addr{
|
||||
Network::Domain::INET,
|
||||
{127, 0, 0, 1},
|
||||
16,
|
||||
u8(Network::Domain::INET),
|
||||
1, // hopefully nobody running this test has something listening on port 1
|
||||
{127, 0, 0, 1},
|
||||
{},
|
||||
};
|
||||
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
|
||||
REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
|
||||
|
||||
std::vector<u8> message{1, 2, 3, 4};
|
||||
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
|
||||
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_float.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_2d.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_color_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/blit_depth_stencil_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/block_linear_unswizzle_3d.comp
|
||||
@@ -31,12 +30,8 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_float_to_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa_depth_stencil.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth_stencil.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
|
||||
@@ -168,44 +163,6 @@ foreach(SOURCE_FILE IN ITEMS ${SHADER_FILES})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Integer variants of the MSAA conversion shaders. They only differ from the float
|
||||
# source in the sampler and output types, so they are generated from it via defines.
|
||||
set(SHADER_TYPE_VARIANTS
|
||||
"convert_msaa_to_non_msaa.frag|sint|isampler2DMS|ivec4"
|
||||
"convert_msaa_to_non_msaa.frag|uint|usampler2DMS|uvec4"
|
||||
"convert_non_msaa_to_msaa.frag|sint|isampler2D|ivec4"
|
||||
"convert_non_msaa_to_msaa.frag|uint|usampler2D|uvec4"
|
||||
)
|
||||
|
||||
foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
||||
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
||||
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
||||
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
||||
list(GET VARIANT_PARTS 2 VARIANT_SAMPLER)
|
||||
list(GET VARIANT_PARTS 3 VARIANT_TEXEL)
|
||||
|
||||
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
||||
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
||||
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
||||
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
||||
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
||||
|
||||
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
||||
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VARIANT_HEADER_FILE}
|
||||
COMMAND
|
||||
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
||||
-DSAMPLER_TYPE=${VARIANT_SAMPLER} -DTEXEL_TYPE=${VARIANT_TEXEL}
|
||||
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
||||
--target-env ${SPIR_V_VERSION}
|
||||
MAIN_DEPENDENCY
|
||||
${VARIANT_SOURCE}
|
||||
)
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||
endforeach()
|
||||
|
||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
|
||||
layout(location = 0) in vec2 texcoord;
|
||||
|
||||
void main() {
|
||||
gl_FragDepth = textureLod(depth_tex, texcoord, 0).r;
|
||||
}
|
||||
@@ -8,5 +8,5 @@ layout(binding = 0) uniform sampler2DMS depth_tex;
|
||||
layout(location = 0) in vec2 texcoord;
|
||||
|
||||
void main() {
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ layout(binding = 1) uniform usampler2DMS stencil_tex;
|
||||
layout(location = 0) in vec2 texcoord;
|
||||
|
||||
void main() {
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r);
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), 0).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,7 @@
|
||||
|
||||
#version 450 core
|
||||
|
||||
#ifndef SAMPLER_TYPE
|
||||
#define SAMPLER_TYPE sampler2DMS
|
||||
#endif
|
||||
#ifndef TEXEL_TYPE
|
||||
#define TEXEL_TYPE vec4
|
||||
#endif
|
||||
|
||||
layout(binding = 0) uniform SAMPLER_TYPE msaa_in;
|
||||
layout(binding = 0) uniform sampler2DMS msaa_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
@@ -18,7 +11,7 @@ layout(push_constant) uniform PushConstants {
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2DMS msaa_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
||||
const ivec2 msaa_coord = coord / scale;
|
||||
const ivec2 sample_offset = coord % scale;
|
||||
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
|
||||
gl_FragDepth = texelFetch(msaa_in, msaa_coord, sample_id).r;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
#extension GL_ARB_shader_stencil_export : require
|
||||
|
||||
layout(binding = 0) uniform sampler2DMS depth_tex;
|
||||
layout(binding = 1) uniform usampler2DMS stencil_tex;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
||||
const ivec2 msaa_coord = coord / scale;
|
||||
const ivec2 sample_offset = coord % scale;
|
||||
const int sample_id = sample_offset.x + scale.x * sample_offset.y;
|
||||
gl_FragDepth = texelFetch(depth_tex, msaa_coord, sample_id).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, msaa_coord, sample_id).r);
|
||||
}
|
||||
@@ -3,14 +3,7 @@
|
||||
|
||||
#version 450 core
|
||||
|
||||
#ifndef SAMPLER_TYPE
|
||||
#define SAMPLER_TYPE sampler2D
|
||||
#endif
|
||||
#ifndef TEXEL_TYPE
|
||||
#define TEXEL_TYPE vec4
|
||||
#endif
|
||||
|
||||
layout(binding = 0) uniform SAMPLER_TYPE img_in;
|
||||
layout(binding = 0) uniform sampler2D img_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
@@ -18,7 +11,7 @@ layout(push_constant) uniform PushConstants {
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2D img_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
||||
gl_FragDepth = texelFetch(img_in, coord, 0).r;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
#extension GL_ARB_shader_stencil_export : require
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
||||
gl_FragDepth = texelFetch(depth_tex, coord, 0).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, coord, 0).r);
|
||||
}
|
||||
@@ -75,8 +75,6 @@ public:
|
||||
|
||||
void Finish();
|
||||
|
||||
void FlushDeferredClear() {}
|
||||
|
||||
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
|
||||
|
||||
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
|
||||
@@ -372,7 +370,6 @@ struct TextureCacheParams {
|
||||
static constexpr bool HAS_EMULATED_COPIES = true;
|
||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||
static constexpr bool HAS_MSAA_DOWNLOADS = false;
|
||||
|
||||
using Runtime = OpenGL::TextureCacheRuntime;
|
||||
using Image = OpenGL::Image;
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/settings.h"
|
||||
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
|
||||
#include "video_core/host_shaders/blit_color_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/blit_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/blit_depth_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/blit_depth_stencil_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
||||
@@ -22,15 +21,7 @@
|
||||
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_float_to_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_depth_stencil_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_sint_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_msaa_to_non_msaa_uint_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_sint_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_uint_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_non_msaa_to_msaa_depth_stencil_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_s8d24_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/full_screen_triangle_vert_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_blit_depth_stencil_frag_spv.h"
|
||||
@@ -527,19 +518,8 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] MSAACopyFormatClass FormatClass(VideoCore::Surface::PixelFormat format) {
|
||||
if (!VideoCore::Surface::IsPixelFormatInteger(format)) {
|
||||
return MSAACopyFormatClass::Float;
|
||||
}
|
||||
if (VideoCore::Surface::IsPixelFormatSignedInteger(format)) {
|
||||
return MSAACopyFormatClass::SignedInteger;
|
||||
}
|
||||
return MSAACopyFormatClass::UnsignedInteger;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeMSAACopyView(const vk::Device& device, VkImage image,
|
||||
VkFormat format, u32 base_level, u32 base_layer,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
VkFormat format, u32 base_level) {
|
||||
return device.CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -554,10 +534,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = base_level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = base_layer,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
});
|
||||
@@ -606,17 +586,12 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
msaa_copy_pipeline_layout(device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
||||
one_texture_set_layout.address(),
|
||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
||||
msaa_copy_depth_stencil_pipeline_layout(
|
||||
device.GetLogical().CreatePipelineLayout(PipelineLayoutCreateInfo(
|
||||
two_textures_set_layout.address(),
|
||||
PUSH_CONSTANT_RANGE<VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(MSAACopyPushConstants)>))),
|
||||
full_screen_vert(BuildShader(device, FULL_SCREEN_TRIANGLE_VERT_SPV)),
|
||||
blit_color_to_color_frag(BuildShader(device, BLIT_COLOR_FLOAT_FRAG_SPV)),
|
||||
blit_color_msaa_frag(BuildShader(device, BLIT_COLOR_MSAA_FRAG_SPV)),
|
||||
blit_depth_stencil_frag(device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, VULKAN_BLIT_DEPTH_STENCIL_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
blit_depth_frag(BuildShader(device, BLIT_DEPTH_FRAG_SPV)),
|
||||
blit_depth_msaa_frag(BuildShader(device, BLIT_DEPTH_MSAA_FRAG_SPV)),
|
||||
blit_depth_stencil_msaa_frag(device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, BLIT_DEPTH_STENCIL_MSAA_FRAG_SPV)
|
||||
@@ -634,25 +609,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_frag(BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_sint_frag(
|
||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_SINT_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_uint_frag(
|
||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_UINT_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_depth_frag(
|
||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_FRAG_SPV)),
|
||||
convert_msaa_to_non_msaa_depth_stencil_frag(
|
||||
BuildShader(device, CONVERT_MSAA_TO_NON_MSAA_DEPTH_STENCIL_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_frag(BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_sint_frag(
|
||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_SINT_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_uint_frag(
|
||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_UINT_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_depth_frag(
|
||||
BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_FRAG_SPV)),
|
||||
convert_non_msaa_to_msaa_depth_stencil_frag(
|
||||
device.IsExtShaderStencilExportSupported()
|
||||
? BuildShader(device, CONVERT_NON_MSAA_TO_MSAA_DEPTH_STENCIL_FRAG_SPV)
|
||||
: vk::ShaderModule{}),
|
||||
linear_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_LINEAR>)),
|
||||
nearest_sampler(device.GetLogical().CreateSampler(SAMPLER_CREATE_INFO<VK_FILTER_NEAREST>)) {}
|
||||
|
||||
@@ -667,13 +624,24 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.operation = operation,
|
||||
};
|
||||
VkSampler sampler = *nearest_sampler;
|
||||
if (is_linear) {
|
||||
sampler = *linear_sampler;
|
||||
}
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
||||
FindOrEmplaceColorPipeline(key), sampler,
|
||||
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkSampler sampler = is_linear ? *linear_sampler : *nearest_sampler;
|
||||
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
|
||||
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||
|
||||
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
|
||||
src_view](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
||||
@@ -702,29 +670,24 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
||||
});
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region, VkPipeline pipeline, VkSampler sampler,
|
||||
VkImageView src_view, VkImageView src_stencil_view,
|
||||
bool blit_stencil) {
|
||||
VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
if (blit_stencil) {
|
||||
layout = *two_textures_pipeline_layout;
|
||||
}
|
||||
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region) {
|
||||
const BlitMSAAPipelineKey key{
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
};
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkPipeline pipeline = FindOrEmplaceBlitColorMSAAPipeline(key);
|
||||
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||
|
||||
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view,
|
||||
src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) {
|
||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
||||
if (blit_stencil) {
|
||||
descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view,
|
||||
src_stencil_view);
|
||||
} else {
|
||||
descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||
}
|
||||
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler,
|
||||
src_view](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
@@ -734,56 +697,41 @@ void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region) {
|
||||
const BlitMSAAPipelineKey key{
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
};
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
||||
FindOrEmplaceBlitColorMSAAPipeline(key), *nearest_sampler,
|
||||
src_image_view.Handle(Shader::TextureType::Color2D), VK_NULL_HANDLE, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region) {
|
||||
const bool blit_stencil =
|
||||
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
||||
const BlitMSAAPipelineKey key{
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
};
|
||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
||||
if (blit_stencil) {
|
||||
src_stencil_view = src_image_view.StencilView();
|
||||
}
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
||||
FindOrEmplaceBlitDepthStencilMSAAPipeline(key, blit_stencil), *nearest_sampler,
|
||||
src_image_view.DepthView(), src_stencil_view, blit_stencil);
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region) {
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
||||
FindOrEmplaceBlitDepthPipeline(dst_framebuffer->RenderPass()), *nearest_sampler,
|
||||
src_image_view.DepthView(), VK_NULL_HANDLE, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region) {
|
||||
const bool resolve_stencil =
|
||||
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
||||
if (resolve_stencil) {
|
||||
src_stencil_view = src_image_view.StencilView();
|
||||
}
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region,
|
||||
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(),
|
||||
resolve_stencil),
|
||||
*nearest_sampler, src_image_view.DepthView(), src_stencil_view, resolve_stencil);
|
||||
const VkPipeline pipeline =
|
||||
FindOrEmplaceResolveDepthStencilPipeline(dst_framebuffer->RenderPass(), resolve_stencil);
|
||||
const VkPipelineLayout layout =
|
||||
resolve_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout;
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||
const VkImageView src_stencil_view =
|
||||
resolve_stencil ? src_image_view.StencilView() : VK_NULL_HANDLE;
|
||||
|
||||
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_depth_view,
|
||||
src_stencil_view, resolve_stencil](vk::CommandBuffer cmdbuf) {
|
||||
if (resolve_stencil) {
|
||||
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
|
||||
src_stencil_view);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
} else {
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_depth_view);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
}
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
@@ -791,23 +739,35 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
const Region2D& dst_region, const Region2D& src_region,
|
||||
Tegra::Engines::Fermi2D::Filter filter,
|
||||
Tegra::Engines::Fermi2D::Operation operation) {
|
||||
if (!device.IsExtShaderStencilExportSupported()) {
|
||||
return;
|
||||
}
|
||||
ASSERT(filter == Tegra::Engines::Fermi2D::Filter::Point);
|
||||
ASSERT(operation == Tegra::Engines::Fermi2D::Operation::SrcCopy);
|
||||
const bool blit_stencil = device.IsExtShaderStencilExportSupported();
|
||||
const BlitImagePipelineKey key{
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.operation = operation,
|
||||
};
|
||||
VkPipeline pipeline{};
|
||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
||||
if (blit_stencil) {
|
||||
pipeline = FindOrEmplaceDepthStencilPipeline(key);
|
||||
src_stencil_view = src_image_view.StencilView();
|
||||
} else {
|
||||
pipeline = FindOrEmplaceBlitDepthPipeline(key.renderpass);
|
||||
}
|
||||
BlitImpl(dst_framebuffer, src_image_view, dst_region, src_region, pipeline, *nearest_sampler,
|
||||
src_image_view.DepthView(), src_stencil_view, blit_stencil);
|
||||
const VkPipelineLayout layout = *two_textures_pipeline_layout;
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkPipeline pipeline = FindOrEmplaceDepthStencilPipeline(key);
|
||||
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||
const VkImageView src_stencil_view = src_image_view.StencilView();
|
||||
|
||||
RecordShaderReadBarrier(scheduler, src_image_view);
|
||||
scheduler.RequestRenderpass(dst_framebuffer);
|
||||
scheduler.Record([dst_region, src_region, pipeline, layout, sampler, src_depth_view,
|
||||
src_stencil_view, this](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_depth_view,
|
||||
src_stencil_view);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
BindBlitState(cmdbuf, layout, dst_region, src_region);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
});
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
||||
@@ -922,183 +882,19 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline,
|
||||
VkPipelineLayout layout, VkImage dst_image,
|
||||
VkFormat dst_vk_format, VkImage src_image,
|
||||
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
const MSAACopyAspectInfo& aspect_info, bool copy_stencil) {
|
||||
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
|
||||
msaa_copy_resources.pop_front();
|
||||
}
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
for (const VideoCommon::ImageCopy& copy : copies) {
|
||||
const s32 num_layers = (std::min)(copy.src_subresource.num_layers,
|
||||
copy.dst_subresource.num_layers);
|
||||
for (s32 layer = 0; layer < num_layers; ++layer) {
|
||||
const u32 src_level = static_cast<u32>(copy.src_subresource.base_level);
|
||||
const u32 src_layer = static_cast<u32>(copy.src_subresource.base_layer + layer);
|
||||
vk::ImageView src_view =
|
||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
|
||||
src_layer, aspect_info.src_view_aspect);
|
||||
vk::ImageView src_stencil_view;
|
||||
if (copy_stencil) {
|
||||
src_stencil_view =
|
||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format, src_level,
|
||||
src_layer, VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||
}
|
||||
vk::ImageView dst_view =
|
||||
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
||||
static_cast<u32>(copy.dst_subresource.base_level),
|
||||
static_cast<u32>(copy.dst_subresource.base_layer + layer),
|
||||
aspect_info.attachment_aspect);
|
||||
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
||||
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
||||
const VkRect2D render_area{
|
||||
.offset = dst_offset,
|
||||
.extent = dst_extent,
|
||||
};
|
||||
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = renderpass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = dst_view.address(),
|
||||
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
|
||||
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
|
||||
.layers = 1,
|
||||
});
|
||||
const MSAACopyPushConstants push_constants{
|
||||
.dst_offset = {dst_offset.x, dst_offset.y},
|
||||
.src_offset = {copy.src_offset.x, copy.src_offset.y},
|
||||
.scale = {scale_x, scale_y},
|
||||
};
|
||||
VkImageView src_stencil_handle = VK_NULL_HANDLE;
|
||||
if (copy_stencil) {
|
||||
src_stencil_handle = *src_stencil_view;
|
||||
}
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, pipeline, layout, sampler, renderpass,
|
||||
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
|
||||
src_stencil_handle, src = src_image, dst = dst_image, render_area,
|
||||
aspect_info, push_constants](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageSubresourceRange barrier_range{
|
||||
.aspectMask = aspect_info.barrier_aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
const std::array pre_barriers{
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = aspect_info.pre_src_access,
|
||||
.dstAccessMask = aspect_info.pre_src_dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = src,
|
||||
.subresourceRange = barrier_range,
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = aspect_info.pre_src_access,
|
||||
.dstAccessMask = aspect_info.pre_dst_dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = barrier_range,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(aspect_info.pre_src_stages, aspect_info.pre_dst_stages, 0,
|
||||
nullptr, nullptr, pre_barriers);
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = renderpass,
|
||||
.framebuffer = framebuffer_handle,
|
||||
.renderArea = render_area,
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
|
||||
if (src_stencil_handle != VK_NULL_HANDLE) {
|
||||
descriptor_set = two_textures_descriptor_allocator.Commit();
|
||||
UpdateTwoTexturesDescriptorSet(device, descriptor_set, sampler, src_view_handle,
|
||||
src_stencil_handle);
|
||||
} else {
|
||||
descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
|
||||
}
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
const VkViewport viewport{
|
||||
.x = static_cast<float>(render_area.offset.x),
|
||||
.y = static_cast<float>(render_area.offset.y),
|
||||
.width = static_cast<float>(render_area.extent.width),
|
||||
.height = static_cast<float>(render_area.extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, render_area);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
const VkImageMemoryBarrier post_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = aspect_info.post_src_access,
|
||||
.dstAccessMask = aspect_info.post_dst_access,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = barrier_range,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(aspect_info.post_src_stages, aspect_info.post_dst_stages, 0,
|
||||
post_barrier);
|
||||
});
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.src_view = std::move(src_view),
|
||||
.dst_view = std::move(dst_view),
|
||||
.framebuffer = std::move(framebuffer),
|
||||
});
|
||||
if (copy_stencil) {
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.src_view = std::move(src_stencil_view),
|
||||
.dst_view = vk::ImageView{},
|
||||
.framebuffer = vk::Framebuffer{},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
bool msaa_to_non_msaa) {
|
||||
while (!msaa_copy_resources.empty() && scheduler.IsFree(msaa_copy_resources.front().tick)) {
|
||||
msaa_copy_resources.pop_front();
|
||||
}
|
||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
||||
const s32 scale_x = 1 << samples_x;
|
||||
const s32 scale_y = 1 << samples_y;
|
||||
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
|
||||
if (msaa_to_non_msaa) {
|
||||
samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
}
|
||||
const VkSampleCountFlagBits samples =
|
||||
msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
|
||||
RenderPassKey renderpass_key{};
|
||||
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||
renderpass_key.color_formats[0] = dst_format;
|
||||
@@ -1109,35 +905,149 @@ void BlitImageHelper::CopyMSAA(RenderPassCache& render_pass_cache, VkImage dst_i
|
||||
.renderpass = renderpass,
|
||||
.samples = samples,
|
||||
.msaa_to_non_msaa = msaa_to_non_msaa,
|
||||
.format_class = FormatClass(dst_format),
|
||||
};
|
||||
const MSAACopyAspectInfo aspect_info{
|
||||
.src_view_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.attachment_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.barrier_aspect = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.pre_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
||||
.pre_dst_dst_access =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.pre_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
.pre_dst_stages =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.post_src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.post_dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
};
|
||||
const VkPipeline pipeline = FindOrEmplaceMSAACopyPipeline(key);
|
||||
const VkPipelineLayout layout = *msaa_copy_pipeline_layout;
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkFormat src_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||
const VkFormat dst_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
||||
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyPipeline(key), *msaa_copy_pipeline_layout,
|
||||
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
|
||||
aspect_info, false);
|
||||
for (const VideoCommon::ImageCopy& copy : copies) {
|
||||
ASSERT(copy.src_subresource.base_layer == 0);
|
||||
ASSERT(copy.src_subresource.num_layers == 1);
|
||||
ASSERT(copy.dst_subresource.base_layer == 0);
|
||||
ASSERT(copy.dst_subresource.num_layers == 1);
|
||||
vk::ImageView src_view =
|
||||
MakeMSAACopyView(device.GetLogical(), src_image, src_vk_format,
|
||||
static_cast<u32>(copy.src_subresource.base_level));
|
||||
vk::ImageView dst_view =
|
||||
MakeMSAACopyView(device.GetLogical(), dst_image, dst_vk_format,
|
||||
static_cast<u32>(copy.dst_subresource.base_level));
|
||||
const VkOffset2D dst_offset{copy.dst_offset.x, copy.dst_offset.y};
|
||||
const VkExtent2D dst_extent{copy.extent.width, copy.extent.height};
|
||||
const VkRect2D render_area{
|
||||
.offset = dst_offset,
|
||||
.extent = dst_extent,
|
||||
};
|
||||
vk::Framebuffer framebuffer = device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.renderPass = renderpass,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = dst_view.address(),
|
||||
.width = static_cast<u32>(dst_offset.x) + dst_extent.width,
|
||||
.height = static_cast<u32>(dst_offset.y) + dst_extent.height,
|
||||
.layers = 1,
|
||||
});
|
||||
const MSAACopyPushConstants push_constants{
|
||||
.dst_offset = {dst_offset.x, dst_offset.y},
|
||||
.src_offset = {copy.src_offset.x, copy.src_offset.y},
|
||||
.scale = {scale_x, scale_y},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([this, pipeline, layout, sampler, renderpass,
|
||||
framebuffer_handle = *framebuffer, src_view_handle = *src_view,
|
||||
src = src_image, dst = dst_image, render_area,
|
||||
push_constants](vk::CommandBuffer cmdbuf) {
|
||||
constexpr VkImageSubresourceRange color_range{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
};
|
||||
const std::array pre_barriers{
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = src,
|
||||
.subresourceRange = color_range,
|
||||
},
|
||||
VkImageMemoryBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = color_range,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0, nullptr, nullptr, pre_barriers);
|
||||
const VkRenderPassBeginInfo renderpass_bi{
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.pNext = nullptr,
|
||||
.renderPass = renderpass,
|
||||
.framebuffer = framebuffer_handle,
|
||||
.renderArea = render_area,
|
||||
.clearValueCount = 0,
|
||||
.pClearValues = nullptr,
|
||||
};
|
||||
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
||||
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
|
||||
UpdateOneTextureDescriptorSet(device, descriptor_set, sampler, src_view_handle);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set,
|
||||
nullptr);
|
||||
const VkViewport viewport{
|
||||
.x = static_cast<float>(render_area.offset.x),
|
||||
.y = static_cast<float>(render_area.offset.y),
|
||||
.width = static_cast<float>(render_area.extent.width),
|
||||
.height = static_cast<float>(render_area.extent.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
cmdbuf.SetViewport(0, viewport);
|
||||
cmdbuf.SetScissor(0, render_area);
|
||||
cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, push_constants);
|
||||
cmdbuf.Draw(3, 1, 0, 0);
|
||||
cmdbuf.EndRenderPass();
|
||||
const VkImageMemoryBarrier post_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = dst,
|
||||
.subresourceRange = color_range,
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
0, post_barrier);
|
||||
});
|
||||
msaa_copy_resources.push_back(MSAACopyResources{
|
||||
.tick = scheduler.CurrentTick(),
|
||||
.src_view = std::move(src_view),
|
||||
.dst_view = std::move(dst_view),
|
||||
.framebuffer = std::move(framebuffer),
|
||||
});
|
||||
}
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
@@ -1469,87 +1379,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPip
|
||||
return *blit_msaa_color_pipelines.back();
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthStencilMSAAPipeline(
|
||||
const BlitMSAAPipelineKey& key, bool blit_stencil) {
|
||||
auto& keys = blit_stencil ? blit_msaa_depth_stencil_keys : blit_msaa_depth_keys;
|
||||
auto& pipelines = blit_stencil ? blit_msaa_depth_stencil_pipelines : blit_msaa_depth_pipelines;
|
||||
const auto it = std::ranges::find(keys, key);
|
||||
if (it != keys.end()) {
|
||||
return *pipelines[std::distance(keys.begin(), it)];
|
||||
}
|
||||
keys.push_back(key);
|
||||
const std::array stages =
|
||||
MakeStages(*full_screen_vert,
|
||||
blit_stencil ? *blit_depth_stencil_msaa_frag : *blit_depth_msaa_frag);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = key.samples,
|
||||
.sampleShadingEnable = VK_TRUE,
|
||||
.minSampleShading = 1.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = blit_stencil ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
|
||||
: &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = blit_stencil ? *two_textures_pipeline_layout : *one_texture_pipeline_layout,
|
||||
.renderPass = key.renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass) {
|
||||
const auto it = std::ranges::find(blit_depth_keys, renderpass);
|
||||
if (it != blit_depth_keys.end()) {
|
||||
return *blit_depth_pipelines[std::distance(blit_depth_keys.begin(), it)];
|
||||
}
|
||||
blit_depth_keys.push_back(renderpass);
|
||||
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_frag);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
blit_depth_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_ONLY_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *one_texture_pipeline_layout,
|
||||
.renderPass = renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
return *blit_depth_pipelines.back();
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
||||
bool resolve_stencil) {
|
||||
auto& keys = resolve_stencil ? resolve_depth_stencil_keys : resolve_depth_keys;
|
||||
@@ -1588,83 +1417,15 @@ VkPipeline BlitImageHelper::FindOrEmplaceResolveDepthStencilPipeline(VkRenderPas
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
bool copy_stencil, bool msaa_to_non_msaa) {
|
||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(static_cast<int>(num_samples));
|
||||
const s32 scale_x = 1 << samples_x;
|
||||
const s32 scale_y = 1 << samples_y;
|
||||
VkSampleCountFlagBits samples = SampleCountFlag(num_samples);
|
||||
if (msaa_to_non_msaa) {
|
||||
samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
}
|
||||
RenderPassKey renderpass_key{};
|
||||
renderpass_key.color_formats.fill(VideoCore::Surface::PixelFormat::Invalid);
|
||||
renderpass_key.depth_format = dst_format;
|
||||
renderpass_key.samples = samples;
|
||||
const VkRenderPass renderpass = render_pass_cache.Get(renderpass_key);
|
||||
const MSAACopyPipelineKey key{
|
||||
.renderpass = renderpass,
|
||||
.samples = samples,
|
||||
.msaa_to_non_msaa = msaa_to_non_msaa,
|
||||
.format_class = MSAACopyFormatClass::Float,
|
||||
};
|
||||
VkImageAspectFlags attachment_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
if (VideoCore::Surface::GetFormatType(dst_format) ==
|
||||
VideoCore::Surface::SurfaceType::DepthStencil) {
|
||||
attachment_aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
}
|
||||
VkPipelineLayout layout = *msaa_copy_pipeline_layout;
|
||||
if (copy_stencil) {
|
||||
layout = *msaa_copy_depth_stencil_pipeline_layout;
|
||||
}
|
||||
const MSAACopyAspectInfo aspect_info{
|
||||
.src_view_aspect = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
.attachment_aspect = attachment_aspect,
|
||||
.barrier_aspect = attachment_aspect,
|
||||
.pre_src_access =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.pre_src_dst_access = VK_ACCESS_SHADER_READ_BIT,
|
||||
.pre_dst_dst_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.pre_src_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
.pre_dst_stages =
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
||||
.post_src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.post_dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
|
||||
.post_src_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
|
||||
.post_dst_stages = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||
};
|
||||
const VkFormat src_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, src_format).format;
|
||||
const VkFormat dst_vk_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, true, dst_format).format;
|
||||
CopyMSAAImpl(renderpass, FindOrEmplaceMSAACopyDepthPipeline(key, copy_stencil), layout,
|
||||
dst_image, dst_vk_format, src_image, src_vk_format, scale_x, scale_y, copies,
|
||||
aspect_info, copy_stencil);
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key) {
|
||||
const auto it = std::ranges::find(msaa_copy_keys, key);
|
||||
if (it != msaa_copy_keys.end()) {
|
||||
return *msaa_copy_pipelines[std::distance(msaa_copy_keys.begin(), it)];
|
||||
}
|
||||
msaa_copy_keys.push_back(key);
|
||||
VkShaderModule frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_frag
|
||||
: *convert_non_msaa_to_msaa_frag;
|
||||
if (key.format_class == MSAACopyFormatClass::SignedInteger) {
|
||||
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_sint_frag
|
||||
: *convert_non_msaa_to_msaa_sint_frag;
|
||||
} else if (key.format_class == MSAACopyFormatClass::UnsignedInteger) {
|
||||
frag_module = key.msaa_to_non_msaa ? *convert_msaa_to_non_msaa_uint_frag
|
||||
: *convert_non_msaa_to_msaa_uint_frag;
|
||||
}
|
||||
const std::array stages = MakeStages(*clear_color_vert, frag_module);
|
||||
const std::array stages = MakeStages(*clear_color_vert, key.msaa_to_non_msaa
|
||||
? *convert_msaa_to_non_msaa_frag
|
||||
: *convert_non_msaa_to_msaa_frag);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1701,85 +1462,6 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyPipeline(const MSAACopyPipeline
|
||||
return *msaa_copy_pipelines.back();
|
||||
}
|
||||
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
||||
bool copy_stencil) {
|
||||
auto& keys = copy_stencil ? msaa_copy_depth_stencil_keys : msaa_copy_depth_keys;
|
||||
auto& pipelines = copy_stencil ? msaa_copy_depth_stencil_pipelines : msaa_copy_depth_pipelines;
|
||||
const auto it = std::ranges::find(keys, key);
|
||||
if (it != keys.end()) {
|
||||
return *pipelines[std::distance(keys.begin(), it)];
|
||||
}
|
||||
keys.push_back(key);
|
||||
VkShaderModule frag_module;
|
||||
if (key.msaa_to_non_msaa) {
|
||||
frag_module = copy_stencil ? *convert_msaa_to_non_msaa_depth_stencil_frag
|
||||
: *convert_msaa_to_non_msaa_depth_frag;
|
||||
} else {
|
||||
frag_module = copy_stencil ? *convert_non_msaa_to_msaa_depth_stencil_frag
|
||||
: *convert_non_msaa_to_msaa_depth_frag;
|
||||
}
|
||||
const std::array stages = MakeStages(*clear_color_vert, frag_module);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.rasterizationSamples = key.samples,
|
||||
.sampleShadingEnable = key.msaa_to_non_msaa ? VK_FALSE : VK_TRUE,
|
||||
.minSampleShading = key.msaa_to_non_msaa ? 0.0f : 1.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
static constexpr VkStencilOpState REPLACE_STENCIL_OP{
|
||||
.failOp = VK_STENCIL_OP_REPLACE,
|
||||
.passOp = VK_STENCIL_OP_REPLACE,
|
||||
.depthFailOp = VK_STENCIL_OP_REPLACE,
|
||||
.compareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.compareMask = 0xFF,
|
||||
.writeMask = 0xFF,
|
||||
.reference = 0,
|
||||
};
|
||||
const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthTestEnable = VK_TRUE,
|
||||
.depthWriteEnable = VK_TRUE,
|
||||
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.depthBoundsTestEnable = VK_FALSE,
|
||||
.stencilTestEnable = copy_stencil ? VK_TRUE : VK_FALSE,
|
||||
.front = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
||||
.back = copy_stencil ? REPLACE_STENCIL_OP : VkStencilOpState{},
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
|
||||
GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<u32>(stages.size()),
|
||||
.pStages = stages.data(),
|
||||
.pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &depth_stencil_ci,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = copy_stencil ? *msaa_copy_depth_stencil_pipeline_layout
|
||||
: *msaa_copy_pipeline_layout,
|
||||
.renderPass = key.renderpass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
}));
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline, VkRenderPass renderpass) {
|
||||
ConvertPipeline(pipeline, renderpass, false);
|
||||
}
|
||||
|
||||
@@ -45,19 +45,12 @@ struct BlitDepthStencilPipelineKey {
|
||||
u32 stencil_ref;
|
||||
};
|
||||
|
||||
enum class MSAACopyFormatClass : u32 {
|
||||
Float,
|
||||
SignedInteger,
|
||||
UnsignedInteger,
|
||||
};
|
||||
|
||||
struct MSAACopyPipelineKey {
|
||||
constexpr auto operator<=>(const MSAACopyPipelineKey&) const noexcept = default;
|
||||
|
||||
VkRenderPass renderpass;
|
||||
VkSampleCountFlagBits samples;
|
||||
bool msaa_to_non_msaa;
|
||||
MSAACopyFormatClass format_class;
|
||||
};
|
||||
|
||||
struct BlitMSAAPipelineKey {
|
||||
@@ -85,12 +78,6 @@ public:
|
||||
void BlitColorMSAA(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region);
|
||||
|
||||
void BlitDepthStencilMSAA(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region);
|
||||
|
||||
void BlitDepth(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region);
|
||||
|
||||
void ResolveDepthStencil(const Framebuffer* dst_framebuffer, ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region);
|
||||
|
||||
@@ -129,39 +116,7 @@ public:
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies, bool msaa_to_non_msaa);
|
||||
|
||||
void CopyMSAADepth(RenderPassCache& render_pass_cache, VkImage dst_image,
|
||||
VideoCore::Surface::PixelFormat dst_format, VkImage src_image,
|
||||
VideoCore::Surface::PixelFormat src_format, u32 num_samples,
|
||||
std::span<const VideoCommon::ImageCopy> copies, bool copy_stencil,
|
||||
bool msaa_to_non_msaa);
|
||||
|
||||
private:
|
||||
struct MSAACopyAspectInfo {
|
||||
VkImageAspectFlags src_view_aspect;
|
||||
VkImageAspectFlags attachment_aspect;
|
||||
VkImageAspectFlags barrier_aspect;
|
||||
VkAccessFlags pre_src_access;
|
||||
VkAccessFlags pre_src_dst_access;
|
||||
VkAccessFlags pre_dst_dst_access;
|
||||
VkPipelineStageFlags pre_src_stages;
|
||||
VkPipelineStageFlags pre_dst_stages;
|
||||
VkAccessFlags post_src_access;
|
||||
VkAccessFlags post_dst_access;
|
||||
VkPipelineStageFlags post_src_stages;
|
||||
VkPipelineStageFlags post_dst_stages;
|
||||
};
|
||||
|
||||
void BlitImpl(const Framebuffer* dst_framebuffer, const ImageView& src_image_view,
|
||||
const Region2D& dst_region, const Region2D& src_region, VkPipeline pipeline,
|
||||
VkSampler sampler, VkImageView src_view, VkImageView src_stencil_view,
|
||||
bool blit_stencil);
|
||||
|
||||
void CopyMSAAImpl(VkRenderPass renderpass, VkPipeline pipeline, VkPipelineLayout layout,
|
||||
VkImage dst_image, VkFormat dst_vk_format, VkImage src_image,
|
||||
VkFormat src_vk_format, s32 scale_x, s32 scale_y,
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
const MSAACopyAspectInfo& aspect_info, bool copy_stencil);
|
||||
|
||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view);
|
||||
|
||||
@@ -176,13 +131,7 @@ private:
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceClearStencilPipeline(
|
||||
const BlitDepthStencilPipelineKey& key);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyPipeline(const MSAACopyPipelineKey& key);
|
||||
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPipelineKey& key,
|
||||
bool copy_stencil);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitColorMSAAPipeline(const BlitMSAAPipelineKey& key);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthStencilMSAAPipeline(
|
||||
const BlitMSAAPipelineKey& key, bool blit_stencil);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(VkRenderPass renderpass);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(VkRenderPass renderpass,
|
||||
bool resolve_stencil);
|
||||
|
||||
@@ -213,12 +162,10 @@ private:
|
||||
vk::PipelineLayout two_textures_pipeline_layout;
|
||||
vk::PipelineLayout clear_color_pipeline_layout;
|
||||
vk::PipelineLayout msaa_copy_pipeline_layout;
|
||||
vk::PipelineLayout msaa_copy_depth_stencil_pipeline_layout;
|
||||
vk::ShaderModule full_screen_vert;
|
||||
vk::ShaderModule blit_color_to_color_frag;
|
||||
vk::ShaderModule blit_color_msaa_frag;
|
||||
vk::ShaderModule blit_depth_stencil_frag;
|
||||
vk::ShaderModule blit_depth_frag;
|
||||
vk::ShaderModule blit_depth_msaa_frag;
|
||||
vk::ShaderModule blit_depth_stencil_msaa_frag;
|
||||
vk::ShaderModule clear_color_vert;
|
||||
@@ -232,15 +179,7 @@ private:
|
||||
vk::ShaderModule convert_d24s8_to_abgr8_frag;
|
||||
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_sint_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_uint_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_depth_frag;
|
||||
vk::ShaderModule convert_msaa_to_non_msaa_depth_stencil_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_sint_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_uint_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_frag;
|
||||
vk::ShaderModule convert_non_msaa_to_msaa_depth_stencil_frag;
|
||||
vk::Sampler linear_sampler;
|
||||
vk::Sampler nearest_sampler;
|
||||
|
||||
@@ -254,18 +193,8 @@ private:
|
||||
std::vector<vk::Pipeline> clear_stencil_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_depth_pipelines;
|
||||
std::vector<MSAACopyPipelineKey> msaa_copy_depth_stencil_keys;
|
||||
std::vector<vk::Pipeline> msaa_copy_depth_stencil_pipelines;
|
||||
std::vector<BlitMSAAPipelineKey> blit_msaa_color_keys;
|
||||
std::vector<vk::Pipeline> blit_msaa_color_pipelines;
|
||||
std::vector<VkRenderPass> blit_depth_keys;
|
||||
std::vector<vk::Pipeline> blit_depth_pipelines;
|
||||
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_keys;
|
||||
std::vector<vk::Pipeline> blit_msaa_depth_pipelines;
|
||||
std::vector<BlitMSAAPipelineKey> blit_msaa_depth_stencil_keys;
|
||||
std::vector<vk::Pipeline> blit_msaa_depth_stencil_pipelines;
|
||||
std::vector<VkRenderPass> resolve_depth_keys;
|
||||
std::vector<vk::Pipeline> resolve_depth_pipelines;
|
||||
std::vector<VkRenderPass> resolve_depth_stencil_keys;
|
||||
|
||||
@@ -424,12 +424,24 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
||||
const bool ds_deferrable =
|
||||
!ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) &&
|
||||
(!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial);
|
||||
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && !regs.clear_control.use_scissor &&
|
||||
regs.clear_surface.layer == 0 &&
|
||||
!scheduler.IsRenderPassActive() &&
|
||||
(!use_color || color_full_channels) && ds_deferrable;
|
||||
if (!can_defer_clear) {
|
||||
scheduler.RequestRenderpass(framebuffer);
|
||||
}
|
||||
|
||||
query_cache.NotifySegment(true);
|
||||
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
|
||||
u32 up_scale = 1;
|
||||
u32 down_shift = 0;
|
||||
if (texture_cache.IsRescaling()) {
|
||||
up_scale = Settings::values.resolution_info.up_scale;
|
||||
down_shift = Settings::values.resolution_info.down_shift;
|
||||
}
|
||||
UpdateViewportsState(regs);
|
||||
|
||||
VkRect2D default_scissor{};
|
||||
default_scissor.offset.x = 0;
|
||||
default_scissor.offset.y = 0;
|
||||
@@ -485,22 +497,6 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool clear_covers_render_area =
|
||||
clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 &&
|
||||
clear_rect.rect.extent.width >= render_area.width &&
|
||||
clear_rect.rect.extent.height >= render_area.height;
|
||||
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) &&
|
||||
regs.clear_surface.layer == 0 &&
|
||||
!scheduler.IsRenderPassActive() &&
|
||||
(!use_color || color_full_channels) && ds_deferrable;
|
||||
if (!can_defer_clear) {
|
||||
scheduler.RequestRenderpass(framebuffer);
|
||||
}
|
||||
|
||||
query_cache.NotifySegment(true);
|
||||
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable);
|
||||
UpdateViewportsState(regs);
|
||||
|
||||
const u32 color_attachment = regs.clear_surface.RT;
|
||||
if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
|
||||
const auto format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(regs.rt[color_attachment].format);
|
||||
|
||||
@@ -65,72 +65,8 @@ using VideoCore::Surface::SurfaceType;
|
||||
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
}
|
||||
|
||||
struct ResolveAspects {
|
||||
bool depth;
|
||||
bool stencil;
|
||||
};
|
||||
|
||||
struct ResolveModes {
|
||||
VkResolveModeFlagBits depth;
|
||||
VkResolveModeFlagBits stencil;
|
||||
};
|
||||
|
||||
constexpr ResolveAspects GetResolveAspects(PixelFormat format) {
|
||||
const SurfaceType surface_type = GetSurfaceType(format);
|
||||
return ResolveAspects{
|
||||
.depth = surface_type == SurfaceType::Depth ||
|
||||
surface_type == SurfaceType::DepthStencil,
|
||||
.stencil = surface_type == SurfaceType::Stencil ||
|
||||
surface_type == SurfaceType::DepthStencil,
|
||||
};
|
||||
}
|
||||
|
||||
ResolveModes PickResolveModes(const Device& device, PixelFormat format) {
|
||||
constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
|
||||
|
||||
const ResolveAspects aspects = GetResolveAspects(format);
|
||||
const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0;
|
||||
const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0;
|
||||
|
||||
ResolveModes modes{
|
||||
.depth = VK_RESOLVE_MODE_NONE,
|
||||
.stencil = VK_RESOLVE_MODE_NONE,
|
||||
};
|
||||
if (aspects.depth && depth_mode_supported) {
|
||||
modes.depth = mode;
|
||||
}
|
||||
if (aspects.stencil && stencil_mode_supported) {
|
||||
modes.stencil = mode;
|
||||
}
|
||||
if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) {
|
||||
return modes;
|
||||
}
|
||||
if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) {
|
||||
modes.stencil = mode;
|
||||
} else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) {
|
||||
modes.depth = mode;
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
bool SupportsDepthStencilResolve(const Device& device, PixelFormat depth_format) {
|
||||
if (depth_format == PixelFormat::Invalid || !device.IsKhrDepthStencilResolveSupported()) {
|
||||
return false;
|
||||
}
|
||||
const ResolveAspects aspects = GetResolveAspects(depth_format);
|
||||
if (!aspects.depth && !aspects.stencil) {
|
||||
return false;
|
||||
}
|
||||
const ResolveModes modes = PickResolveModes(device, depth_format);
|
||||
if ((aspects.depth && modes.depth == VK_RESOLVE_MODE_NONE) ||
|
||||
(aspects.stencil && modes.stencil == VK_RESOLVE_MODE_NONE)) {
|
||||
return false;
|
||||
}
|
||||
return modes.depth == modes.stencil || device.SupportsIndependentResolveNone();
|
||||
}
|
||||
|
||||
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
||||
|
||||
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
@@ -139,9 +75,7 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
if (!is_new) {
|
||||
return *pair->second;
|
||||
}
|
||||
static constexpr size_t MAX_ATTACHMENTS =
|
||||
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
|
||||
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
|
||||
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
|
||||
std::array<VkAttachmentReference, 8> references{};
|
||||
u32 num_attachments{};
|
||||
u32 num_colors{};
|
||||
@@ -175,11 +109,8 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
const VkAttachmentLoadOp depth_load_op = key.depth_stencil_clear
|
||||
? VK_ATTACHMENT_LOAD_OP_CLEAR
|
||||
: VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
const VkAttachmentStoreOp depth_store_op = key.depth_stencil_discard
|
||||
? VK_ATTACHMENT_STORE_OP_DONT_CARE
|
||||
: VK_ATTACHMENT_STORE_OP_STORE;
|
||||
descriptions.push_back(AttachmentDescription(*device, key.depth_format, key.samples,
|
||||
depth_load_op, depth_store_op));
|
||||
depth_load_op, VK_ATTACHMENT_STORE_OP_STORE));
|
||||
}
|
||||
std::array<VkAttachmentReference, 8> resolve_references{};
|
||||
const bool do_resolve_color =
|
||||
@@ -202,21 +133,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool do_resolve_depth_stencil = key.resolve_depth_stencil && has_depth &&
|
||||
key.samples != VK_SAMPLE_COUNT_1_BIT &&
|
||||
SupportsDepthStencilResolve(*device, key.depth_format);
|
||||
VkAttachmentReference depth_resolve_reference{};
|
||||
if (do_resolve_depth_stencil) {
|
||||
depth_resolve_reference = VkAttachmentReference{
|
||||
.attachment = static_cast<u32>(descriptions.size()),
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
};
|
||||
VkAttachmentDescription resolve_desc =
|
||||
AttachmentDescription(*device, key.depth_format, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE);
|
||||
resolve_desc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
descriptions.push_back(resolve_desc);
|
||||
}
|
||||
const VkSubpassDescription subpass{
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
@@ -241,92 +157,6 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT
|
||||
};
|
||||
|
||||
if (device->IsKhrCreateRenderPass2Supported()) {
|
||||
boost::container::static_vector<VkAttachmentDescription2, MAX_ATTACHMENTS> descriptions2;
|
||||
for (const VkAttachmentDescription& description : descriptions) {
|
||||
descriptions2.push_back(VkAttachmentDescription2{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
|
||||
.pNext = nullptr,
|
||||
.flags = description.flags,
|
||||
.format = description.format,
|
||||
.samples = description.samples,
|
||||
.loadOp = description.loadOp,
|
||||
.storeOp = description.storeOp,
|
||||
.stencilLoadOp = description.stencilLoadOp,
|
||||
.stencilStoreOp = description.stencilStoreOp,
|
||||
.initialLayout = description.initialLayout,
|
||||
.finalLayout = description.finalLayout,
|
||||
});
|
||||
}
|
||||
const auto promote = [](const VkAttachmentReference& reference) {
|
||||
return VkAttachmentReference2{
|
||||
.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
|
||||
.pNext = nullptr,
|
||||
.attachment = reference.attachment,
|
||||
.layout = reference.layout,
|
||||
.aspectMask = 0,
|
||||
};
|
||||
};
|
||||
std::array<VkAttachmentReference2, 8> references2{};
|
||||
std::array<VkAttachmentReference2, 8> resolve_references2{};
|
||||
for (size_t index = 0; index < references.size(); ++index) {
|
||||
references2[index] = promote(references[index]);
|
||||
resolve_references2[index] = promote(resolve_references[index]);
|
||||
}
|
||||
const VkAttachmentReference2 depth_reference2 = promote(depth_reference);
|
||||
const VkAttachmentReference2 depth_resolve_reference2 = promote(depth_resolve_reference);
|
||||
const ResolveModes resolve_modes = PickResolveModes(*device, key.depth_format);
|
||||
const VkSubpassDescriptionDepthStencilResolve depth_stencil_resolve{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE,
|
||||
.pNext = nullptr,
|
||||
.depthResolveMode = resolve_modes.depth,
|
||||
.stencilResolveMode = resolve_modes.stencil,
|
||||
.pDepthStencilResolveAttachment = &depth_resolve_reference2,
|
||||
};
|
||||
const VkSubpassDescription2 subpass2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
|
||||
.pNext = do_resolve_depth_stencil ? &depth_stencil_resolve : nullptr,
|
||||
.flags = 0,
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.viewMask = 0,
|
||||
.inputAttachmentCount = 0,
|
||||
.pInputAttachments = nullptr,
|
||||
.colorAttachmentCount = num_attachments,
|
||||
.pColorAttachments = references2.data(),
|
||||
.pResolveAttachments = do_resolve_color ? resolve_references2.data() : nullptr,
|
||||
.pDepthStencilAttachment = has_depth ? &depth_reference2 : nullptr,
|
||||
.preserveAttachmentCount = 0,
|
||||
.pPreserveAttachments = nullptr,
|
||||
};
|
||||
const VkSubpassDependency2 dependency2{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2,
|
||||
.pNext = nullptr,
|
||||
.srcSubpass = dependency.srcSubpass,
|
||||
.dstSubpass = dependency.dstSubpass,
|
||||
.srcStageMask = dependency.srcStageMask,
|
||||
.dstStageMask = dependency.dstStageMask,
|
||||
.srcAccessMask = dependency.srcAccessMask,
|
||||
.dstAccessMask = dependency.dstAccessMask,
|
||||
.dependencyFlags = dependency.dependencyFlags,
|
||||
.viewOffset = 0,
|
||||
};
|
||||
pair->second = device->GetLogical().CreateRenderPass2({
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.attachmentCount = static_cast<u32>(descriptions2.size()),
|
||||
.pAttachments = descriptions2.empty() ? nullptr : descriptions2.data(),
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass2,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &dependency2,
|
||||
.correlatedViewMaskCount = 0,
|
||||
.pCorrelatedViewMasks = nullptr,
|
||||
});
|
||||
return *pair->second;
|
||||
}
|
||||
|
||||
pair->second = device->GetLogical().CreateRenderPass({
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <mutex>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/container_hash.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
|
||||
@@ -22,11 +21,9 @@ struct RenderPassKey {
|
||||
VideoCore::Surface::PixelFormat depth_format;
|
||||
VkSampleCountFlagBits samples;
|
||||
bool resolve_color;
|
||||
bool resolve_depth_stencil;
|
||||
u32 color_clear_mask;
|
||||
bool depth_stencil_clear;
|
||||
u32 color_discard_mask;
|
||||
bool depth_stencil_discard;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -34,28 +31,17 @@ struct RenderPassKey {
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Vulkan::RenderPassKey> {
|
||||
static_assert(std::tuple_size_v<decltype(Vulkan::RenderPassKey::color_formats)> <= 8);
|
||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Invalid) <= 0xFF);
|
||||
static_assert(static_cast<u32>(VideoCore::Surface::PixelFormat::Max) <= 0xFF);
|
||||
static_assert(VK_SAMPLE_COUNT_64_BIT <= 0xFF);
|
||||
|
||||
[[nodiscard]] size_t operator()(const Vulkan::RenderPassKey& key) const noexcept {
|
||||
u64 formats = 0;
|
||||
for (size_t index = 0; index < key.color_formats.size(); ++index) {
|
||||
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
|
||||
size_t value = static_cast<size_t>(key.depth_format) << 48;
|
||||
value ^= static_cast<size_t>(key.samples) << 52;
|
||||
value ^= static_cast<size_t>(key.resolve_color) << 63;
|
||||
value ^= static_cast<size_t>(key.color_clear_mask) << 54;
|
||||
value ^= static_cast<size_t>(key.depth_stencil_clear) << 62;
|
||||
value ^= static_cast<size_t>(key.color_discard_mask) << 24;
|
||||
for (size_t i = 0; i < key.color_formats.size(); ++i) {
|
||||
value ^= static_cast<size_t>(key.color_formats[i]) << (i * 6);
|
||||
}
|
||||
const u64 state = static_cast<u64>(key.depth_format) |
|
||||
(static_cast<u64>(key.samples) << 8) |
|
||||
(static_cast<u64>(key.color_clear_mask) << 16) |
|
||||
(static_cast<u64>(key.color_discard_mask) << 24) |
|
||||
(static_cast<u64>(key.resolve_color) << 32) |
|
||||
(static_cast<u64>(key.depth_stencil_clear) << 33) |
|
||||
(static_cast<u64>(key.resolve_depth_stencil) << 34) |
|
||||
(static_cast<u64>(key.depth_stencil_discard) << 35);
|
||||
size_t seed = 0;
|
||||
Common::HashCombine(seed, formats);
|
||||
Common::HashCombine(seed, state);
|
||||
return seed;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -64,9 +50,6 @@ namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
|
||||
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
|
||||
VideoCore::Surface::PixelFormat depth_format);
|
||||
|
||||
class RenderPassCache {
|
||||
public:
|
||||
explicit RenderPassCache(const Device& device_);
|
||||
|
||||
@@ -132,7 +132,6 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass
|
||||
num_renderpass_images = framebuffer->NumImages();
|
||||
renderpass_images = framebuffer->Images();
|
||||
renderpass_image_ranges = framebuffer->ImageRanges();
|
||||
framebuffer->MarkResolveShadowsUpToDate();
|
||||
}
|
||||
|
||||
void Scheduler::RealizeDeferredClear() {
|
||||
@@ -156,10 +155,8 @@ void Scheduler::RealizeDeferredClear() {
|
||||
}
|
||||
const u32 color_discard_mask =
|
||||
dc.framebuffer->DiscardsMsaaColor() ? dc.color_clear_mask : 0u;
|
||||
const bool depth_stencil_discard =
|
||||
dc.depth_stencil && dc.framebuffer->DiscardsMsaaDepthStencil();
|
||||
const VkRenderPass renderpass = dc.framebuffer->RenderPassVariant(
|
||||
dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard);
|
||||
dc.color_clear_mask, dc.depth_stencil, color_discard_mask);
|
||||
EndRenderPass();
|
||||
BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
|
||||
}
|
||||
@@ -193,14 +190,6 @@ bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkC
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scheduler::FlushDeferredClear() {
|
||||
if (deferred_clear.framebuffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
RealizeDeferredClear();
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
|
||||
if (deferred_clear.framebuffer == framebuffer) {
|
||||
RealizeDeferredClear();
|
||||
|
||||
@@ -65,9 +65,6 @@ public:
|
||||
/// Defers a full depth/stencil clear so it becomes the next render pass.
|
||||
bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
|
||||
|
||||
/// Realizes any pending deferred clear before its framebuffer can be moved or freed.
|
||||
void FlushDeferredClear();
|
||||
|
||||
/// Requests the current execution context to be able to execute operations only allowed outside
|
||||
/// of a renderpass.
|
||||
void RequestOutsideRenderPassOperationContext();
|
||||
|
||||
@@ -33,10 +33,10 @@ constexpr VkDeviceSize MAX_ALIGNMENT = 256;
|
||||
// Windows ones however, can intake bigger buffers and generally do not OOM.
|
||||
// - GTX 960 on Windows will not OOM with 256mib
|
||||
// - GT 1030 on ^NIX will OOM with 256mib
|
||||
#if defined(__FreeBSD__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||
#else
|
||||
#if defined(_WIN32) || defined(__ANDROID__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
#else
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||
#endif
|
||||
|
||||
size_t GetStreamBufferSize(const Device& device) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,8 +60,6 @@ public:
|
||||
|
||||
void TickFrame();
|
||||
|
||||
void FlushDeferredClear();
|
||||
|
||||
u64 GetDeviceLocalMemory() const;
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
@@ -70,12 +68,6 @@ public:
|
||||
|
||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
||||
|
||||
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
|
||||
|
||||
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
|
||||
|
||||
void ReleaseMsaaScratchImage(VkImage image);
|
||||
|
||||
void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
|
||||
const Region2D& dst_region, const Region2D& src_region,
|
||||
Tegra::Engines::Fermi2D::Filter filter,
|
||||
@@ -125,20 +117,16 @@ public:
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkExtent2D extent{};
|
||||
u32 layers = 0;
|
||||
VkImageAspectFlags aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
bool up_to_date = false;
|
||||
};
|
||||
|
||||
[[nodiscard]] VkImageView GetOrCreateResolveShadow(VkImage msaa_image, VkFormat format,
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkImageAspectFlags aspect_mask);
|
||||
VkExtent2D extent, u32 layers);
|
||||
|
||||
[[nodiscard]] const ResolveShadow* GetValidResolveShadow(VkImage msaa_image) const;
|
||||
|
||||
void InvalidateResolveShadow(VkImage msaa_image);
|
||||
|
||||
void MarkResolveShadowUpToDate(VkImage msaa_image);
|
||||
|
||||
void EraseResolveShadow(VkImage msaa_image);
|
||||
|
||||
std::span<const VkFormat> ViewFormats(PixelFormat format) {
|
||||
@@ -166,30 +154,8 @@ public:
|
||||
|
||||
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
|
||||
std::array<vk::Buffer, indexing_slots> buffers{};
|
||||
struct MsaaScratchKey {
|
||||
VkFormat format;
|
||||
VkImageType type;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 levels;
|
||||
u32 layers;
|
||||
VkImageUsageFlags usage;
|
||||
VkImageCreateFlags flags;
|
||||
|
||||
bool operator==(const MsaaScratchKey&) const noexcept = default;
|
||||
};
|
||||
|
||||
struct MsaaScratchImage {
|
||||
MsaaScratchKey key;
|
||||
vk::Image image;
|
||||
u64 tick;
|
||||
u32 unused_frames;
|
||||
};
|
||||
|
||||
std::vector<MsaaScratchImage> msaa_scratch_images;
|
||||
std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
|
||||
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
|
||||
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
@@ -225,8 +191,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
|
||||
u32 color_discard_mask,
|
||||
bool depth_stencil_discard) const;
|
||||
u32 color_discard_mask) const;
|
||||
|
||||
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
|
||||
return render_area;
|
||||
@@ -268,21 +233,19 @@ public:
|
||||
return is_rescaled;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasResolveColor() const noexcept {
|
||||
return !resolve_images.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImage ResolveColorImage(size_t index) const noexcept {
|
||||
return index < resolve_images.size() ? *resolve_images[index] : VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DiscardsMsaaColor() const noexcept {
|
||||
return discard_msaa_color;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DiscardsMsaaDepthStencil() const noexcept {
|
||||
return discard_msaa_depth_stencil;
|
||||
}
|
||||
|
||||
/// Records that a render pass has begun, so its resolve attachments will hold valid contents
|
||||
/// once it ends.
|
||||
void MarkResolveShadowsUpToDate() const;
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_MEMOIZED_RENDER_PASS_VARIANTS = 8;
|
||||
|
||||
vk::Framebuffer framebuffer;
|
||||
VkRenderPass renderpass{};
|
||||
VkExtent2D render_area{};
|
||||
@@ -295,16 +258,11 @@ private:
|
||||
bool has_depth{};
|
||||
bool has_stencil{};
|
||||
bool is_rescaled{};
|
||||
std::array<VkImage, 9> resolve_shadow_images{};
|
||||
u32 num_resolve_shadows = 0;
|
||||
TextureCacheRuntime* runtime_ptr{nullptr};
|
||||
std::vector<vk::Image> resolve_images;
|
||||
std::vector<vk::ImageView> resolve_image_views;
|
||||
RenderPassKey render_pass_key{};
|
||||
RenderPassCache* render_pass_cache{nullptr};
|
||||
bool discard_msaa_color{};
|
||||
bool discard_msaa_depth_stencil{};
|
||||
mutable std::array<u32, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_keys{};
|
||||
mutable std::array<VkRenderPass, NUM_MEMOIZED_RENDER_PASS_VARIANTS> variant_render_passes{};
|
||||
mutable u32 num_memoized_variants{};
|
||||
};
|
||||
|
||||
class Image : public VideoCommon::ImageBase {
|
||||
@@ -528,7 +486,6 @@ struct TextureCacheParams {
|
||||
static constexpr bool HAS_EMULATED_COPIES = false;
|
||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||
static constexpr bool HAS_MSAA_DOWNLOADS = true;
|
||||
|
||||
using Runtime = Vulkan::TextureCacheRuntime;
|
||||
using Image = Vulkan::Image;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -121,7 +121,7 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie
|
||||
image_view_ids.push_back(image_view_id);
|
||||
}
|
||||
|
||||
bool ImageBase::IsSafeGpuCopy() const noexcept {
|
||||
bool ImageBase::IsSafeDownload() const noexcept {
|
||||
// Skip images that were not modified from the GPU
|
||||
if (False(flags & ImageFlagBits::GpuModified)) {
|
||||
return false;
|
||||
@@ -131,6 +131,10 @@ bool ImageBase::IsSafeGpuCopy() const noexcept {
|
||||
if (True(flags & ImageFlagBits::CpuModified)) {
|
||||
return false;
|
||||
}
|
||||
if (info.num_samples > 1) {
|
||||
LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -67,7 +64,7 @@ struct ImageBase {
|
||||
|
||||
void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id);
|
||||
|
||||
[[nodiscard]] bool IsSafeGpuCopy() const noexcept;
|
||||
[[nodiscard]] bool IsSafeDownload() const noexcept;
|
||||
|
||||
[[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
|
||||
const VAddr overlap_end = overlap_cpu_addr + overlap_size;
|
||||
|
||||
@@ -134,7 +134,7 @@ void TextureCache<P>::RunGarbageCollector() {
|
||||
if (True(image.flags & ImageFlagBits::IsDecoding)) {
|
||||
return false;
|
||||
}
|
||||
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
|
||||
const bool must_download = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
|
||||
if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
|
||||
return false;
|
||||
}
|
||||
@@ -577,7 +577,6 @@ FramebufferId TextureCache<P>::GetFramebufferId(const RenderTargets& key) {
|
||||
return id ? &slot_image_views[id] : nullptr;
|
||||
});
|
||||
ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr;
|
||||
runtime.FlushDeferredClear();
|
||||
framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key);
|
||||
return framebuffer_id;
|
||||
}
|
||||
@@ -595,26 +594,11 @@ void TextureCache<P>::WriteMemory(DAddr cpu_addr, size_t size) {
|
||||
});
|
||||
}
|
||||
|
||||
template <class P>
|
||||
bool TextureCache<P>::IsDownloadable(const ImageBase& image) const noexcept {
|
||||
if (!image.IsSafeGpuCopy()) {
|
||||
return false;
|
||||
}
|
||||
if (image.info.num_samples == 1) {
|
||||
return true;
|
||||
}
|
||||
if constexpr (P::HAS_MSAA_DOWNLOADS) {
|
||||
return runtime.CanDownloadMsaa(image.info);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::DownloadMemory(DAddr cpu_addr, size_t size) {
|
||||
boost::container::small_vector<ImageId, 16> images;
|
||||
ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
|
||||
if (!IsDownloadable(image)) {
|
||||
ForEachImageInRegion(cpu_addr, size, [&images](ImageId image_id, ImageBase& image) {
|
||||
if (!image.IsSafeDownload()) {
|
||||
return;
|
||||
}
|
||||
image.flags &= ~ImageFlagBits::GpuModified;
|
||||
@@ -1492,12 +1476,12 @@ template <class P>
|
||||
bool TextureCache<P>::ScaleUp(Image& image) {
|
||||
const bool has_copy = image.HasScaled();
|
||||
const bool rescaled = image.ScaleUp();
|
||||
if (!has_copy && image.HasScaled()) {
|
||||
total_used_memory += GetScaledImageSizeBytes(image);
|
||||
}
|
||||
if (!rescaled) {
|
||||
return false;
|
||||
}
|
||||
if (!has_copy) {
|
||||
total_used_memory += GetScaledImageSizeBytes(image);
|
||||
}
|
||||
InvalidateScale(image);
|
||||
return true;
|
||||
}
|
||||
@@ -1707,10 +1691,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, DA
|
||||
for (const auto& copy_object : join_copies_to_do) {
|
||||
Image& overlap = slot_images[copy_object.id];
|
||||
if (copy_object.is_alias) {
|
||||
if (!overlap.IsSafeGpuCopy()) {
|
||||
continue;
|
||||
}
|
||||
if (overlap.info.num_samples != new_image.info.num_samples) {
|
||||
if (!overlap.IsSafeDownload()) {
|
||||
continue;
|
||||
}
|
||||
const auto alias_pointer = join_alias_indices.find(copy_object.id);
|
||||
@@ -2480,7 +2461,6 @@ void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> rem
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> removed_views) {
|
||||
runtime.FlushDeferredClear();
|
||||
auto it = framebuffers.begin();
|
||||
while (it != framebuffers.end()) {
|
||||
if (it->first.Contains(removed_views)) {
|
||||
|
||||
@@ -303,8 +303,6 @@ private:
|
||||
FramebufferId GetFramebufferId(const RenderTargets& key);
|
||||
|
||||
/// Refresh the contents (pixel data) of an image
|
||||
[[nodiscard]] bool IsDownloadable(const ImageBase& image) const noexcept;
|
||||
|
||||
void RefreshContents(Image& image, ImageId image_id);
|
||||
|
||||
/// Upload data from guest to an image
|
||||
|
||||
@@ -650,6 +650,13 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
features.shader_float16_int8.shaderFloat16 = false;
|
||||
}
|
||||
|
||||
if (is_intel_windows) {
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Intel proprietary drivers do not support MSAA->MSAA image blits. "
|
||||
"MSAA scaling will use 3D helpers. MSAA resolves work normally.");
|
||||
cant_blit_msaa = true;
|
||||
}
|
||||
|
||||
has_broken_compute =
|
||||
CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) &&
|
||||
!Settings::values.enable_compute_pipelines.GetValue();
|
||||
@@ -968,12 +975,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION);
|
||||
FOR_EACH_VK_EXTENSION(EXTENSION);
|
||||
|
||||
extensions.depth_stencil_resolve =
|
||||
extensions.depth_stencil_resolve &&
|
||||
(instance_version >= VK_API_VERSION_1_2 || extensions.create_renderpass2);
|
||||
RemoveExtensionIfUnsuitable(extensions.depth_stencil_resolve,
|
||||
VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
|
||||
|
||||
if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) {
|
||||
loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
|
||||
loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME);
|
||||
@@ -1121,11 +1122,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;
|
||||
SetNext(next, properties.push_descriptor);
|
||||
}
|
||||
if (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) {
|
||||
properties.depth_stencil_resolve.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES;
|
||||
SetNext(next, properties.depth_stencil_resolve);
|
||||
}
|
||||
if (extensions.descriptor_buffer) {
|
||||
properties.descriptor_buffer.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
|
||||
|
||||
@@ -91,8 +91,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
|
||||
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
|
||||
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
|
||||
EXTENSION(KHR, CREATE_RENDERPASS_2, create_renderpass2) \
|
||||
EXTENSION(KHR, DEPTH_STENCIL_RESOLVE, depth_stencil_resolve) \
|
||||
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
|
||||
EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
|
||||
EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
|
||||
@@ -615,32 +613,6 @@ FN_MAX_LIMIT_LIST
|
||||
return extensions.shader_stencil_export;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_create_renderpass2.
|
||||
bool IsKhrCreateRenderPass2Supported() const {
|
||||
return extensions.create_renderpass2 || instance_version >= VK_API_VERSION_1_2;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_depth_stencil_resolve.
|
||||
bool IsKhrDepthStencilResolveSupported() const {
|
||||
return (extensions.depth_stencil_resolve || instance_version >= VK_API_VERSION_1_2) &&
|
||||
IsKhrCreateRenderPass2Supported();
|
||||
}
|
||||
|
||||
/// Returns the supported resolve modes for the depth aspect.
|
||||
VkResolveModeFlags GetDepthResolveModes() const {
|
||||
return properties.depth_stencil_resolve.supportedDepthResolveModes;
|
||||
}
|
||||
|
||||
/// Returns the supported resolve modes for the stencil aspect.
|
||||
VkResolveModeFlags GetStencilResolveModes() const {
|
||||
return properties.depth_stencil_resolve.supportedStencilResolveModes;
|
||||
}
|
||||
|
||||
/// Returns true if only one of the depth and stencil aspects may be resolved.
|
||||
bool SupportsIndependentResolveNone() const {
|
||||
return properties.depth_stencil_resolve.independentResolveNone == VK_TRUE;
|
||||
}
|
||||
|
||||
/// Returns true if depth/stencil operations can be performed efficiently.
|
||||
/// Either through shader export or hardware blits.
|
||||
bool CanPerformDepthStencilOperations() const {
|
||||
@@ -949,6 +921,10 @@ FN_MAX_LIMIT_LIST
|
||||
return supports_d24_depth;
|
||||
}
|
||||
|
||||
bool CantBlitMSAA() const {
|
||||
return cant_blit_msaa;
|
||||
}
|
||||
|
||||
bool MustEmulateScaledFormats() const {
|
||||
return must_emulate_scaled_formats;
|
||||
}
|
||||
@@ -1172,7 +1148,6 @@ private:
|
||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
};
|
||||
@@ -1200,6 +1175,7 @@ private:
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
bool has_radeon_gpu_profiler{}; ///< Has Radeon GPU Profiler attached.
|
||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||
bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting.
|
||||
bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation
|
||||
bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3.
|
||||
bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3.
|
||||
|
||||
@@ -184,7 +184,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCreatePipelineLayout);
|
||||
X(vkCreateQueryPool);
|
||||
X(vkCreateRenderPass);
|
||||
X(vkCreateRenderPass2);
|
||||
X(vkCreateSampler);
|
||||
X(vkCreateSemaphore);
|
||||
X(vkCreateShaderModule);
|
||||
@@ -271,10 +270,6 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
if (!dld.vkQueueSubmit2) {
|
||||
Proc(dld.vkQueueSubmit2, dld, "vkQueueSubmit2KHR", device);
|
||||
}
|
||||
|
||||
if (!dld.vkCreateRenderPass2) {
|
||||
Proc(dld.vkCreateRenderPass2, dld, "vkCreateRenderPass2KHR", device);
|
||||
}
|
||||
#undef X
|
||||
}
|
||||
|
||||
@@ -730,12 +725,6 @@ RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const {
|
||||
return RenderPass(object, handle, *dld);
|
||||
}
|
||||
|
||||
RenderPass Device::CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const {
|
||||
VkRenderPass object;
|
||||
Check(dld->vkCreateRenderPass2(handle, &ci, nullptr, &object));
|
||||
return RenderPass(object, handle, *dld);
|
||||
}
|
||||
|
||||
DescriptorSetLayout Device::CreateDescriptorSetLayout(
|
||||
const VkDescriptorSetLayoutCreateInfo& ci) const {
|
||||
VkDescriptorSetLayout object;
|
||||
|
||||
@@ -300,7 +300,6 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
|
||||
PFN_vkCreateQueryPool vkCreateQueryPool{};
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass{};
|
||||
PFN_vkCreateRenderPass2 vkCreateRenderPass2{};
|
||||
PFN_vkCreateSampler vkCreateSampler{};
|
||||
PFN_vkCreateSemaphore vkCreateSemaphore{};
|
||||
PFN_vkCreateShaderModule vkCreateShaderModule{};
|
||||
@@ -1046,8 +1045,6 @@ public:
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const;
|
||||
|
||||
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
|
||||
const VkDescriptorSetLayoutCreateInfo& ci) const;
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ UpdateDialog::~UpdateDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// TODO: migrate to a net.cpp wrapper
|
||||
void UpdateDialog::Download() {
|
||||
const auto filename = QtCommon::Frontend::GetSaveFileName(
|
||||
tr("New Version Location"),
|
||||
@@ -162,7 +161,7 @@ void UpdateDialog::Download() {
|
||||
QString::number(response.status)));
|
||||
return;
|
||||
}
|
||||
if (!response.has_header("content-type")) {
|
||||
if (!response.headers.contains("content-type")) {
|
||||
LOG_ERROR(Frontend, "GET to {}{} returned no content", m_asset.url, m_asset.path);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user