Compare commits

..

3 Commits

Author SHA1 Message Date
CamilleLaVey b96224bcf6 [vulkan] Adjustments on MSAA and BlitHelpers 2026-08-23 01:13:27 -04:00
xbzk 5b86242313 [android, ui] remove the wash (white tint) of focused game cards (#4285)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This removes the ugly wash from focused game cards, without removing the interaction's cool ripple effects.
I've stumbled upon a reddit complaining about it, and in fact i always hated it but hadn't realized yet.

It happens to any layout.
Joypad users notice more since navigation consists on focusing cards.
Carousel users notice even more coz cards are bigger and always focused.

Border is more than enough to highlight a card, and we want to cherish colors just like creator artists wanted it to be, right?

No chance of bugs. No chance of disagreement (¬¬).
Approve it already so Youtube videos with carousel thumbs get prettier earlier ^^.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4285
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-23 00:29:59 +02:00
xbzk d24e79c991 [android, ui] refresh game icons when updates are discovered (#4284)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This small impl aims to fix an android old visual bug where it could cache/read a base game icon before its update was mounted, so games with update-specific icons kept showing the base icon like almost always (in a way that some players may never have seen the updated icons).

Fix: when scan detects an update matching an already found base game, reload that game’s metadata and refresh only its visible icon if the icon actually changed.

I believe it's polished to the max. No find/scans. When base is found it is tracked via hashmap, and when update is found and conditions met the refresh is triggered.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4284
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-23 00:25:55 +02:00
30 changed files with 1658 additions and 576 deletions
@@ -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)
@@ -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;
+555 -237
View File
@@ -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();
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;
+2 -6
View File
@@ -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;
}
+4 -1
View File
@@ -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;
+27 -7
View File
@@ -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
+11 -7
View File
@@ -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;
+29 -5
View File
@@ -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;