mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-05-12 15:49:39 -06:00
video_core/renderer_vulkan: fix shadow rendering (#1634)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (appimage-wayland) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux (appimage) (push) Waiting to run
citra-build / linux (appimage-wayland) (push) Waiting to run
citra-build / linux (fresh) (push) Waiting to run
citra-build / macos (arm64) (push) Waiting to run
citra-build / macos (x86_64) (push) Waiting to run
citra-build / macos-universal (push) Blocked by required conditions
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
This commit is contained in:
parent
4deb7e63b5
commit
0571187bd3
@ -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<vk::Filter::eLinear>)},
|
||||
nearest_sampler{device.createSampler(SAMPLER_CREATE_INFO<vk::Filter::eNearest>)} {
|
||||
|
||||
@ -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<u32>(stages.size()),
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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<const VideoCore::TextureCopy> 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<vk::ImageView, 2> 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<vk::ImageView, 2> 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<vk::ImageView, 1> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Handle, 3> handles{};
|
||||
std::array<vk::UniqueFramebuffer, 2> framebuffers{};
|
||||
Handle copy_handle;
|
||||
std::array<vk::UniqueImageView, 2> 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<vk::Image, 2> images{};
|
||||
std::array<vk::ImageView, 2> image_views{};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user