mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-23 16:06:30 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9c59407ca | |||
| 55acf5d260 | |||
| 3e07b466eb | |||
| 5b86242313 | |||
| d24e79c991 | |||
| df05d3de23 | |||
| c249ff462b | |||
| 056d11027c | |||
| 61ffb309a3 | |||
| 65a3cfd2be | |||
| 5c54abf353 |
+7
-3
@@ -99,7 +99,12 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key)
|
||||
add(IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.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_QUEUE_TARGET.key)
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key)
|
||||
if (!BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(
|
||||
@@ -307,8 +312,6 @@ 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)
|
||||
@@ -325,6 +328,7 @@ 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,6 +18,7 @@ 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
|
||||
@@ -59,6 +60,10 @@ 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"
|
||||
@@ -168,10 +173,9 @@ class GamesFragment : Fragment() {
|
||||
|
||||
gamesViewModel.shouldScrollAfterReload.collect(viewLifecycleOwner) { shouldScroll ->
|
||||
if (shouldScroll) {
|
||||
binding.gridGames.post {
|
||||
(binding.gridGames as? CarouselRecyclerView)?.pendingScrollAfterReload = true
|
||||
gameAdapter.notifyDataSetChanged()
|
||||
}
|
||||
pendingPostReloadListSettle = true
|
||||
pendingPostReloadListSettleGeneration = gameListSubmitGeneration
|
||||
schedulePostReloadListSettle()
|
||||
gamesViewModel.setShouldScrollAfterReload(false)
|
||||
}
|
||||
}
|
||||
@@ -273,11 +277,42 @@ class GamesFragment : Fragment() {
|
||||
lastSearchText = currentSearchText
|
||||
lastFilter = currentFilter
|
||||
} else {
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(games)
|
||||
submitGameList(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()) {
|
||||
@@ -414,9 +449,7 @@ class GamesFragment : Fragment() {
|
||||
|
||||
val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
|
||||
if (searchTerm.isEmpty()) {
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(
|
||||
filteredList
|
||||
)
|
||||
submitGameList(filteredList)
|
||||
gamesViewModel.setFilteredGames(filteredList)
|
||||
return
|
||||
}
|
||||
@@ -432,7 +465,7 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
}.sortedByDescending { it.score }.map { it.item }
|
||||
|
||||
((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(sortedList)
|
||||
submitGameList(sortedList)
|
||||
gamesViewModel.setFilteredGames(sortedList)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ object GameHelper {
|
||||
|
||||
fun getGames(): List<Game> {
|
||||
val games = mutableListOf<Game>()
|
||||
val gamesByProgramId = mutableMapOf<String, Game>()
|
||||
val context = YuzuApplication.appContext
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@@ -63,6 +64,7 @@ object GameHelper {
|
||||
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(gameDirUri),
|
||||
scanDepth,
|
||||
mountedContainerUris
|
||||
@@ -136,6 +138,7 @@ object GameHelper {
|
||||
|
||||
private fun addGamesRecursive(
|
||||
games: MutableList<Game>,
|
||||
gamesByProgramId: MutableMap<String, Game>,
|
||||
files: Array<MinimalDocumentFile>,
|
||||
depth: Int,
|
||||
mountedContainerUris: MutableSet<String>
|
||||
@@ -148,6 +151,7 @@ object GameHelper {
|
||||
if (it.isDirectory) {
|
||||
addGamesRecursive(
|
||||
games,
|
||||
gamesByProgramId,
|
||||
FileUtil.listFiles(it.uri),
|
||||
depth - 1,
|
||||
mountedContainerUris
|
||||
@@ -156,8 +160,9 @@ object GameHelper {
|
||||
val extension = FileUtil.getExtension(it.uri).lowercase()
|
||||
val filePath = it.uri.toString()
|
||||
|
||||
if (externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)) {
|
||||
val mountedContainer = externalContentExtensions.contains(extension) &&
|
||||
mountedContainerUris.add(filePath)
|
||||
if (mountedContainer) {
|
||||
NativeLibrary.addGameFolderFileToFilesystemProvider(filePath)
|
||||
}
|
||||
|
||||
@@ -165,6 +170,20 @@ 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,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -24,6 +27,15 @@ 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,
|
||||
@@ -31,14 +43,15 @@ class GameIconFetcher(
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
return DrawableResult(
|
||||
drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
|
||||
drawable = decodeGameIcon(game)!!.toDrawable(options.context.resources),
|
||||
isSampled = false,
|
||||
dataSource = DataSource.DISK
|
||||
)
|
||||
}
|
||||
|
||||
private fun decodeGameIcon(uri: String): Bitmap? {
|
||||
val data = GameMetadata.getIcon(uri)
|
||||
private fun decodeGameIcon(game: Game): Bitmap? {
|
||||
val data = GameMetadata.getIcon(game.path)
|
||||
gameIconHashes[game.iconCacheKey()] = data.contentHashCode()
|
||||
return BitmapFactory.decodeByteArray(
|
||||
data,
|
||||
0,
|
||||
@@ -54,7 +67,7 @@ class GameIconFetcher(
|
||||
}
|
||||
|
||||
class GameIconKeyer : Keyer<Game> {
|
||||
override fun key(data: Game, options: Options): String = data.path
|
||||
override fun key(data: Game, options: Options): String = data.iconCacheKey()
|
||||
}
|
||||
|
||||
object GameIconUtils {
|
||||
@@ -71,14 +84,58 @@ 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 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.ui
|
||||
@@ -11,6 +11,8 @@ 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
|
||||
@@ -34,6 +36,7 @@ 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"
|
||||
@@ -42,8 +45,13 @@ 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_CARDS_SCALING_SHAPE = "CarouselCardsScalingShape"
|
||||
private const val CAROUSEL_CARDS_ALPHA_SHAPE = "CarouselCardsAlphaShape"
|
||||
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
|
||||
const val CAROUSEL_LAST_SCROLL_POSITION = "CarouselLastScrollPosition"
|
||||
const val CAROUSEL_VIEW_TYPE_PORTRAIT = "GamesViewTypePortrait"
|
||||
const val CAROUSEL_VIEW_TYPE_LANDSCAPE = "GamesViewTypeLandscape"
|
||||
@@ -160,46 +168,52 @@ 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
|
||||
}
|
||||
child.layoutParams.width = cardSize
|
||||
child.layoutParams.height = cardSize
|
||||
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
|
||||
|
||||
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 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 shapedScaling = 1f - depthInput
|
||||
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,
|
||||
@@ -209,15 +223,12 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
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)
|
||||
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)
|
||||
|
||||
child.animate().cancel()
|
||||
child.alpha = alpha
|
||||
child.scaleX = scale
|
||||
child.scaleY = scale
|
||||
@@ -273,7 +284,9 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
return (userFactor * (height - bottomInset)).toInt()
|
||||
val scaledHeight = height * userFactor
|
||||
val availableHeight = height - bottomInset
|
||||
return minOf(scaledHeight.toInt(), availableHeight.toInt())
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
@@ -282,6 +295,13 @@ 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
|
||||
|
||||
@@ -336,6 +356,12 @@ 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)
|
||||
@@ -344,6 +370,7 @@ 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
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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,6 +10,7 @@
|
||||
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,6 +11,7 @@
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardPreventCornerOverlap="true"
|
||||
android:clipChildren="true"
|
||||
app:rippleColor="@color/game_card_ripple"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
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,6 +22,7 @@
|
||||
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,6 +12,7 @@
|
||||
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,8 +5,6 @@
|
||||
<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">Select how video decoding (NVDEC) is handled during cutscenes and intros.</string>
|
||||
<string name="nvdec_emulation_description">Change to CPU if a crash occurs on cinematics.</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>
|
||||
|
||||
@@ -241,7 +241,7 @@ std::optional<std::string> MakeRequest(const std::string& url, const std::string
|
||||
response.status);
|
||||
return {};
|
||||
}
|
||||
if (!response.headers.contains("content-type")) {
|
||||
if (!response.has_header("content-type")) {
|
||||
LOG_ERROR(Common, "GET to {}{} returned no content", url, path);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -17,56 +17,12 @@
|
||||
|
||||
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);
|
||||
// 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();
|
||||
std::string msg_buffer(len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,33 +5,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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"} {
|
||||
: ServiceFramework{system_, "IShopServiceAsync"},
|
||||
service_context{system_, "IShopServiceAsync"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "Cancel"},
|
||||
{1, nullptr, "GetSize"},
|
||||
{2, nullptr, "Read"},
|
||||
{3, nullptr, "GetErrorCode"},
|
||||
{4, nullptr, "Request"},
|
||||
{5, nullptr, "Prepare"},
|
||||
{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"},
|
||||
};
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,11 +152,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateAsyncInterface(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_NIM, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
void CreateAsyncInterface(HLERequestContext& ctx) {LOG_DEBUG(Service_NIM, "called");
|
||||
auto async_interface = std::make_shared<IShopServiceAsync>(system);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, system);
|
||||
rb.PushCopyObjects(ctx, async_interface->GetEvent());
|
||||
rb.PushIpcInterface<IShopServiceAsync>(ctx, std::move(async_interface));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 skfd, char* ifname, char* args[], int count) -> int {
|
||||
iw_enum_devices(sock, [](int f_skfd, char* f_ifname, char* f_args[], int) -> int {
|
||||
iwrange range;
|
||||
int res = iw_get_range_info(skfd, ifname, &range);
|
||||
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", ifname, res);
|
||||
int res = iw_get_range_info(f_skfd, f_ifname, &range);
|
||||
LOG_INFO(Network, "ifname {} returned {} on iw_get_range_info", f_ifname, res);
|
||||
if (res >= 0) {
|
||||
strncpy(args[0], ifname, IFNAMSIZ - 1);
|
||||
args[0][IFNAMSIZ - 1] = 0;
|
||||
strncpy(f_args[0], f_ifname, IFNAMSIZ - 1);
|
||||
f_args[0][IFNAMSIZ - 1] = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -18,6 +18,7 @@ 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
|
||||
@@ -30,8 +31,12 @@ 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
|
||||
@@ -163,6 +168,44 @@ 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})
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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), 0).r;
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).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), 0).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), 0).r);
|
||||
gl_FragDepth = texelFetch(depth_tex, ivec2(texcoord), gl_SampleID).r;
|
||||
gl_FragStencilRefARB = int(texelFetch(stencil_tex, ivec2(texcoord), gl_SampleID).r);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2DMS msaa_in;
|
||||
#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(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
@@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants {
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
||||
|
||||
void main() {
|
||||
const ivec2 coord = ivec2(gl_FragCoord.xy) - dst_offset + src_offset;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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,7 +3,14 @@
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2D img_in;
|
||||
#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(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
@@ -11,7 +18,7 @@ layout(push_constant) uniform PushConstants {
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 frag_color;
|
||||
layout(location = 0) out TEXEL_TYPE frag_color;
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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,6 +75,8 @@ public:
|
||||
|
||||
void Finish();
|
||||
|
||||
void FlushDeferredClear() {}
|
||||
|
||||
StagingBufferMap UploadStagingBuffer(size_t size, bool deferred = false);
|
||||
|
||||
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
|
||||
@@ -370,6 +372,7 @@ 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,6 +12,7 @@
|
||||
#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"
|
||||
@@ -21,7 +22,15 @@
|
||||
#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"
|
||||
@@ -518,8 +527,19 @@ 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) {
|
||||
VkFormat format, u32 base_level, u32 base_layer,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
return device.CreateImageView(VkImageViewCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -534,10 +554,10 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
|
||||
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
},
|
||||
.subresourceRange{
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = base_level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.baseArrayLayer = base_layer,
|
||||
.layerCount = 1,
|
||||
},
|
||||
});
|
||||
@@ -586,12 +606,17 @@ 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)
|
||||
@@ -609,7 +634,25 @@ 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>)) {}
|
||||
|
||||
@@ -624,24 +667,13 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
|
||||
.renderpass = dst_framebuffer->RenderPass(),
|
||||
.operation = operation,
|
||||
};
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView src_image_view,
|
||||
@@ -670,6 +702,38 @@ 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view, const Region2D& dst_region,
|
||||
const Region2D& src_region) {
|
||||
@@ -677,24 +741,34 @@ void BlitImageHelper::BlitColorMSAA(const Framebuffer* dst_framebuffer,
|
||||
.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);
|
||||
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);
|
||||
}
|
||||
|
||||
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::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,
|
||||
@@ -702,36 +776,14 @@ void BlitImageHelper::ResolveDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
const Region2D& src_region) {
|
||||
const bool resolve_stencil =
|
||||
dst_framebuffer->HasAspectStencilBit() && device.IsExtShaderStencilExportSupported();
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
@@ -739,35 +791,23 @@ 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,
|
||||
};
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
||||
@@ -882,19 +922,183 @@ 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;
|
||||
const VkSampleCountFlagBits samples =
|
||||
msaa_to_non_msaa ? VK_SAMPLE_COUNT_1_BIT : SampleCountFlag(num_samples);
|
||||
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.color_formats[0] = dst_format;
|
||||
@@ -905,149 +1109,35 @@ 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;
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
@@ -1379,6 +1469,87 @@ 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;
|
||||
@@ -1417,15 +1588,83 @@ 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);
|
||||
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);
|
||||
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 VkPipelineMultisampleStateCreateInfo multisample_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -1462,6 +1701,85 @@ 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,12 +45,19 @@ 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 {
|
||||
@@ -78,6 +85,12 @@ 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);
|
||||
|
||||
@@ -116,7 +129,39 @@ 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);
|
||||
|
||||
@@ -131,7 +176,13 @@ 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);
|
||||
|
||||
@@ -162,10 +213,12 @@ 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;
|
||||
@@ -179,7 +232,15 @@ 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;
|
||||
|
||||
@@ -193,8 +254,18 @@ 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,24 +424,12 @@ 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;
|
||||
@@ -497,6 +485,22 @@ 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,8 +65,72 @@ 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) {
|
||||
@@ -75,7 +139,9 @@ VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||
if (!is_new) {
|
||||
return *pair->second;
|
||||
}
|
||||
boost::container::static_vector<VkAttachmentDescription, 9> descriptions;
|
||||
static constexpr size_t MAX_ATTACHMENTS =
|
||||
2 * std::tuple_size_v<decltype(RenderPassKey::color_formats)> + 2;
|
||||
boost::container::static_vector<VkAttachmentDescription, MAX_ATTACHMENTS> descriptions;
|
||||
std::array<VkAttachmentReference, 8> references{};
|
||||
u32 num_attachments{};
|
||||
u32 num_colors{};
|
||||
@@ -109,8 +175,11 @@ 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, VK_ATTACHMENT_STORE_OP_STORE));
|
||||
depth_load_op, depth_store_op));
|
||||
}
|
||||
std::array<VkAttachmentReference, 8> resolve_references{};
|
||||
const bool do_resolve_color =
|
||||
@@ -133,6 +202,21 @@ 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,
|
||||
@@ -157,6 +241,92 @@ 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,6 +9,7 @@
|
||||
#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"
|
||||
|
||||
@@ -21,9 +22,11 @@ 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
|
||||
@@ -31,17 +34,28 @@ 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 {
|
||||
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);
|
||||
u64 formats = 0;
|
||||
for (size_t index = 0; index < key.color_formats.size(); ++index) {
|
||||
formats |= static_cast<u64>(key.color_formats[index]) << (index * 8);
|
||||
}
|
||||
return value;
|
||||
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;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -50,6 +64,9 @@ namespace Vulkan {
|
||||
|
||||
class Device;
|
||||
|
||||
[[nodiscard]] bool SupportsDepthStencilResolve(const Device& device,
|
||||
VideoCore::Surface::PixelFormat depth_format);
|
||||
|
||||
class RenderPassCache {
|
||||
public:
|
||||
explicit RenderPassCache(const Device& device_);
|
||||
|
||||
@@ -132,6 +132,7 @@ 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() {
|
||||
@@ -155,8 +156,10 @@ 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);
|
||||
dc.color_clear_mask, dc.depth_stencil, color_discard_mask, depth_stencil_discard);
|
||||
EndRenderPass();
|
||||
BeginRenderPassImpl(dc.framebuffer, renderpass, clear_values.data(), count);
|
||||
}
|
||||
@@ -190,6 +193,14 @@ 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,6 +65,9 @@ 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(_WIN32) || defined(__ANDROID__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
#else
|
||||
#if defined(__FreeBSD__)
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
|
||||
#else
|
||||
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
|
||||
#endif
|
||||
|
||||
size_t GetStreamBufferSize(const Device& device) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,8 @@ public:
|
||||
|
||||
void TickFrame();
|
||||
|
||||
void FlushDeferredClear();
|
||||
|
||||
u64 GetDeviceLocalMemory() const;
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
@@ -68,6 +70,12 @@ 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,
|
||||
@@ -117,16 +125,20 @@ 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);
|
||||
VkExtent2D extent, u32 layers,
|
||||
VkImageAspectFlags aspect_mask);
|
||||
|
||||
[[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) {
|
||||
@@ -154,8 +166,30 @@ public:
|
||||
|
||||
static constexpr size_t indexing_slots = 8 * sizeof(size_t);
|
||||
std::array<vk::Buffer, indexing_slots> buffers{};
|
||||
std::vector<std::pair<u64, vk::Image>> pending_msaa_images;
|
||||
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;
|
||||
ankerl::unordered_dense::map<VkImage, ResolveShadow> resolve_shadows;
|
||||
std::vector<std::pair<u64, ResolveShadow>> pending_resolve_shadows;
|
||||
};
|
||||
|
||||
class Framebuffer {
|
||||
@@ -191,7 +225,8 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]] VkRenderPass RenderPassVariant(u32 color_clear_mask, bool depth_stencil_clear,
|
||||
u32 color_discard_mask) const;
|
||||
u32 color_discard_mask,
|
||||
bool depth_stencil_discard) const;
|
||||
|
||||
[[nodiscard]] VkExtent2D RenderArea() const noexcept {
|
||||
return render_area;
|
||||
@@ -233,19 +268,21 @@ 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{};
|
||||
@@ -258,11 +295,16 @@ private:
|
||||
bool has_depth{};
|
||||
bool has_stencil{};
|
||||
bool is_rescaled{};
|
||||
std::vector<vk::Image> resolve_images;
|
||||
std::vector<vk::ImageView> resolve_image_views;
|
||||
std::array<VkImage, 9> resolve_shadow_images{};
|
||||
u32 num_resolve_shadows = 0;
|
||||
TextureCacheRuntime* runtime_ptr{nullptr};
|
||||
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 {
|
||||
@@ -486,6 +528,7 @@ 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 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
|
||||
@@ -121,7 +121,7 @@ void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_vie
|
||||
image_view_ids.push_back(image_view_id);
|
||||
}
|
||||
|
||||
bool ImageBase::IsSafeDownload() const noexcept {
|
||||
bool ImageBase::IsSafeGpuCopy() const noexcept {
|
||||
// Skip images that were not modified from the GPU
|
||||
if (False(flags & ImageFlagBits::GpuModified)) {
|
||||
return false;
|
||||
@@ -131,10 +131,6 @@ bool ImageBase::IsSafeDownload() 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,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
|
||||
|
||||
@@ -64,7 +67,7 @@ struct ImageBase {
|
||||
|
||||
void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id);
|
||||
|
||||
[[nodiscard]] bool IsSafeDownload() const noexcept;
|
||||
[[nodiscard]] bool IsSafeGpuCopy() 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 = image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
|
||||
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
|
||||
if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
|
||||
return false;
|
||||
}
|
||||
@@ -577,6 +577,7 @@ 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;
|
||||
}
|
||||
@@ -594,11 +595,26 @@ 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, [&images](ImageId image_id, ImageBase& image) {
|
||||
if (!image.IsSafeDownload()) {
|
||||
ForEachImageInRegion(cpu_addr, size, [this, &images](ImageId image_id, ImageBase& image) {
|
||||
if (!IsDownloadable(image)) {
|
||||
return;
|
||||
}
|
||||
image.flags &= ~ImageFlagBits::GpuModified;
|
||||
@@ -1476,12 +1492,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;
|
||||
}
|
||||
@@ -1691,7 +1707,10 @@ 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.IsSafeDownload()) {
|
||||
if (!overlap.IsSafeGpuCopy()) {
|
||||
continue;
|
||||
}
|
||||
if (overlap.info.num_samples != new_image.info.num_samples) {
|
||||
continue;
|
||||
}
|
||||
const auto alias_pointer = join_alias_indices.find(copy_object.id);
|
||||
@@ -2461,6 +2480,7 @@ 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,6 +303,8 @@ 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,13 +650,6 @@ 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();
|
||||
@@ -975,6 +968,12 @@ 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);
|
||||
@@ -1122,6 +1121,11 @@ 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,6 +91,8 @@ 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) \
|
||||
@@ -613,6 +615,32 @@ 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 {
|
||||
@@ -921,10 +949,6 @@ FN_MAX_LIMIT_LIST
|
||||
return supports_d24_depth;
|
||||
}
|
||||
|
||||
bool CantBlitMSAA() const {
|
||||
return cant_blit_msaa;
|
||||
}
|
||||
|
||||
bool MustEmulateScaledFormats() const {
|
||||
return must_emulate_scaled_formats;
|
||||
}
|
||||
@@ -1148,6 +1172,7 @@ private:
|
||||
VkPhysicalDeviceSubgroupSizeControlProperties subgroup_size_control{};
|
||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
};
|
||||
@@ -1175,7 +1200,6 @@ 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,6 +184,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
|
||||
X(vkCreatePipelineLayout);
|
||||
X(vkCreateQueryPool);
|
||||
X(vkCreateRenderPass);
|
||||
X(vkCreateRenderPass2);
|
||||
X(vkCreateSampler);
|
||||
X(vkCreateSemaphore);
|
||||
X(vkCreateShaderModule);
|
||||
@@ -270,6 +271,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -725,6 +730,12 @@ 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,6 +300,7 @@ struct DeviceDispatch : InstanceDispatch {
|
||||
PFN_vkCreatePipelineLayout vkCreatePipelineLayout{};
|
||||
PFN_vkCreateQueryPool vkCreateQueryPool{};
|
||||
PFN_vkCreateRenderPass vkCreateRenderPass{};
|
||||
PFN_vkCreateRenderPass2 vkCreateRenderPass2{};
|
||||
PFN_vkCreateSampler vkCreateSampler{};
|
||||
PFN_vkCreateSemaphore vkCreateSemaphore{};
|
||||
PFN_vkCreateShaderModule vkCreateShaderModule{};
|
||||
@@ -1045,6 +1046,8 @@ public:
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
|
||||
|
||||
[[nodiscard]] RenderPass CreateRenderPass2(const VkRenderPassCreateInfo2& ci) const;
|
||||
|
||||
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
|
||||
const VkDescriptorSetLayoutCreateInfo& ci) const;
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ UpdateDialog::~UpdateDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// TODO: migrate to a net.cpp wrapper
|
||||
void UpdateDialog::Download() {
|
||||
const auto filename = QtCommon::Frontend::GetSaveFileName(
|
||||
tr("New Version Location"),
|
||||
@@ -161,7 +162,7 @@ void UpdateDialog::Download() {
|
||||
QString::number(response.status)));
|
||||
return;
|
||||
}
|
||||
if (!response.headers.contains("content-type")) {
|
||||
if (!response.has_header("content-type")) {
|
||||
LOG_ERROR(Frontend, "GET to {}{} returned no content", m_asset.url, m_asset.path);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user