From 0571187bd31052e4de7bcdf4de22737d6a6891bb Mon Sep 17 00:00:00 2001 From: jbm11208 <81182113+jbm11208@users.noreply.github.com> Date: Sun, 18 Jan 2026 17:56:35 -0500 Subject: [PATCH] video_core/renderer_vulkan: fix shadow rendering (#1634) --- .../renderer_vulkan/vk_blit_helper.cpp | 52 +++--- .../renderer_vulkan/vk_blit_helper.h | 10 +- .../renderer_vulkan/vk_texture_runtime.cpp | 158 ++++++------------ .../renderer_vulkan/vk_texture_runtime.h | 15 +- 4 files changed, 85 insertions(+), 150 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.cpp b/src/video_core/renderer_vulkan/vk_blit_helper.cpp index 679ab3dcd..62f38e8f6 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_helper.cpp @@ -263,12 +263,6 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, depth_to_buffer_pipeline{ MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)}, depth_blit_pipeline{MakeDepthStencilBlitPipeline()}, - // Texture filtering pipelines - bicubic_pipeline{MakeFilterPipeline(bicubic_frag, single_texture_pipeline_layout)}, - scale_force_pipeline{MakeFilterPipeline(scale_force_frag, single_texture_pipeline_layout)}, - xbrz_pipeline{MakeFilterPipeline(xbrz_frag, single_texture_pipeline_layout)}, - mmpx_pipeline{MakeFilterPipeline(mmpx_frag, single_texture_pipeline_layout)}, - refine_pipeline{MakeFilterPipeline(refine_frag, three_textures_pipeline_layout)}, linear_sampler{device.createSampler(SAMPLER_CREATE_INFO)}, nearest_sampler{device.createSampler(SAMPLER_CREATE_INFO)} { @@ -311,12 +305,6 @@ BlitHelper::~BlitHelper() { device.destroyPipeline(depth_to_buffer_pipeline); device.destroyPipeline(d24s8_to_rgba8_pipeline); device.destroyPipeline(depth_blit_pipeline); - // Destroy texture filtering pipelines - device.destroyPipeline(bicubic_pipeline); - device.destroyPipeline(scale_force_pipeline); - device.destroyPipeline(xbrz_pipeline); - device.destroyPipeline(mmpx_pipeline); - device.destroyPipeline(refine_pipeline); device.destroySampler(linear_sampler); device.destroySampler(nearest_sampler); } @@ -665,32 +653,54 @@ bool BlitHelper::Filter(Surface& surface, const VideoCore::TextureBlit& blit) { } void BlitHelper::FilterAnime4K(Surface& surface, const VideoCore::TextureBlit& blit) { - FilterPassThreeTextures(surface, surface, surface, surface, refine_pipeline, + 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) { - FilterPass(surface, surface, bicubic_pipeline, single_texture_pipeline_layout, 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) { - FilterPass(surface, surface, scale_force_pipeline, single_texture_pipeline_layout, 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) { - FilterPass(surface, surface, xbrz_pipeline, single_texture_pipeline_layout, 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) { - FilterPass(surface, surface, mmpx_pipeline, single_texture_pipeline_layout, 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) { + vk::PipelineLayout layout, + VideoCore::PixelFormat color_format) { const std::array stages = MakeStages(full_screen_vert, fragment_shader); - // Use color format for render pass, always a color target - const auto renderpass = renderpass_cache.GetRenderpass(VideoCore::PixelFormat::RGBA8, - VideoCore::PixelFormat::Invalid, false); + // 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()), diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.h b/src/video_core/renderer_vulkan/vk_blit_helper.h index 030c65f5c..923f08cc9 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.h +++ b/src/video_core/renderer_vulkan/vk_blit_helper.h @@ -4,6 +4,7 @@ #pragma once +#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/renderer_vulkan/vk_resource_pool.h" namespace VideoCore { @@ -39,7 +40,9 @@ public: private: vk::Pipeline MakeComputePipeline(vk::ShaderModule shader, vk::PipelineLayout layout); vk::Pipeline MakeDepthStencilBlitPipeline(); - vk::Pipeline MakeFilterPipeline(vk::ShaderModule fragment_shader, vk::PipelineLayout layout); + 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); @@ -90,11 +93,6 @@ private: vk::Pipeline d24s8_to_rgba8_pipeline; vk::Pipeline depth_to_buffer_pipeline; vk::Pipeline depth_blit_pipeline; - vk::Pipeline bicubic_pipeline; - vk::Pipeline scale_force_pipeline; - vk::Pipeline xbrz_pipeline; - vk::Pipeline mmpx_pipeline; - vk::Pipeline refine_pipeline; vk::Sampler linear_sampler; vk::Sampler nearest_sampler; }; diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 6b9b388fa..7bbc2a019 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -465,49 +465,6 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, }); } -vk::UniqueImageView MakeFramebufferImageView(vk::Device device, vk::Image image, vk::Format format, - vk::ImageAspectFlags aspect, u32 base_level = 0) { - // For framebuffer attachments, we must always use levelCount=1 to avoid - // Vulkan validation errors about mipLevel being outside of the allowed range - const vk::ImageViewCreateInfo view_info = { - .image = image, - .viewType = vk::ImageViewType::e2D, - .format = format, - .subresourceRange{ - .aspectMask = aspect, - .baseMipLevel = base_level, // Use the specified base mip level - .levelCount = 1, // Framebuffers require a single mip level - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - return device.createImageViewUnique(view_info); -} - -vk::ImageView CreateFramebufferImageView(const Instance* instance, vk::Image image, - vk::Format format, vk::ImageAspectFlags aspect) { - // Always create a view with a single mip level for framebuffer attachments - const vk::ImageViewCreateInfo view_info = { - .image = image, - .viewType = vk::ImageViewType::e2D, - .format = format, - .subresourceRange{ - .aspectMask = aspect, - .baseMipLevel = 0, - .levelCount = 1, // Always use 1 for framebuffers to avoid Vulkan validation errors - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - return instance->GetDevice().createImageView(view_info); -} - -bool IsImagelessFramebufferSupported(const Instance* instance) { - // We're not using imageless framebuffers to avoid validation errors - // Even if the extension is supported, we'll use standard framebuffers for better compatibility - return false; -} - bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, std::span copies) { renderpass_cache.EndRendering(); @@ -866,7 +823,6 @@ Surface::Surface(TextureRuntime& runtime_, u32 width_, u32 height_, VideoCore::P 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"); @@ -1366,24 +1322,6 @@ vk::ImageView Surface::ImageView(u32 index) const noexcept { return image_view; } -vk::ImageView Surface::FramebufferView() noexcept { - is_framebuffer = true; - - const u32 index = res_scale == 1 ? 0u : 1u; - - // If we already have a framebuffer-compatible view, return it - if (framebuffer_view[index]) { - return framebuffer_view[index].get(); - } - - // Create a new view with a single mip level for framebuffer compatibility - // This is critical to avoid VUID-VkFramebufferCreateInfo-pAttachments-00883 validation errors - framebuffer_view[index] = MakeFramebufferImageView( - instance->GetDevice(), Image(), instance->GetTraits(pixel_format).native, Aspect(), 0); - - return framebuffer_view[index].get(); -} - vk::ImageView Surface::DepthView() noexcept { if (depth_view) { return depth_view.get(); @@ -1471,13 +1409,29 @@ 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 FramebufferView() instead of ImageView() to ensure single mip level - const auto attachments = std::array{FramebufferView()}; + // Use AttachmentView() to get single mip level view for framebuffer + const auto attachments = std::array{AttachmentView()}; 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; @@ -1605,62 +1559,44 @@ 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(); }; + boost::container::static_vector attachments; - // Prepare the surfaces for use in framebuffer - if (color) { - prepare(0, color); - } + if (!shadow_rendering) { + // Prepare the surfaces for use in framebuffer + if (color) { + prepare(0, color); + attachments.emplace_back(image_views[0]); + } - if (depth_stencil) { - prepare(1, depth_stencil); + 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); + } } const vk::Device device = runtime.GetInstance().GetDevice(); - // Create appropriate image views for the framebuffer - boost::container::static_vector fb_attachments; - - if (color) { - vk::UniqueImageView single_level_view = - MakeFramebufferImageView(device, color->Image(), color->traits.native, color->Aspect()); - fb_attachments.push_back(single_level_view.get()); - framebuffer_views.push_back(std::move(single_level_view)); - } - - if (depth_stencil) { - vk::UniqueImageView single_level_view = MakeFramebufferImageView( - device, depth_stencil->Image(), depth_stencil->traits.native, depth_stencil->Aspect()); - fb_attachments.push_back(single_level_view.get()); - framebuffer_views.push_back(std::move(single_level_view)); - } if (shadow_rendering) { - // For shadow rendering, we need a special render pass with depth-only - // Since shadow rendering doesn't output to color buffer, we use depth-only render pass - render_pass = renderpass_cache.GetRenderpass(PixelFormat::Invalid, formats[1], false); - - // Find the depth attachment in fb_attachments - boost::container::static_vector shadow_attachments; - if (depth_stencil) { - // Depth attachment is the last one added (after color if present) - shadow_attachments.push_back(fb_attachments.back()); - } else if (!fb_attachments.empty()) { - // Fallback to first attachment if no depth_stencil - shadow_attachments.push_back(fb_attachments[0]); - } - - // Create framebuffer with depth attachment only - framebuffer = MakeFramebuffer( - device, render_pass, color ? color->GetScaledWidth() : depth_stencil->GetScaledWidth(), - color ? color->GetScaledHeight() : depth_stencil->GetScaledHeight(), - shadow_attachments); + // 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(); } else { - // For normal rendering, create a render pass that matches our attachments - render_pass = renderpass_cache.GetRenderpass( - color ? formats[0] : PixelFormat::Invalid, - depth_stencil ? formats[1] : PixelFormat::Invalid, false); - // Create the framebuffer with attachments matching the render pass - framebuffer = MakeFramebuffer(device, render_pass, width, height, fb_attachments); + 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 ecd7a5ef4..9c6bccd8d 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -148,9 +148,6 @@ public: /// 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; @@ -163,6 +160,9 @@ 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); @@ -201,7 +201,6 @@ public: std::array handles{}; std::array framebuffers{}; Handle copy_handle; - std::array framebuffer_view; vk::UniqueImageView depth_view; vk::UniqueImageView stencil_view; vk::UniqueImageView storage_view; @@ -249,14 +248,6 @@ public: return res_scale; } - u32 Width() const noexcept { - return width; - } - - u32 Height() const noexcept { - return height; - } - private: std::array images{}; std::array image_views{};