mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-06 22:10:49 +00:00
Some corrections towards HCR + Discard MSAA
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -7,8 +10,10 @@ layout (push_constant) uniform PushConstants {
|
||||
vec4 clear_color;
|
||||
};
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
layout(location = 0) out vec4 colors[8];
|
||||
|
||||
void main() {
|
||||
color = clear_color;
|
||||
for (int index = 0; index < 8; ++index) {
|
||||
colors[index] = clear_color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +279,44 @@ constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_ONLY_STATE_CREATE
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
|
||||
constexpr VkPipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.depthTestEnable = VK_FALSE,
|
||||
.depthWriteEnable = VK_FALSE,
|
||||
.depthCompareOp = VK_COMPARE_OP_ALWAYS,
|
||||
.depthBoundsTestEnable = VK_FALSE,
|
||||
.stencilTestEnable = VK_FALSE,
|
||||
.front = VkStencilOpState{},
|
||||
.back = VkStencilOpState{},
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
|
||||
using BlendAttachments = std::array<VkPipelineColorBlendAttachmentState, 8>;
|
||||
|
||||
[[nodiscard]] VkPipelineColorBlendStateCreateInfo MakeColorBlendState(
|
||||
const BlendAttachments& attachments, const RenderingFormats& formats) {
|
||||
return VkPipelineColorBlendStateCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_CLEAR,
|
||||
.attachmentCount = formats.num_colors,
|
||||
.pAttachments = attachments.data(),
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] VkPipelineMultisampleStateCreateInfo MakeMultisampleState(
|
||||
VkSampleCountFlagBits samples) {
|
||||
VkPipelineMultisampleStateCreateInfo multisample_ci = PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisample_ci.rasterizationSamples = samples;
|
||||
return multisample_ci;
|
||||
}
|
||||
|
||||
template <VkFilter filter>
|
||||
inline constexpr VkSamplerCreateInfo SAMPLER_CREATE_INFO{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
@@ -628,6 +666,8 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, const ImageV
|
||||
const BlitImagePipelineKey key{
|
||||
.formats = dst_framebuffer->Formats(),
|
||||
.operation = operation,
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
.target = 0,
|
||||
};
|
||||
VkSampler sampler = *nearest_sampler;
|
||||
if (is_linear) {
|
||||
@@ -645,6 +685,8 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
|
||||
const BlitImagePipelineKey key{
|
||||
.formats = dst_framebuffer->Formats(),
|
||||
.operation = Tegra::Engines::Fermi2D::Operation::SrcCopy,
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
.target = 0,
|
||||
};
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkPipeline pipeline = FindOrEmplaceColorPipeline(key);
|
||||
@@ -763,6 +805,8 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
const BlitImagePipelineKey key{
|
||||
.formats = dst_framebuffer->Formats(),
|
||||
.operation = operation,
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
.target = 0,
|
||||
};
|
||||
VkPipeline pipeline{};
|
||||
VkImageView src_stencil_view = VK_NULL_HANDLE;
|
||||
@@ -778,26 +822,22 @@ void BlitImageHelper::BlitDepthStencil(const Framebuffer* dst_framebuffer,
|
||||
|
||||
void BlitImageHelper::ConvertD32ToR32(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertDepthToColorPipeline(convert_d32_to_r32_pipeline, dst_framebuffer->Formats());
|
||||
Convert(*convert_d32_to_r32_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_depth_to_float_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertR32ToD32(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertColorToDepthPipeline(convert_r32_to_d32_pipeline, dst_framebuffer->Formats());
|
||||
Convert(*convert_r32_to_d32_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_float_to_depth_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD16ToR16(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertDepthToColorPipeline(convert_d16_to_r16_pipeline, dst_framebuffer->Formats());
|
||||
Convert(*convert_d16_to_r16_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_depth_to_float_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertR16ToD16(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertColorToDepthPipeline(convert_r16_to_d16_pipeline, dst_framebuffer->Formats());
|
||||
Convert(*convert_r16_to_d16_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_float_to_depth_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
||||
@@ -807,45 +847,37 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
||||
LOG_WARNING(Render_Vulkan, "ConvertABGR8ToD24S8 requires shader_stencil_export, skipping");
|
||||
return;
|
||||
}
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d24s8_pipeline, dst_framebuffer->Formats(),
|
||||
convert_abgr8_to_d24s8_frag);
|
||||
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_abgr8_to_d24s8_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->Formats(),
|
||||
convert_abgr8_to_d32f_frag);
|
||||
Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
|
||||
Convert(*convert_abgr8_to_d32f_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->Formats(),
|
||||
convert_d32f_to_abgr8_frag);
|
||||
ConvertDepthStencil(*convert_d32f_to_abgr8_pipeline, dst_framebuffer, src_image_view);
|
||||
ConvertDepthStencil(*convert_d32f_to_abgr8_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_d24s8_to_abgr8_pipeline, dst_framebuffer->Formats(),
|
||||
convert_d24s8_to_abgr8_frag);
|
||||
ConvertDepthStencil(*convert_d24s8_to_abgr8_pipeline, dst_framebuffer, src_image_view);
|
||||
ConvertDepthStencil(*convert_d24s8_to_abgr8_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_s8d24_to_abgr8_pipeline, dst_framebuffer->Formats(),
|
||||
convert_s8d24_to_abgr8_frag);
|
||||
ConvertDepthStencil(*convert_s8d24_to_abgr8_pipeline, dst_framebuffer, src_image_view);
|
||||
ConvertDepthStencil(*convert_s8d24_to_abgr8_frag, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
|
||||
void BlitImageHelper::ClearColor(const Framebuffer* dst_framebuffer, u32 target, u8 color_mask,
|
||||
const std::array<f32, 4>& clear_color,
|
||||
const Region2D& dst_region) {
|
||||
const BlitImagePipelineKey key{
|
||||
.formats = dst_framebuffer->Formats(),
|
||||
.operation = Tegra::Engines::Fermi2D::Operation::BlendPremult,
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
.target = target,
|
||||
};
|
||||
const VkPipeline pipeline = FindOrEmplaceClearColorPipeline(key);
|
||||
const VkPipelineLayout layout = *clear_color_pipeline_layout;
|
||||
@@ -873,6 +905,7 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool
|
||||
.stencil_mask = stencil_mask,
|
||||
.stencil_compare_mask = stencil_compare_mask,
|
||||
.stencil_ref = stencil_ref,
|
||||
.samples = dst_framebuffer->Samples(),
|
||||
};
|
||||
const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key);
|
||||
const VkPipelineLayout layout = *clear_color_pipeline_layout;
|
||||
@@ -1079,9 +1112,11 @@ void BlitImageHelper::CopyMSAA(VkImage dst_image, VideoCore::Surface::PixelForma
|
||||
aspect_info, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
void BlitImageHelper::Convert(VkShaderModule module, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
const VkPipelineLayout layout = *one_texture_pipeline_layout;
|
||||
const VkPipeline pipeline =
|
||||
FindOrEmplaceConvertPipeline(dst_framebuffer->Formats(), module, layout);
|
||||
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
const VkExtent2D extent = GetConversionExtent(src_image_view);
|
||||
@@ -1128,9 +1163,12 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
|
||||
scheduler.InvalidateState();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
void BlitImageHelper::ConvertDepthStencil(VkShaderModule module,
|
||||
const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
const VkPipelineLayout layout = *two_textures_pipeline_layout;
|
||||
const VkPipeline pipeline =
|
||||
FindOrEmplaceConvertPipeline(dst_framebuffer->Formats(), module, layout);
|
||||
const VkImageView src_depth_view = src_image_view.DepthView();
|
||||
const VkImageView src_stencil_view = src_image_view.StencilView();
|
||||
const VkSampler sampler = *nearest_sampler;
|
||||
@@ -1187,28 +1225,12 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
|
||||
blit_color_keys.push_back(key);
|
||||
|
||||
const std::array stages = MakeStages(*full_screen_vert, *blit_color_to_color_frag);
|
||||
const VkPipelineColorBlendAttachmentState blend_attachment{
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
BlendAttachments blend_attachments{};
|
||||
blend_attachments[key.target] = PIPELINE_COLOR_BLEND_ATTACHMENT_STATE;
|
||||
// TODO: programmable blending
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_CLEAR,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &blend_attachment,
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_create_info =
|
||||
MakeColorBlendState(blend_attachments, key.formats);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo();
|
||||
blit_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
@@ -1222,8 +1244,8 @@ VkPipeline BlitImageHelper::FindOrEmplaceColorPipeline(const BlitImagePipelineKe
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO,
|
||||
.pColorBlendState = &color_blend_create_info,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *one_texture_pipeline_layout,
|
||||
@@ -1241,6 +1263,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
||||
}
|
||||
blit_depth_stencil_keys.push_back(key);
|
||||
const std::array stages = MakeStages(*full_screen_vert, *blit_depth_stencil_frag);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo();
|
||||
blit_depth_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
@@ -1254,7 +1277,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceDepthStencilPipeline(const BlitImagePip
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
@@ -1284,16 +1307,11 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
|
||||
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_state_generic_create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_CLEAR,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_blend_attachment_state,
|
||||
.blendConstants = {0.0f, 0.0f, 0.0f, 0.0f},
|
||||
};
|
||||
BlendAttachments blend_attachments{};
|
||||
blend_attachments[key.target] = color_blend_attachment_state;
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_create_info =
|
||||
MakeColorBlendState(blend_attachments, key.formats);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo();
|
||||
clear_color_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
@@ -1307,9 +1325,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearColorPipeline(const BlitImagePipel
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.pColorBlendState = &color_blend_state_generic_create_info,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_DISABLED_CREATE_INFO,
|
||||
.pColorBlendState = &color_blend_create_info,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *clear_color_pipeline_layout,
|
||||
.subpass = 0,
|
||||
@@ -1350,6 +1368,10 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
|
||||
.minDepthBounds = 0.0f,
|
||||
.maxDepthBounds = 0.0f,
|
||||
};
|
||||
const BlendAttachments blend_attachments{};
|
||||
const VkPipelineColorBlendStateCreateInfo color_blend_create_info =
|
||||
MakeColorBlendState(blend_attachments, key.formats);
|
||||
const VkPipelineMultisampleStateCreateInfo multisample_ci = MakeMultisampleState(key.samples);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = key.formats.CreateInfo();
|
||||
clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
@@ -1363,9 +1385,9 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline(
|
||||
.pTessellationState = nullptr,
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &multisample_ci,
|
||||
.pDepthStencilState = &depth_stencil_ci,
|
||||
.pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
|
||||
.pColorBlendState = &color_blend_create_info,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *clear_color_pipeline_layout,
|
||||
.subpass = 0,
|
||||
@@ -1716,26 +1738,30 @@ VkPipeline BlitImageHelper::FindOrEmplaceMSAACopyDepthPipeline(const MSAACopyPip
|
||||
return *pipelines.back();
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertDepthToColorPipeline(vk::Pipeline& pipeline,
|
||||
const RenderingFormats& formats) {
|
||||
ConvertPipeline(pipeline, formats, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertColorToDepthPipeline(vk::Pipeline& pipeline,
|
||||
const RenderingFormats& formats) {
|
||||
ConvertPipeline(pipeline, formats, true);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
vk::ShaderModule& module, bool single_texture,
|
||||
bool is_target_depth) {
|
||||
if (pipeline) {
|
||||
return;
|
||||
VkPipeline BlitImageHelper::FindOrEmplaceConvertPipeline(const RenderingFormats& formats,
|
||||
VkShaderModule module,
|
||||
VkPipelineLayout layout) {
|
||||
const ConvertPipelineKey key{
|
||||
.formats = formats,
|
||||
.module = module,
|
||||
};
|
||||
const auto it = std::ranges::find(convert_keys, key);
|
||||
if (it != convert_keys.end()) {
|
||||
return *convert_pipelines[std::distance(convert_keys.begin(), it)];
|
||||
}
|
||||
const std::array stages = MakeStages(*full_screen_vert, *module);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
convert_keys.push_back(key);
|
||||
const VkPipelineDepthStencilStateCreateInfo* depth_stencil_ci = nullptr;
|
||||
const VkPipelineColorBlendStateCreateInfo* color_blend_ci =
|
||||
&PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO;
|
||||
if (formats.num_colors == 0) {
|
||||
depth_stencil_ci = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
color_blend_ci = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO;
|
||||
}
|
||||
const std::array stages = MakeStages(*full_screen_vert, module);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci =
|
||||
GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo();
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
|
||||
convert_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = &rendering_ci,
|
||||
.flags = 0,
|
||||
@@ -1747,60 +1773,15 @@ void BlitImageHelper::ConvertPipelineEx(vk::Pipeline& pipeline, const RenderingF
|
||||
.pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.pDepthStencilState = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
|
||||
.pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
|
||||
: &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
|
||||
.pDepthStencilState = depth_stencil_ci,
|
||||
.pColorBlendState = color_blend_ci,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = single_texture ? *one_texture_pipeline_layout : *two_textures_pipeline_layout,
|
||||
.layout = layout,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineColorTargetEx(vk::Pipeline& pipeline,
|
||||
const RenderingFormats& formats,
|
||||
vk::ShaderModule& module) {
|
||||
ConvertPipelineEx(pipeline, formats, module, false, false);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline,
|
||||
const RenderingFormats& formats,
|
||||
vk::ShaderModule& module) {
|
||||
ConvertPipelineEx(pipeline, formats, module, true, true);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertPipeline(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
bool is_target_depth) {
|
||||
if (pipeline) {
|
||||
return;
|
||||
}
|
||||
VkShaderModule frag_shader =
|
||||
is_target_depth ? *convert_float_to_depth_frag : *convert_depth_to_float_frag;
|
||||
const std::array stages = MakeStages(*full_screen_vert, frag_shader);
|
||||
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device);
|
||||
const VkPipelineRenderingCreateInfo rendering_ci = formats.CreateInfo();
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline(VkGraphicsPipelineCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.pNext = &rendering_ci,
|
||||
.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 = is_target_depth ? &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO : nullptr,
|
||||
.pColorBlendState = is_target_depth ? &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO
|
||||
: &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO,
|
||||
.pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.layout = *one_texture_pipeline_layout,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = 0,
|
||||
});
|
||||
}));
|
||||
return *convert_pipelines.back();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -33,6 +33,8 @@ struct BlitImagePipelineKey {
|
||||
|
||||
RenderingFormats formats;
|
||||
Tegra::Engines::Fermi2D::Operation operation;
|
||||
VkSampleCountFlagBits samples;
|
||||
u32 target;
|
||||
};
|
||||
|
||||
struct BlitDepthStencilPipelineKey {
|
||||
@@ -43,6 +45,14 @@ struct BlitDepthStencilPipelineKey {
|
||||
u8 stencil_mask;
|
||||
u32 stencil_compare_mask;
|
||||
u32 stencil_ref;
|
||||
VkSampleCountFlagBits samples;
|
||||
};
|
||||
|
||||
struct ConvertPipelineKey {
|
||||
constexpr auto operator<=>(const ConvertPipelineKey&) const noexcept = default;
|
||||
|
||||
RenderingFormats formats;
|
||||
VkShaderModule module;
|
||||
};
|
||||
|
||||
enum class MSAACopyFormatClass : u32 {
|
||||
@@ -117,7 +127,7 @@ public:
|
||||
|
||||
void ConvertS8D24ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
|
||||
void ClearColor(const Framebuffer* dst_framebuffer, u8 color_mask,
|
||||
void ClearColor(const Framebuffer* dst_framebuffer, u32 target, u8 color_mask,
|
||||
const std::array<f32, 4>& clear_color, const Region2D& dst_region);
|
||||
|
||||
void ClearDepthStencil(const Framebuffer* dst_framebuffer, bool depth_clear, f32 clear_depth,
|
||||
@@ -156,10 +166,10 @@ private:
|
||||
std::span<const VideoCommon::ImageCopy> copies,
|
||||
const MSAACopyAspectInfo& aspect_info, bool copy_stencil);
|
||||
|
||||
void Convert(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
void Convert(VkShaderModule module, const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view);
|
||||
|
||||
void ConvertDepthStencil(VkPipeline pipeline, const Framebuffer* dst_framebuffer,
|
||||
void ConvertDepthStencil(VkShaderModule module, const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view);
|
||||
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceColorPipeline(const BlitImagePipelineKey& key);
|
||||
@@ -179,22 +189,9 @@ private:
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceBlitDepthPipeline(const RenderingFormats& formats);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceResolveDepthStencilPipeline(
|
||||
const RenderingFormats& formats, bool resolve_stencil);
|
||||
|
||||
void ConvertPipeline(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
bool is_target_depth);
|
||||
|
||||
void ConvertDepthToColorPipeline(vk::Pipeline& pipeline, const RenderingFormats& formats);
|
||||
|
||||
void ConvertColorToDepthPipeline(vk::Pipeline& pipeline, const RenderingFormats& formats);
|
||||
|
||||
void ConvertPipelineEx(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
vk::ShaderModule& module, bool single_texture, bool is_target_depth);
|
||||
|
||||
void ConvertPipelineColorTargetEx(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
vk::ShaderModule& module);
|
||||
|
||||
void ConvertPipelineDepthTargetEx(vk::Pipeline& pipeline, const RenderingFormats& formats,
|
||||
vk::ShaderModule& module);
|
||||
[[nodiscard]] VkPipeline FindOrEmplaceConvertPipeline(const RenderingFormats& formats,
|
||||
VkShaderModule module,
|
||||
VkPipelineLayout layout);
|
||||
|
||||
const Device& device;
|
||||
Scheduler& scheduler;
|
||||
@@ -271,15 +268,8 @@ private:
|
||||
vk::ImageView dst_view;
|
||||
};
|
||||
std::deque<MSAACopyResources> msaa_copy_resources;
|
||||
vk::Pipeline convert_d32_to_r32_pipeline;
|
||||
vk::Pipeline convert_r32_to_d32_pipeline;
|
||||
vk::Pipeline convert_d16_to_r16_pipeline;
|
||||
vk::Pipeline convert_r16_to_d16_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d32f_pipeline;
|
||||
vk::Pipeline convert_d32f_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_s8d24_to_abgr8_pipeline;
|
||||
std::vector<ConvertPipelineKey> convert_keys;
|
||||
std::vector<vk::Pipeline> convert_pipelines;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -1421,24 +1421,19 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
|
||||
impl->hcr_buffer = buffer->Handle();
|
||||
impl->hcr_offset = offset;
|
||||
}
|
||||
if (impl->hcr_is_set) {
|
||||
if (impl->hcr_setup.buffer == impl->hcr_buffer &&
|
||||
impl->hcr_setup.offset == impl->hcr_offset) {
|
||||
return;
|
||||
}
|
||||
VkConditionalRenderingFlagsEXT flags = 0;
|
||||
if (is_equal) {
|
||||
flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
|
||||
}
|
||||
bool was_running = impl->is_hcr_running;
|
||||
if (was_running) {
|
||||
PauseHostConditionalRendering();
|
||||
if (impl->hcr_is_set && impl->hcr_setup.buffer == impl->hcr_buffer &&
|
||||
impl->hcr_setup.offset == impl->hcr_offset && impl->hcr_setup.flags == flags) {
|
||||
return;
|
||||
}
|
||||
PauseHostConditionalRendering();
|
||||
impl->hcr_setup.buffer = impl->hcr_buffer;
|
||||
impl->hcr_setup.offset = impl->hcr_offset;
|
||||
impl->hcr_setup.flags = is_equal ? VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT : 0;
|
||||
impl->hcr_setup.flags = flags;
|
||||
impl->hcr_is_set = true;
|
||||
impl->is_hcr_running = false;
|
||||
if (was_running) {
|
||||
ResumeHostConditionalRendering();
|
||||
}
|
||||
}
|
||||
|
||||
void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal,
|
||||
@@ -1455,20 +1450,16 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
|
||||
to_resolve = buffer->Handle();
|
||||
to_resolve_offset = static_cast<u32>(offset);
|
||||
}
|
||||
bool was_running = impl->is_hcr_running;
|
||||
if (was_running) {
|
||||
PauseHostConditionalRendering();
|
||||
}
|
||||
PauseHostConditionalRendering();
|
||||
impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve,
|
||||
to_resolve_offset, compare_to_zero);
|
||||
impl->hcr_setup.buffer = *impl->hcr_resolve_buffer;
|
||||
impl->hcr_setup.offset = 0;
|
||||
impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
|
||||
impl->hcr_is_set = true;
|
||||
impl->is_hcr_running = false;
|
||||
if (was_running) {
|
||||
ResumeHostConditionalRendering();
|
||||
impl->hcr_setup.flags = 0;
|
||||
if (!is_equal) {
|
||||
impl->hcr_setup.flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
|
||||
}
|
||||
impl->hcr_is_set = true;
|
||||
}
|
||||
|
||||
bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1,
|
||||
|
||||
@@ -550,7 +550,8 @@ void RasterizerVulkan::Clear(u32 layer_count) {
|
||||
Offset2D{.x = clear_rect.rect.offset.x + s32(clear_rect.rect.extent.width),
|
||||
.y = clear_rect.rect.offset.y + s32(clear_rect.rect.extent.height)}};
|
||||
request_renderpass();
|
||||
blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
|
||||
blit_image.ClearColor(framebuffer, color_attachment, color_mask, regs.clear_color,
|
||||
dst_region);
|
||||
} else if (!clear_on_load ||
|
||||
!scheduler.OverrideLoadOps(framebuffer, 1u << color_attachment,
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR, clear_value)) {
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
@@ -116,6 +118,7 @@ void Scheduler::DispatchWork() {
|
||||
}
|
||||
event_cv.notify_all();
|
||||
recorded_attachments = nullptr;
|
||||
ended_attachments = nullptr;
|
||||
AcquireNewChunk();
|
||||
}
|
||||
}
|
||||
@@ -169,29 +172,41 @@ bool Scheduler::OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments,
|
||||
if (!renderpass_pristine || recorded_attachments == nullptr) {
|
||||
return false;
|
||||
}
|
||||
const auto set_load_op = [&](VkRenderingAttachmentInfo& attachment, bool discards_msaa) {
|
||||
const auto set_load_op = [&](VkRenderingAttachmentInfo& attachment) {
|
||||
attachment.loadOp = load_op;
|
||||
attachment.clearValue = value;
|
||||
if (discards_msaa && load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
}
|
||||
};
|
||||
for (u32 slot = 0; slot < recorded_attachments->num_colors; ++slot) {
|
||||
if ((attachments & (1u << slot)) != 0) {
|
||||
set_load_op(recorded_attachments->colors[slot], framebuffer->DiscardsMsaaColor());
|
||||
set_load_op(recorded_attachments->colors[slot]);
|
||||
}
|
||||
}
|
||||
if ((attachments & DEPTH_ATTACHMENT_BIT) != 0) {
|
||||
set_load_op(recorded_attachments->depth, framebuffer->DiscardsMsaaDepthStencil());
|
||||
set_load_op(recorded_attachments->depth);
|
||||
}
|
||||
if ((attachments & STENCIL_ATTACHMENT_BIT) != 0) {
|
||||
set_load_op(recorded_attachments->stencil, framebuffer->DiscardsMsaaDepthStencil());
|
||||
set_load_op(recorded_attachments->stencil);
|
||||
}
|
||||
attachments_touched |= attachments;
|
||||
attachments_written |= attachments;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Scheduler::DiscardResolvedAttachments(VkImageView resolve_view) {
|
||||
if (ended_attachments == nullptr) {
|
||||
return;
|
||||
}
|
||||
const auto discard = [resolve_view](VkRenderingAttachmentInfo& attachment) {
|
||||
if (attachment.resolveImageView == resolve_view) {
|
||||
attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
}
|
||||
};
|
||||
std::ranges::for_each(std::span(ended_attachments->colors).first(ended_attachments->num_colors),
|
||||
discard);
|
||||
discard(ended_attachments->depth);
|
||||
discard(ended_attachments->stencil);
|
||||
}
|
||||
|
||||
void Scheduler::RequestOutsideRenderPassOperationContext() {
|
||||
EndRenderPass();
|
||||
PublishComputeWrites();
|
||||
@@ -420,8 +435,8 @@ void Scheduler::EndRenderPass()
|
||||
}
|
||||
if (recorded_attachments != nullptr) {
|
||||
RelaxAttachmentOps(*recorded_attachments);
|
||||
recorded_attachments = nullptr;
|
||||
}
|
||||
ended_attachments = std::exchange(recorded_attachments, nullptr);
|
||||
|
||||
query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount);
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ public:
|
||||
bool OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments,
|
||||
VkAttachmentLoadOp load_op, const VkClearValue& value);
|
||||
|
||||
void DiscardResolvedAttachments(VkImageView resolve_view);
|
||||
|
||||
/// Requests the current execution context to be able to execute operations only allowed outside
|
||||
/// of a renderpass.
|
||||
void RequestOutsideRenderPassOperationContext();
|
||||
@@ -338,6 +340,7 @@ private:
|
||||
u32 attachments_touched = 0;
|
||||
u32 attachments_written = 0;
|
||||
RenderingAttachments* recorded_attachments = nullptr;
|
||||
RenderingAttachments* ended_attachments = nullptr;
|
||||
VkMemoryBarrier2 renderpass_write_barrier{};
|
||||
VkMemoryBarrier2 compute_write_barrier{};
|
||||
VkMemoryBarrier2 upload_write_barrier{};
|
||||
|
||||
@@ -1838,6 +1838,11 @@ void TextureCacheRuntime::CopyImageMSAA(Image& dst, Image& src,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region);
|
||||
cmdbuf.PipelineBarrier(0, {}, {}, post_barriers);
|
||||
});
|
||||
const bool is_color = aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
if ((is_color && ENABLE_MSAA_COLOR_DISCARD) ||
|
||||
(!is_color && ENABLE_MSAA_DEPTH_STENCIL_DISCARD)) {
|
||||
scheduler.DiscardResolvedAttachments(*shadow->view);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -3076,11 +3081,6 @@ void Framebuffer::CreateFramebuffer(TextureCacheRuntime& runtime,
|
||||
samples != VK_SAMPLE_COUNT_1_BIT && depth_image != VK_NULL_HANDLE &&
|
||||
runtime.device.IsTiler() && SupportsDepthStencilResolve(runtime.device, depth_format);
|
||||
|
||||
discard_msaa_color =
|
||||
ENABLE_MSAA_RESOLVE_CONSUME && ENABLE_MSAA_COLOR_DISCARD && do_resolve_color;
|
||||
discard_msaa_depth_stencil = ENABLE_MSAA_RESOLVE_CONSUME &&
|
||||
ENABLE_MSAA_DEPTH_STENCIL_DISCARD && do_resolve_depth_stencil;
|
||||
|
||||
runtime_ptr = &runtime;
|
||||
id = ++runtime.framebuffer_serial;
|
||||
render_area.width = (std::min)(render_area.width, width);
|
||||
|
||||
@@ -250,14 +250,6 @@ public:
|
||||
return is_rescaled;
|
||||
}
|
||||
|
||||
[[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;
|
||||
@@ -278,8 +270,6 @@ private:
|
||||
std::array<VkImage, 9> resolve_shadow_images{};
|
||||
u32 num_resolve_shadows = 0;
|
||||
TextureCacheRuntime* runtime_ptr{nullptr};
|
||||
bool discard_msaa_color{};
|
||||
bool discard_msaa_depth_stencil{};
|
||||
};
|
||||
|
||||
class Image : public VideoCommon::ImageBase {
|
||||
|
||||
Reference in New Issue
Block a user