From f1fa5647335fc2e4a6882debe2b2fe26bc5779ac Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Sat, 24 Jan 2026 23:48:54 +0100 Subject: [PATCH] Revert: video_core/renderer_vulkan: Add texture filtering (#1678) --- .../renderer_vulkan/vk_blit_helper.cpp | 381 +----------------- .../renderer_vulkan/vk_blit_helper.h | 32 +- .../renderer_vulkan/vk_resource_pool.cpp | 7 +- .../renderer_vulkan/vk_texture_runtime.cpp | 185 ++------- .../renderer_vulkan/vk_texture_runtime.h | 34 +- 5 files changed, 74 insertions(+), 565 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.cpp b/src/video_core/renderer_vulkan/vk_blit_helper.cpp index 62f38e8f6..0a7a3be44 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_helper.cpp @@ -1,8 +1,7 @@ -// Copyright Citra Emulator Project / Azahar Emulator Project +// Copyright 2022 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/settings.h" #include "common/vector_math.h" #include "video_core/renderer_vulkan/vk_blit_helper.h" #include "video_core/renderer_vulkan/vk_descriptor_update_queue.h" @@ -17,19 +16,8 @@ #include "video_core/host_shaders/vulkan_blit_depth_stencil_frag.h" #include "video_core/host_shaders/vulkan_depth_to_buffer_comp.h" -// Texture filtering shader includes -#include "video_core/host_shaders/texture_filtering/bicubic_frag.h" -#include "video_core/host_shaders/texture_filtering/mmpx_frag.h" -#include "video_core/host_shaders/texture_filtering/refine_frag.h" -#include "video_core/host_shaders/texture_filtering/scale_force_frag.h" -#include "video_core/host_shaders/texture_filtering/x_gradient_frag.h" -#include "video_core/host_shaders/texture_filtering/xbrz_freescale_frag.h" -#include "video_core/host_shaders/texture_filtering/y_gradient_frag.h" -#include "vk_blit_helper.h" - namespace Vulkan { -using Settings::TextureFilter; using VideoCore::PixelFormat; namespace { @@ -67,33 +55,8 @@ constexpr std::array TWO_TEXTURES_BINDINGS = {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, }}; -// Texture filtering descriptor set bindings -constexpr std::array SINGLE_TEXTURE_BINDINGS = {{ - {0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, -}}; - -constexpr std::array THREE_TEXTURES_BINDINGS = {{ - {0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, - {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, - {2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, -}}; - -// Note: Removed FILTER_UTILITY_BINDINGS as texture filtering doesn't need shadow buffers - -// Push constant structure for texture filtering -struct FilterPushConstants { - std::array tex_scale; - std::array tex_offset; - float res_scale; // For xBRZ filter -}; - -inline constexpr vk::PushConstantRange FILTER_PUSH_CONSTANT_RANGE{ - .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, - .offset = 0, - .size = sizeof(FilterPushConstants), -}; inline constexpr vk::PushConstantRange PUSH_CONSTANT_RANGE{ - .stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, + .stageFlags = vk::ShaderStageFlagBits::eVertex, .offset = 0, .size = sizeof(PushConstants), }; @@ -141,17 +104,12 @@ constexpr vk::PipelineDynamicStateCreateInfo PIPELINE_DYNAMIC_STATE_CREATE_INFO{ .dynamicStateCount = static_cast(DYNAMIC_STATES.size()), .pDynamicStates = DYNAMIC_STATES.data(), }; - -constexpr vk::PipelineColorBlendAttachmentState COLOR_BLEND_ATTACHMENT{ - .blendEnable = VK_FALSE, - .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | - vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, -}; - -constexpr vk::PipelineColorBlendStateCreateInfo PIPELINE_COLOR_BLEND_STATE_CREATE_INFO{ +constexpr vk::PipelineColorBlendStateCreateInfo PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO{ .logicOpEnable = VK_FALSE, - .attachmentCount = 1, - .pAttachments = &COLOR_BLEND_ATTACHMENT, + .logicOp = vk::LogicOp::eClear, + .attachmentCount = 0, + .pAttachments = nullptr, + .blendConstants = std::array{0.0f, 0.0f, 0.0f, 0.0f}, }; constexpr vk::PipelineDepthStencilStateCreateInfo PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO{ .depthTestEnable = VK_TRUE, @@ -170,9 +128,9 @@ inline constexpr vk::SamplerCreateInfo SAMPLER_CREATE_INFO{ .magFilter = filter, .minFilter = filter, .mipmapMode = vk::SamplerMipmapMode::eNearest, - .addressModeU = vk::SamplerAddressMode::eClampToEdge, - .addressModeV = vk::SamplerAddressMode::eClampToEdge, - .addressModeW = vk::SamplerAddressMode::eClampToEdge, + .addressModeU = vk::SamplerAddressMode::eClampToBorder, + .addressModeV = vk::SamplerAddressMode::eClampToBorder, + .addressModeW = vk::SamplerAddressMode::eClampToBorder, .mipLodBias = 0.0f, .anisotropyEnable = VK_FALSE, .maxAnisotropy = 0.0f, @@ -185,14 +143,12 @@ inline constexpr vk::SamplerCreateInfo SAMPLER_CREATE_INFO{ }; constexpr vk::PipelineLayoutCreateInfo PipelineLayoutCreateInfo( - const vk::DescriptorSetLayout* set_layout, bool compute = false, bool filter = false) { + const vk::DescriptorSetLayout* set_layout, bool compute = false) { return vk::PipelineLayoutCreateInfo{ .setLayoutCount = 1, .pSetLayouts = set_layout, .pushConstantRangeCount = 1, - .pPushConstantRanges = - (compute ? &COMPUTE_PUSH_CONSTANT_RANGE - : (filter ? &FILTER_PUSH_CONSTANT_RANGE : &PUSH_CONSTANT_RANGE)), + .pPushConstantRanges = (compute ? &COMPUTE_PUSH_CONSTANT_RANGE : &PUSH_CONSTANT_RANGE), }; } @@ -229,20 +185,12 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, compute_provider{instance, scheduler.GetMasterSemaphore(), COMPUTE_BINDINGS}, compute_buffer_provider{instance, scheduler.GetMasterSemaphore(), COMPUTE_BUFFER_BINDINGS}, two_textures_provider{instance, scheduler.GetMasterSemaphore(), TWO_TEXTURES_BINDINGS, 16}, - single_texture_provider{instance, scheduler.GetMasterSemaphore(), SINGLE_TEXTURE_BINDINGS, - 16}, - three_textures_provider{instance, scheduler.GetMasterSemaphore(), THREE_TEXTURES_BINDINGS, - 16}, compute_pipeline_layout{ device.createPipelineLayout(PipelineLayoutCreateInfo(&compute_provider.Layout(), true))}, compute_buffer_pipeline_layout{device.createPipelineLayout( PipelineLayoutCreateInfo(&compute_buffer_provider.Layout(), true))}, two_textures_pipeline_layout{ device.createPipelineLayout(PipelineLayoutCreateInfo(&two_textures_provider.Layout()))}, - single_texture_pipeline_layout{device.createPipelineLayout( - PipelineLayoutCreateInfo(&single_texture_provider.Layout(), false, true))}, - three_textures_pipeline_layout{device.createPipelineLayout( - PipelineLayoutCreateInfo(&three_textures_provider.Layout(), false, true))}, full_screen_vert{Compile(HostShaders::FULL_SCREEN_TRIANGLE_VERT, vk::ShaderStageFlagBits::eVertex, device)}, d24s8_to_rgba8_comp{Compile(HostShaders::VULKAN_D24S8_TO_RGBA8_COMP, @@ -251,14 +199,6 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, vk::ShaderStageFlagBits::eCompute, device)}, blit_depth_stencil_frag{Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, - // Texture filtering shader modules - bicubic_frag{Compile(HostShaders::BICUBIC_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, - scale_force_frag{ - Compile(HostShaders::SCALE_FORCE_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, - xbrz_frag{ - Compile(HostShaders::XBRZ_FREESCALE_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, - mmpx_frag{Compile(HostShaders::MMPX_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, - refine_frag{Compile(HostShaders::REFINE_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)}, depth_to_buffer_pipeline{ MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)}, @@ -290,18 +230,10 @@ BlitHelper::~BlitHelper() { device.destroyPipelineLayout(compute_pipeline_layout); device.destroyPipelineLayout(compute_buffer_pipeline_layout); device.destroyPipelineLayout(two_textures_pipeline_layout); - device.destroyPipelineLayout(single_texture_pipeline_layout); - device.destroyPipelineLayout(three_textures_pipeline_layout); device.destroyShaderModule(full_screen_vert); device.destroyShaderModule(d24s8_to_rgba8_comp); device.destroyShaderModule(depth_to_buffer_comp); device.destroyShaderModule(blit_depth_stencil_frag); - // Destroy texture filtering shader modules - device.destroyShaderModule(bicubic_frag); - device.destroyShaderModule(scale_force_frag); - device.destroyShaderModule(xbrz_frag); - device.destroyShaderModule(mmpx_frag); - device.destroyShaderModule(refine_frag); device.destroyPipeline(depth_to_buffer_pipeline); device.destroyPipeline(d24s8_to_rgba8_pipeline); device.destroyPipeline(depth_blit_pipeline); @@ -310,7 +242,7 @@ BlitHelper::~BlitHelper() { } void BindBlitState(vk::CommandBuffer cmdbuf, vk::PipelineLayout layout, - const VideoCore::TextureBlit& blit, const Surface& dest) { + const VideoCore::TextureBlit& blit) { const vk::Offset2D offset{ .x = std::min(blit.dst_rect.left, blit.dst_rect.right), .y = std::min(blit.dst_rect.bottom, blit.dst_rect.top), @@ -340,9 +272,8 @@ void BindBlitState(vk::CommandBuffer cmdbuf, vk::PipelineLayout layout, }; cmdbuf.setViewport(0, viewport); cmdbuf.setScissor(0, scissor); - cmdbuf.pushConstants(layout, - vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, - sizeof(push_constants), &push_constants); + cmdbuf.pushConstants(layout, vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants), + &push_constants); } bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest, @@ -369,12 +300,12 @@ bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest, }; renderpass_cache.BeginRendering(depth_pass); - scheduler.Record([blit, descriptor_set, &dest, this](vk::CommandBuffer cmdbuf) { + scheduler.Record([blit, descriptor_set, this](vk::CommandBuffer cmdbuf) { const vk::PipelineLayout layout = two_textures_pipeline_layout; cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, depth_blit_pipeline); cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, descriptor_set, {}); - BindBlitState(cmdbuf, layout, blit, dest); + BindBlitState(cmdbuf, layout, blit); cmdbuf.draw(3, 1, 0, 0); }); scheduler.MakeDirty(StateFlags::Pipeline); @@ -600,7 +531,7 @@ vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() { .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, - .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_EMPTY_CREATE_INFO, .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, .layout = two_textures_pipeline_layout, .renderPass = renderpass, @@ -616,280 +547,4 @@ vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() { return VK_NULL_HANDLE; } -bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) { - const auto filter = Settings::values.texture_filter.GetValue(); - const bool is_depth = - surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; // Skip filtering for depth textures - // and when no filter is selected - if (filter == Settings::TextureFilter::NoFilter || is_depth) { - return false; - } // Only filter base mipmap level - if (blit.src_level != 0) { - return true; - } - - switch (filter) { - case TextureFilter::Anime4K: - FilterAnime4K(surface, blit); - break; - case TextureFilter::Bicubic: - FilterBicubic(surface, blit); - break; - case TextureFilter::ScaleForce: - FilterScaleForce(surface, blit); - break; - case TextureFilter::xBRZ: - FilterXbrz(surface, blit); - break; - case TextureFilter::MMPX: - FilterMMPX(surface, blit); - break; - default: - LOG_ERROR(Render_Vulkan, "Unknown texture filter {}", filter); - return false; - } - return true; -} - -void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit) { - const bool is_depth = surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format; - auto pipeline = MakeFilterPipeline(refine_frag, three_textures_pipeline_layout, color_format); - FilterPassThreeTextures(surface, surface, surface, surface, pipeline, - three_textures_pipeline_layout, blit); -} - -void BlitHelper::FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit) { - const bool is_depth = surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format; - auto pipeline = MakeFilterPipeline(bicubic_frag, single_texture_pipeline_layout, color_format); - FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit); -} - -void BlitHelper::FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit) { - const bool is_depth = surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format; - auto pipeline = - MakeFilterPipeline(scale_force_frag, single_texture_pipeline_layout, color_format); - FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit); -} - -void BlitHelper::FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit) { - const bool is_depth = surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format; - auto pipeline = MakeFilterPipeline(xbrz_frag, single_texture_pipeline_layout, color_format); - FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit); -} - -void BlitHelper::FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit) { - const bool is_depth = surface.type == VideoCore::SurfaceType::Depth || - surface.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : surface.pixel_format; - auto pipeline = MakeFilterPipeline(mmpx_frag, single_texture_pipeline_layout, color_format); - FilterPass(surface, surface, pipeline, single_texture_pipeline_layout, blit); -} - -vk::Pipeline BlitHelper::MakeFilterPipeline(vk::ShaderModule fragment_shader, - vk::PipelineLayout layout, - VideoCore::PixelFormat color_format) { - const std::array stages = MakeStages(full_screen_vert, fragment_shader); - // Use the provided color format for render pass compatibility - const auto renderpass = - renderpass_cache.GetRenderpass(color_format, VideoCore::PixelFormat::Invalid, false); - - vk::GraphicsPipelineCreateInfo pipeline_info = { - .stageCount = static_cast(stages.size()), - .pStages = stages.data(), - .pVertexInputState = &PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .pInputAssemblyState = &PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .pTessellationState = nullptr, - .pViewportState = &PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pRasterizationState = &PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pDepthStencilState = nullptr, - .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .layout = layout, - .renderPass = renderpass, - }; - - if (const auto result = device.createGraphicsPipeline({}, pipeline_info); - result.result == vk::Result::eSuccess) { - return result.value; - } else { - LOG_CRITICAL(Render_Vulkan, "Filter pipeline creation failed!"); - UNREACHABLE(); - } -} - -void BlitHelper::FilterPass(Surface& source, Surface& dest, vk::Pipeline pipeline, - vk::PipelineLayout layout, const VideoCore::TextureBlit& blit) { - const auto texture_descriptor_set = single_texture_provider.Commit(); - update_queue.AddImageSampler(texture_descriptor_set, 0, 0, source.ImageView(0), linear_sampler, - vk::ImageLayout::eGeneral); - - const bool is_depth = dest.type == VideoCore::SurfaceType::Depth || - dest.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : dest.pixel_format; - const auto depth_format = is_depth ? dest.pixel_format : VideoCore::PixelFormat::Invalid; - const auto renderpass = renderpass_cache.GetRenderpass(color_format, depth_format, false); - - const RenderPass render_pass = { - .framebuffer = dest.Framebuffer(), - .render_pass = renderpass, - .render_area = - { - .offset = {0, 0}, - .extent = {dest.GetScaledWidth(), dest.GetScaledHeight()}, - }, - }; - renderpass_cache.BeginRendering(render_pass); - const float src_scale = static_cast(source.GetResScale()); - // Calculate normalized texture coordinates like OpenGL does - const auto src_extent = source.RealExtent(false); // Get unscaled texture extent - const float tex_scale_x = - static_cast(blit.src_rect.GetWidth()) / static_cast(src_extent.width); - const float tex_scale_y = - static_cast(blit.src_rect.GetHeight()) / static_cast(src_extent.height); - const float tex_offset_x = - static_cast(blit.src_rect.left) / static_cast(src_extent.width); - const float tex_offset_y = - static_cast(blit.src_rect.bottom) / static_cast(src_extent.height); - - scheduler.Record([pipeline, layout, texture_descriptor_set, blit, tex_scale_x, tex_scale_y, - tex_offset_x, tex_offset_y, src_scale](vk::CommandBuffer cmdbuf) { - const FilterPushConstants push_constants{.tex_scale = {tex_scale_x, tex_scale_y}, - .tex_offset = {tex_offset_x, tex_offset_y}, - .res_scale = src_scale}; - - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - - // Bind single texture descriptor set - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, - texture_descriptor_set, {}); - - cmdbuf.pushConstants(layout, FILTER_PUSH_CONSTANT_RANGE.stageFlags, - FILTER_PUSH_CONSTANT_RANGE.offset, FILTER_PUSH_CONSTANT_RANGE.size, - &push_constants); - - // Set up viewport and scissor for filtering (don't use BindBlitState as it overwrites push - // constants) - const vk::Offset2D offset{ - .x = std::min(blit.dst_rect.left, blit.dst_rect.right), - .y = std::min(blit.dst_rect.bottom, blit.dst_rect.top), - }; - const vk::Extent2D extent{ - .width = blit.dst_rect.GetWidth(), - .height = blit.dst_rect.GetHeight(), - }; - const vk::Viewport viewport{ - .x = static_cast(offset.x), - .y = static_cast(offset.y), - .width = static_cast(extent.width), - .height = static_cast(extent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - const vk::Rect2D scissor{ - .offset = offset, - .extent = extent, - }; - cmdbuf.setViewport(0, viewport); - cmdbuf.setScissor(0, scissor); - cmdbuf.draw(3, 1, 0, 0); - }); - scheduler.MakeDirty(StateFlags::Pipeline); -} - -void BlitHelper::FilterPassThreeTextures(Surface& source1, Surface& source2, Surface& source3, - Surface& dest, vk::Pipeline pipeline, - vk::PipelineLayout layout, - const VideoCore::TextureBlit& blit) { - const auto texture_descriptor_set = three_textures_provider.Commit(); - - update_queue.AddImageSampler(texture_descriptor_set, 0, 0, source1.ImageView(0), linear_sampler, - vk::ImageLayout::eGeneral); - update_queue.AddImageSampler(texture_descriptor_set, 1, 0, source2.ImageView(0), linear_sampler, - vk::ImageLayout::eGeneral); - update_queue.AddImageSampler(texture_descriptor_set, 2, 0, source3.ImageView(0), linear_sampler, - vk::ImageLayout::eGeneral); - - const bool is_depth = dest.type == VideoCore::SurfaceType::Depth || - dest.type == VideoCore::SurfaceType::DepthStencil; - const auto color_format = is_depth ? VideoCore::PixelFormat::Invalid : dest.pixel_format; - const auto depth_format = is_depth ? dest.pixel_format : VideoCore::PixelFormat::Invalid; - const auto renderpass = renderpass_cache.GetRenderpass(color_format, depth_format, false); - - const RenderPass render_pass = { - .framebuffer = dest.Framebuffer(), - .render_pass = renderpass, - .render_area = - { - .offset = {0, 0}, - .extent = {dest.GetScaledWidth(), dest.GetScaledHeight()}, - }, - }; - renderpass_cache.BeginRendering(render_pass); - - const float src_scale = static_cast(source1.GetResScale()); - // Calculate normalized texture coordinates like OpenGL does - const auto src_extent = source1.RealExtent(false); // Get unscaled texture extent - const float tex_scale_x = - static_cast(blit.src_rect.GetWidth()) / static_cast(src_extent.width); - const float tex_scale_y = - static_cast(blit.src_rect.GetHeight()) / static_cast(src_extent.height); - const float tex_offset_x = - static_cast(blit.src_rect.left) / static_cast(src_extent.width); - const float tex_offset_y = - static_cast(blit.src_rect.bottom) / static_cast(src_extent.height); - - scheduler.Record([pipeline, layout, texture_descriptor_set, blit, tex_scale_x, tex_scale_y, - tex_offset_x, tex_offset_y, src_scale](vk::CommandBuffer cmdbuf) { - const FilterPushConstants push_constants{.tex_scale = {tex_scale_x, tex_scale_y}, - .tex_offset = {tex_offset_x, tex_offset_y}, - .res_scale = src_scale}; - - cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); - - // Bind single texture descriptor set - cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, - texture_descriptor_set, {}); - - cmdbuf.pushConstants(layout, FILTER_PUSH_CONSTANT_RANGE.stageFlags, - FILTER_PUSH_CONSTANT_RANGE.offset, FILTER_PUSH_CONSTANT_RANGE.size, - &push_constants); - - // Set up viewport and scissor using safe viewport like working filters - const vk::Offset2D offset{ - .x = std::min(blit.dst_rect.left, blit.dst_rect.right), - .y = std::min(blit.dst_rect.bottom, blit.dst_rect.top), - }; - const vk::Extent2D extent{ - .width = blit.dst_rect.GetWidth(), - .height = blit.dst_rect.GetHeight(), - }; - const vk::Viewport viewport{ - .x = static_cast(offset.x), - .y = static_cast(offset.y), - .width = static_cast(extent.width), - .height = static_cast(extent.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - const vk::Rect2D scissor{ - .offset = offset, - .extent = extent, - }; - cmdbuf.setViewport(0, viewport); - cmdbuf.setScissor(0, scissor); - cmdbuf.draw(3, 1, 0, 0); - }); - scheduler.MakeDirty(StateFlags::Pipeline); -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.h b/src/video_core/renderer_vulkan/vk_blit_helper.h index 923f08cc9..d9b5c7760 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.h +++ b/src/video_core/renderer_vulkan/vk_blit_helper.h @@ -1,10 +1,9 @@ -// Copyright Citra Emulator Project / Azahar Emulator Project +// Copyright 2023 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" namespace VideoCore { @@ -28,7 +27,6 @@ public: explicit BlitHelper(const Instance& instance, Scheduler& scheduler, RenderManager& renderpass_cache, DescriptorUpdateQueue& update_queue); ~BlitHelper(); - bool Filter(Surface& surface, const VideoCore::TextureBlit& blit); bool BlitDepthStencil(Surface& source, Surface& dest, const VideoCore::TextureBlit& blit); @@ -40,25 +38,6 @@ public: private: vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout); vk::Pipeline MakeDepthStencilBlitPipeline(); - vk::Pipeline MakeFilterPipeline( - vk::ShaderModule fragment_shader, vk::PipelineLayout layout, - VideoCore::PixelFormat color_format = VideoCore::PixelFormat::RGBA8); - - void FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit); - void FilterBicubic(Surface& surface, const VideoCore::TextureBlit& blit); - void FilterScaleForce(Surface& surface, const VideoCore::TextureBlit& blit); - void FilterXbrz(Surface& surface, const VideoCore::TextureBlit& blit); - void FilterMMPX(Surface& surface, const VideoCore::TextureBlit& blit); - - void FilterPass(Surface& source, Surface& dest, vk::Pipeline pipeline, - vk::PipelineLayout layout, const VideoCore::TextureBlit& blit); - - void FilterPassThreeTextures(Surface& source1, Surface& source2, Surface& source3, - Surface& dest, vk::Pipeline pipeline, vk::PipelineLayout layout, - const VideoCore::TextureBlit& blit); - - void FilterPassYGradient(Surface& source, Surface& dest, vk::Pipeline pipeline, - vk::PipelineLayout layout, const VideoCore::TextureBlit& blit); private: const Instance& instance; @@ -72,23 +51,14 @@ private: DescriptorHeap compute_provider; DescriptorHeap compute_buffer_provider; DescriptorHeap two_textures_provider; - DescriptorHeap single_texture_provider; - DescriptorHeap three_textures_provider; vk::PipelineLayout compute_pipeline_layout; vk::PipelineLayout compute_buffer_pipeline_layout; vk::PipelineLayout two_textures_pipeline_layout; - vk::PipelineLayout single_texture_pipeline_layout; - vk::PipelineLayout three_textures_pipeline_layout; vk::ShaderModule full_screen_vert; vk::ShaderModule d24s8_to_rgba8_comp; vk::ShaderModule depth_to_buffer_comp; vk::ShaderModule blit_depth_stencil_frag; - vk::ShaderModule bicubic_frag; - vk::ShaderModule scale_force_frag; - vk::ShaderModule xbrz_frag; - vk::ShaderModule mmpx_frag; - vk::ShaderModule refine_frag; vk::Pipeline d24s8_to_rgba8_pipeline; vk::Pipeline depth_to_buffer_pipeline; diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp index 28ef0504c..0021167e4 100644 --- a/src/video_core/renderer_vulkan/vk_resource_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp @@ -1,4 +1,4 @@ -// Copyright Citra Emulator Project / Azahar Emulator Project +// Copyright 2020 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -103,14 +103,13 @@ vk::CommandBuffer CommandPool::Commit() { return cmd_buffers[index]; } -constexpr u32 DESCRIPTOR_SET_BATCH = 64; -constexpr u32 DESCRIPTOR_MULTIPLIER = 4; // Increase capacity of each pool +constexpr u32 DESCRIPTOR_SET_BATCH = 32; DescriptorHeap::DescriptorHeap(const Instance& instance, MasterSemaphore* master_semaphore, std::span bindings, u32 descriptor_heap_count_) : ResourcePool{master_semaphore, DESCRIPTOR_SET_BATCH}, device{instance.GetDevice()}, - descriptor_heap_count{descriptor_heap_count_ * DESCRIPTOR_MULTIPLIER} { // Increase pool size + descriptor_heap_count{descriptor_heap_count_} { // Create descriptor set layout. const vk::DescriptorSetLayoutCreateInfo layout_ci = { .bindingCount = static_cast(bindings.size()), diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 7bbc2a019..fdaa4ef2e 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -1,23 +1,9 @@ -// Copyright Citra Emulator Project / Azahar Emulator Project +// Copyright 2023 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "video_core/renderer_vulkan/vk_texture_runtime.h" - -#include -#include -#include -#include #include #include -#include -#include "video_core/custom_textures/custom_tex_manager.h" -#include "video_core/rasterizer_cache/pixel_format.h" -#include "video_core/rasterizer_cache/surface_params.h" -#include "video_core/renderer_base.h" -#include "video_core/renderer_vulkan/vk_blit_helper.h" -#include "video_core/renderer_vulkan/vk_descriptor_update_queue.h" -#include "video_core/renderer_vulkan/vk_stream_buffer.h" #include "common/literals.h" #include "common/microprofile.h" @@ -714,7 +700,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param : SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, scheduler{&runtime_.GetScheduler()}, traits{instance->GetTraits(pixel_format)} { - if (pixel_format == VideoCore::PixelFormat::Invalid || !traits.transfer_support) { + if (pixel_format == VideoCore::PixelFormat::Invalid) { return; } @@ -734,25 +720,18 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param flags |= vk::ImageCreateFlagBits::eMutableFormat; } - // Ensure color formats have the color attachment bit set for framebuffers - auto usage = traits.usage; - const bool is_color = - (traits.aspect & vk::ImageAspectFlagBits::eColor) != vk::ImageAspectFlags{}; - if (is_color) { - usage |= vk::ImageUsageFlagBits::eColorAttachment; - } - const bool need_format_list = is_mutable && instance->IsImageFormatListSupported(); - handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, usage, flags, - traits.aspect, need_format_list, DebugName(false)); + handles[0] = MakeHandle(instance, width, height, levels, texture_type, format, traits.usage, + flags, traits.aspect, need_format_list, DebugName(false)); raw_images.emplace_back(handles[0].image); if (res_scale != 1) { handles[1] = MakeHandle(instance, GetScaledWidth(), GetScaledHeight(), levels, texture_type, format, - usage, flags, traits.aspect, need_format_list, DebugName(true)); + traits.usage, flags, traits.aspect, need_format_list, DebugName(true)); raw_images.emplace_back(handles[1].image); } + runtime->renderpass_cache.EndRendering(); scheduler->Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { const auto barriers = MakeInitBarriers(aspect, raw_images); @@ -809,49 +788,6 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface material = mat; } -Surface::Surface(TextureRuntime& runtime_, u32 width_, u32 height_, VideoCore::PixelFormat format_) - : SurfaceBase{{ - .width = width_, - .height = height_, - .pixel_format = format_, - .type = VideoCore::SurfaceType::Texture, - }}, - runtime{&runtime_}, instance{&runtime_.GetInstance()}, scheduler{&runtime_.GetScheduler()}, - traits{instance->GetTraits(format_)} { - - // Create texture with requested size and format - const vk::ImageUsageFlags usage = - vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | - vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled; - handles[0] = MakeHandle(instance, width_, height_, 1, VideoCore::TextureType::Texture2D, - traits.native, usage, {}, traits.aspect, false, "Temporary Surface"); - - // Create image view - const vk::ImageViewCreateInfo view_info = { - .image = handles[0].image, - .viewType = vk::ImageViewType::e2D, - .format = traits.native, - .subresourceRange{ - .aspectMask = traits.aspect, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - handles[0].image_view = instance->GetDevice().createImageViewUnique(view_info); - - runtime->renderpass_cache.EndRendering(); - scheduler->Record( - [raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - const auto barriers = MakeInitBarriers(aspect, raw_images); - cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eTopOfPipe, - vk::DependencyFlagBits::eByRegion, {}, {}, barriers); - }); -} - Surface::~Surface() { if (!handles[0].image_view) { return; @@ -940,23 +876,14 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, runtime->upload_buffer.Commit(staging.size); if (res_scale != 1) { - // Always ensure the scaled image exists - if (!handles[1].image) { - // This will create handles[1] and perform the initial scaling - ScaleUp(res_scale); - } else { - // Update the scaled version of the uploaded area - const VideoCore::TextureBlit blit = { - .src_level = upload.texture_level, - .dst_level = upload.texture_level, - .src_rect = upload.texture_rect, - .dst_rect = upload.texture_rect * res_scale, - }; - // Only apply texture filtering when upscaling, matching OpenGL behavior - if (res_scale != 1 && !runtime->blit_helper.Filter(*this, blit)) { - BlitScale(blit, true); - } - } + const VideoCore::TextureBlit blit = { + .src_level = upload.texture_level, + .dst_level = upload.texture_level, + .src_rect = upload.texture_rect, + .dst_rect = upload.texture_rect * res_scale, + }; + + BlitScale(blit, true); } } @@ -1322,6 +1249,11 @@ vk::ImageView Surface::ImageView(u32 index) const noexcept { return image_view; } +vk::ImageView Surface::FramebufferView() noexcept { + is_framebuffer = true; + return ImageView(); +} + vk::ImageView Surface::DepthView() noexcept { if (depth_view) { return depth_view.get(); @@ -1397,8 +1329,6 @@ vk::ImageView Surface::StorageView() noexcept { } vk::Framebuffer Surface::Framebuffer() noexcept { - is_framebuffer = true; - const u32 index = res_scale == 1 ? 0u : 1u; if (framebuffers[index]) { return framebuffers[index].get(); @@ -1409,29 +1339,12 @@ vk::Framebuffer Surface::Framebuffer() noexcept { const auto depth_format = is_depth ? pixel_format : PixelFormat::Invalid; const auto render_pass = runtime->renderpass_cache.GetRenderpass(color_format, depth_format, false); - // Use AttachmentView() to get single mip level view for framebuffer - const auto attachments = std::array{AttachmentView()}; + const auto attachments = std::array{ImageView()}; framebuffers[index] = MakeFramebuffer(instance->GetDevice(), render_pass, GetScaledWidth(), GetScaledHeight(), attachments); return framebuffers[index].get(); } -vk::ImageView Surface::AttachmentView() noexcept { - const vk::ImageViewCreateInfo view_info = { - .image = Image(), - .viewType = vk::ImageViewType::e2D, - .format = traits.native, - .subresourceRange{ - .aspectMask = traits.aspect, - .baseMipLevel = 0, - .levelCount = 1, // Single mip level for framebuffer - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - return instance->GetDevice().createImageViewUnique(view_info).release(); -} - void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { const FormatTraits& depth_traits = instance->GetTraits(pixel_format); const bool is_depth_stencil = pixel_format == PixelFormat::D24S8; @@ -1440,16 +1353,9 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { return; } - // Always use consistent source and destination images for proper scaling - // When upscaling: source = unscaled (0), destination = scaled (1) - // When downscaling: source = scaled (1), destination = unscaled (0) - const vk::Image src_image = up_scale ? Image(0) : Image(1); - const vk::Image dst_image = up_scale ? Image(1) : Image(0); - - scheduler->Record([src_image, aspect = Aspect(), filter = MakeFilter(pixel_format), dst_image, - src_access = AccessFlags(), dst_access = AccessFlags(), + scheduler->Record([src_image = Image(!up_scale), aspect = Aspect(), + filter = MakeFilter(pixel_format), dst_image = Image(up_scale), blit](vk::CommandBuffer render_cmdbuf) { - // Adjust blitting parameters for filtered upscaling const std::array source_offsets = { vk::Offset3D{static_cast(blit.src_rect.left), static_cast(blit.src_rect.bottom), 0}, @@ -1483,7 +1389,7 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { const std::array read_barriers = { vk::ImageMemoryBarrier{ - .srcAccessMask = src_access, + .srcAccessMask = vk::AccessFlagBits::eMemoryWrite, .dstAccessMask = vk::AccessFlagBits::eTransferRead, .oldLayout = vk::ImageLayout::eGeneral, .newLayout = vk::ImageLayout::eTransferSrcOptimal, @@ -1493,7 +1399,10 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { .subresourceRange = MakeSubresourceRange(aspect, blit.src_level), }, vk::ImageMemoryBarrier{ - .srcAccessMask = dst_access, + .srcAccessMask = vk::AccessFlagBits::eShaderRead | + vk::AccessFlagBits::eDepthStencilAttachmentRead | + vk::AccessFlagBits::eColorAttachmentRead | + vk::AccessFlagBits::eTransferRead, .dstAccessMask = vk::AccessFlagBits::eTransferWrite, .oldLayout = vk::ImageLayout::eGeneral, .newLayout = vk::ImageLayout::eTransferDstOptimal, @@ -1540,9 +1449,9 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { } Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, - Surface* color, Surface* depth_stencil) + Surface* color, Surface* depth) : VideoCore::FramebufferParams{params}, - res_scale{color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u)} { + res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)} { auto& renderpass_cache = runtime.GetRenderpassCache(); if (shadow_rendering && !color) { return; @@ -1559,41 +1468,27 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa } images[index] = surface->Image(); aspects[index] = surface->Aspect(); - // Use AttachmentView() for single-mip-level framebuffer attachment - image_views[index] = surface->AttachmentView(); + image_views[index] = shadow_rendering ? surface->StorageView() : surface->FramebufferView(); }; + boost::container::static_vector attachments; - if (!shadow_rendering) { - // Prepare the surfaces for use in framebuffer - if (color) { - prepare(0, color); - attachments.emplace_back(image_views[0]); - } + if (color) { + prepare(0, color); + attachments.emplace_back(image_views[0]); + } - if (depth_stencil) { - prepare(1, depth_stencil); - attachments.emplace_back(image_views[1]); - } - } else { - // For shadow rendering, just collect surface info without adding attachments - if (color) { - prepare(0, color); - } - if (depth_stencil) { - prepare(1, depth_stencil); - } + if (depth) { + prepare(1, depth); + attachments.emplace_back(image_views[1]); } const vk::Device device = runtime.GetInstance().GetDevice(); - if (shadow_rendering) { - // For shadow rendering, we don't need a framebuffer with attachments - // Just create a dummy render pass for the rendering pipeline render_pass = renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false); - // Don't create a framebuffer for shadow rendering - framebuffer.reset(); + framebuffer = MakeFramebuffer(device, render_pass, color->GetScaledWidth(), + color->GetScaledHeight(), {}); } else { render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false); framebuffer = MakeFramebuffer(device, render_pass, width, height, attachments); diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index 9c6bccd8d..e1745b22b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -1,4 +1,4 @@ -// Copyright Citra Emulator Project / Azahar Emulator Project +// Copyright 2023 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -110,8 +110,6 @@ public: explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params); explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceBase& surface, const VideoCore::Material* materal); - explicit Surface(TextureRuntime& runtime, u32 width_, u32 height_, - VideoCore::PixelFormat format_); ~Surface(); Surface(const Surface&) = delete; @@ -130,24 +128,12 @@ public: /// Returns the image view at index, otherwise the base view vk::ImageView ImageView(u32 index = 1) const noexcept; - /// Returns width of the surface - u32 GetWidth() const noexcept { - return width; - } - - /// Returns height of the surface - u32 GetHeight() const noexcept { - return height; - } - - /// Returns resolution scale of the surface - u32 GetResScale() const noexcept { - return res_scale; - } - /// Returns a copy of the upscaled image handle, used for feedback loops. vk::ImageView CopyImageView() noexcept; + /// Returns the framebuffer view of the surface image + vk::ImageView FramebufferView() noexcept; + /// Returns the depth view of the surface image vk::ImageView DepthView() noexcept; @@ -160,9 +146,6 @@ public: /// Returns a framebuffer handle for rendering to this surface vk::Framebuffer Framebuffer() noexcept; - /// Returns a single-mip-level view suitable for framebuffer attachments - vk::ImageView AttachmentView() noexcept; - /// Uploads pixel data in staging to a rectangle region of the surface texture void Upload(const VideoCore::BufferTextureCopy& upload, const VideoCore::StagingData& staging); @@ -248,12 +231,19 @@ public: return res_scale; } + u32 Width() const noexcept { + return width; + } + + u32 Height() const noexcept { + return height; + } + private: std::array images{}; std::array image_views{}; vk::UniqueFramebuffer framebuffer; vk::RenderPass render_pass; - std::vector framebuffer_views; std::array aspects{}; std::array formats{VideoCore::PixelFormat::Invalid, VideoCore::PixelFormat::Invalid};