diff --git a/src/video_core/rasterizer_cache/surface_params.cpp b/src/video_core/rasterizer_cache/surface_params.cpp index fe1db9ca2..4e4e3cc69 100644 --- a/src/video_core/rasterizer_cache/surface_params.cpp +++ b/src/video_core/rasterizer_cache/surface_params.cpp @@ -1,4 +1,4 @@ -// Copyright 2022 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 9ae8aadb0..fe78e974e 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -1,4 +1,4 @@ -// Copyright 2022 Citra Emulator Project +// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 0f997d5e9..0fb9ab449 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -558,6 +558,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { pipeline_info.state.attachments.color = framebuffer->Format(SurfaceType::Color); pipeline_info.state.attachments.depth = framebuffer->Format(SurfaceType::Depth); + pipeline_info.state.attachments.sample_count = framebuffer->Samples(); // Update scissor uniforms const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = fb_helper.Scissor(); @@ -777,6 +778,7 @@ bool RasterizerVulkan::AccelerateDisplay(const Pica::FramebufferConfig& config, src_params.stride = pixel_stride; src_params.is_tiled = false; src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format); + src_params.sample_count = (1u << Settings::values.sample_count.GetValue()); src_params.UpdateParams(); const auto [src_surface_id, src_rect] = diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 51adebb87..875658815 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -147,6 +147,11 @@ void MakeInitBarriers(vk::ImageAspectFlags aspect, u32 num_images, } } +void MakeInitBarriers(vk::ImageAspectFlags aspect, std::span images, + std::span out_barriers) { + MakeInitBarriers(aspect, images.size(), images, out_barriers); +} + vk::ImageSubresourceRange MakeSubresourceRange(vk::ImageAspectFlags aspect, u32 level = 0, u32 levels = 1, u32 layer = 0) { return vk::ImageSubresourceRange{ @@ -164,8 +169,8 @@ constexpr u64 DOWNLOAD_BUFFER_SIZE = 16_MiB; } // Anonymous namespace void Handle::Create(u32 width, u32 height, u32 levels, TextureType type, vk::Format format, - vk::ImageUsageFlags usage, vk::ImageCreateFlags flags, - vk::ImageAspectFlags aspect, bool need_format_list, + vk::SampleCountFlagBits samples, vk::ImageUsageFlags usage, + vk::ImageCreateFlags flags, vk::ImageAspectFlags aspect, bool need_format_list, std::string_view debug_name) { const bool is_cube_map = type == TextureType::CubeMap && instance.IsLayeredRenderingSupported(); if (!is_cube_map) { @@ -194,7 +199,7 @@ void Handle::Create(u32 width, u32 height, u32 levels, TextureType type, vk::For .extent = {width, height, 1}, .mipLevels = levels, .arrayLayers = layers, - .samples = vk::SampleCountFlagBits::e1, + .samples = samples, .usage = usage, }; @@ -344,7 +349,10 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea .src_image = surface.Image(), }; - if (clear.texture_rect == surface.GetScaledRect()) { + // MSAA images should always use a render-pass to clear both the MSAA texture and the regular + // texture at the same time + + if (clear.texture_rect == surface.GetScaledRect() && (surface.GetSampleCount() == 1)) { scheduler.Record([params, clear](vk::CommandBuffer cmdbuf) { const vk::ImageSubresourceRange range = { .aspectMask = params.aspect, @@ -407,7 +415,8 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, const auto color_format = is_color ? surface.pixel_format : PixelFormat::Invalid; const auto depth_format = is_color ? PixelFormat::Invalid : surface.pixel_format; - const auto render_pass = renderpass_cache.GetRenderpass(color_format, depth_format, true); + const auto render_pass = + renderpass_cache.GetRenderpass(color_format, depth_format, true, surface.GetSampleCount()); const RecordParams params = { .aspect = surface.Aspect(), @@ -462,13 +471,14 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, }; const auto clear_value = MakeClearValue(clear.value); + std::array clear_values = {clear_value, clear_value}; const vk::RenderPassBeginInfo renderpass_begin_info = { .renderPass = render_pass, .framebuffer = framebuffer, .renderArea = render_area, - .clearValueCount = 1, - .pClearValues = &clear_value, + .clearValueCount = static_cast(clear_values.size()), + .pClearValues = clear_values.data(), }; cmdbuf.pipelineBarrier(params.pipeline_flags, pipeline_flags, @@ -604,6 +614,94 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, .dst_image = dest.Image(), }; + // Todo(wunk): Add a "dirty" flag for msaa resolves to avoid redundant image resolves + const auto resolve_image = [&](Surface& msaa_surface) { + scheduler.Record([&msaa_surface](vk::CommandBuffer cmdbuf) { + const vk::ImageResolve resolve_area = { + .srcSubresource{ + .aspectMask = msaa_surface.Aspect(), + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = {}, + .dstSubresource{ + .aspectMask = msaa_surface.Aspect(), + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffset = {}, + .extent{msaa_surface.GetScaledWidth(), msaa_surface.GetScaledHeight(), 1}, + }; + + const std::array read_barriers = { + vk::ImageMemoryBarrier{ + .srcAccessMask = msaa_surface.AccessFlags(), + .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_surface.Image(Type::MultiSampled), + .subresourceRange = MakeSubresourceRange(msaa_surface.Aspect(), 0), + }, + vk::ImageMemoryBarrier{ + .srcAccessMask = msaa_surface.AccessFlags(), + .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_surface.Image(), + .subresourceRange = MakeSubresourceRange(msaa_surface.Aspect(), 0), + }, + }; + const std::array write_barriers = { + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eTransferRead, + .dstAccessMask = msaa_surface.AccessFlags(), + .oldLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_surface.Image(Type::MultiSampled), + .subresourceRange = MakeSubresourceRange(msaa_surface.Aspect(), 0), + }, + vk::ImageMemoryBarrier{ + .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = msaa_surface.AccessFlags(), + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = msaa_surface.Image(), + .subresourceRange = MakeSubresourceRange(msaa_surface.Aspect(), 0), + }, + }; + + cmdbuf.pipelineBarrier(msaa_surface.PipelineStageFlags(), + vk::PipelineStageFlagBits::eTransfer, + vk::DependencyFlagBits::eByRegion, {}, {}, read_barriers); + + cmdbuf.resolveImage(msaa_surface.Image(Type::MultiSampled), + vk::ImageLayout::eTransferSrcOptimal, msaa_surface.Image(), + vk::ImageLayout::eTransferDstOptimal, resolve_area); + + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + msaa_surface.PipelineStageFlags(), + vk::DependencyFlagBits::eByRegion, {}, {}, write_barriers); + }); + }; + + // Must resolve images first + if (source.sample_count > 1) { + resolve_image(source); + } + if (dest.sample_count > 1) { + resolve_image(dest); + } + scheduler.Record([params, blit](vk::CommandBuffer cmdbuf) { const std::array source_offsets = { vk::Offset3D{static_cast(blit.src_rect.left), @@ -754,8 +852,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param ASSERT_MSG(format != vk::Format::eUndefined && levels >= 1, "Image allocation parameters are invalid"); - u32 num_images{}; - std::array raw_images; + boost::container::static_vector raw_images; vk::ImageCreateFlags flags{}; if (texture_type == VideoCore::TextureType::CubeMap) { @@ -774,26 +871,40 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param } const bool need_format_list = is_mutable && instance.IsImageFormatListSupported(); - handles[Type::Base].Create(width, height, levels, texture_type, format, usage, flags, - traits.aspect, need_format_list, DebugName(false)); - raw_images[num_images++] = handles[Type::Base].image; + handles[Type::Base].Create(width, height, levels, texture_type, format, + vk::SampleCountFlagBits::e1, usage, flags, traits.aspect, + need_format_list, DebugName(false)); + raw_images.emplace_back(handles[Type::Base].image); + // Upscaled image if (res_scale != 1) { handles[Type::Scaled].Create(GetScaledWidth(), GetScaledHeight(), levels, texture_type, - format, usage, flags, traits.aspect, need_format_list, - DebugName(true)); - raw_images[num_images++] = handles[Type::Scaled].image; + format, vk::SampleCountFlagBits::e1, usage, flags, + traits.aspect, need_format_list, DebugName(true)); + raw_images.emplace_back(handles[Type::Scaled].image); } + // Upscaled+MSAA image + if (vk::SampleCountFlagBits(sample_count) > vk::SampleCountFlagBits::e1) { + handles[Type::MultiSampled].Create( + GetScaledWidth(), GetScaledHeight(), levels, texture_type, format, + vk::SampleCountFlagBits(sample_count), traits.usage, flags, traits.aspect, + need_format_list, DebugName(true, false, sample_count)); + raw_images.emplace_back(handles[Type::MultiSampled].image); + } + + // current = sample_count != 1 ? Type::MultiSampled : res_scale != 1 ? Type::Scaled : + // Type::Base; current = res_scale != 1 ? Type::Scaled : Type::Base; runtime.renderpass_cache.EndRendering(); - scheduler.Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - std::array barriers; - MakeInitBarriers(aspect, num_images, raw_images, barriers); - cmdbuf.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, - vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, num_images, barriers.data()); + scheduler.Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { + std::array barriers; + MakeInitBarriers(aspect, raw_images, barriers); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eTopOfPipe, + vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, + raw_images.size(), barriers.data()); }); } @@ -810,8 +921,7 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface const bool has_normal = mat && mat->Map(MapType::Normal); const vk::Format format = traits.native; - u32 num_images{}; - std::array raw_images; + boost::container::static_vector raw_images; vk::ImageCreateFlags flags{}; if (texture_type == VideoCore::TextureType::CubeMap) { @@ -819,31 +929,43 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface } const std::string debug_name = DebugName(false, true); - handles[Type::Base].Create(mat->width, mat->height, levels, texture_type, format, traits.usage, - flags, traits.aspect, false, debug_name); - raw_images[num_images++] = handles[Type::Base].image; + handles[Type::Base].Create(mat->width, mat->height, levels, texture_type, format, + vk::SampleCountFlagBits::e1, traits.usage, flags, traits.aspect, + false, debug_name); + raw_images.emplace_back(handles[Type::Base].image); if (res_scale != 1) { handles[Type::Scaled].Create(mat->width, mat->height, levels, texture_type, - vk::Format::eR8G8B8A8Unorm, traits.usage, flags, traits.aspect, - false, debug_name); - raw_images[num_images++] = handles[Type::Scaled].image; + vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1, + traits.usage, flags, traits.aspect, false, debug_name); + raw_images.emplace_back(handles[Type::Scaled].image); + } + if (sample_count > 1) { + handles[Type::MultiSampled].Create(GetScaledWidth(), GetScaledHeight(), levels, + texture_type, format, + vk::SampleCountFlagBits(sample_count), traits.usage, + flags, traits.aspect, false, debug_name); + raw_images.emplace_back(handles[Type::MultiSampled].image); } if (has_normal) { handles[Type::Custom].Create(mat->width, mat->height, levels, texture_type, format, - traits.usage, flags, traits.aspect, false, debug_name); - raw_images[num_images++] = handles[Type::Custom].image; + vk::SampleCountFlagBits::e1, traits.usage, flags, + traits.aspect, false, debug_name); + raw_images.emplace_back(handles[Type::Custom].image); } + // current = sample_count != 1 ? Type::MultiSampled : res_scale != 1 ? Type::Scaled : + // Type::Base; current = res_scale != 1 ? Type::Scaled : Type::Base; runtime.renderpass_cache.EndRendering(); - scheduler.Record([raw_images, num_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { - std::array barriers; - MakeInitBarriers(aspect, num_images, raw_images, barriers); - cmdbuf.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, - vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, num_images, barriers.data()); + scheduler.Record([raw_images, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { + std::array barriers; + MakeInitBarriers(aspect, raw_images, barriers); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eTopOfPipe, + vk::DependencyFlagBits::eByRegion, 0, nullptr, 0, nullptr, + raw_images.size(), barriers.data()); }); custom_format = mat->format; @@ -1099,12 +1221,6 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, } void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) { - if (res_scale == new_scale && sample_count == new_sample_count) { - return; - } - - res_scale = new_scale; - sample_count = new_sample_count; const bool is_mutable = pixel_format == VideoCore::PixelFormat::RGBA8; @@ -1116,12 +1232,13 @@ void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) { flags |= vk::ImageCreateFlagBits::eMutableFormat; } - if (res_scale > 1) { + if (res_scale != new_scale && res_scale > 1) { handles[Type::Scaled].Create(GetScaledWidth(), GetScaledHeight(), levels, texture_type, - traits.native, traits.usage, flags, traits.aspect, false, - DebugName(true)); + traits.native, vk::SampleCountFlagBits::e1, traits.usage, + flags, traits.aspect, false, DebugName(true)); current = Type::Scaled; + res_scale = new_scale; runtime.renderpass_cache.EndRendering(); scheduler.Record( @@ -1144,8 +1261,23 @@ void Surface::ScaleUp(u32 new_scale, u8 new_sample_count) { } } - if (new_sample_count > 1) { - // Todo(wunk): Vulkan MSAA + if (sample_count != new_sample_count && sample_count > 1) { + handles[Type::MultiSampled].Create(GetScaledWidth(), GetScaledHeight(), levels, + texture_type, traits.native, + vk::SampleCountFlagBits(sample_count), traits.usage, + flags, traits.aspect, false, DebugName(true)); + // current = Type::MultiSampled; + sample_count = new_sample_count; + + runtime.renderpass_cache.EndRendering(); + scheduler.Record( + [raw_images = std::array{Image()}, aspect = traits.aspect](vk::CommandBuffer cmdbuf) { + std::array barriers; + MakeInitBarriers(aspect, 1, raw_images, barriers); + cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eTopOfPipe, + vk::DependencyFlagBits::eByRegion, {}, {}, barriers); + }); } } @@ -1195,7 +1327,7 @@ vk::ImageView Surface::CopyImageView() noexcept { flags |= vk::ImageCreateFlagBits::eCubeCompatible; } copy_handle.Create(GetScaledWidth(), GetScaledHeight(), levels, texture_type, traits.native, - traits.usage, flags, traits.aspect, false); + vk::SampleCountFlagBits::e1, traits.usage, flags, traits.aspect, false); copy_layout = vk::ImageLayout::eUndefined; } @@ -1327,7 +1459,8 @@ vk::ImageView Surface::ImageView(ViewType view_type, Type type) noexcept { } vk::Framebuffer Surface::Framebuffer(Type type) noexcept { - auto& handle = handles[type == Type::Current ? current : type]; + type = Type::Current ? current : type; + auto& handle = handles[type]; if (handle.framebuffer) { return handle.framebuffer; } @@ -1337,11 +1470,20 @@ vk::Framebuffer Surface::Framebuffer(Type type) noexcept { const auto color_format = is_depth ? PixelFormat::Invalid : pixel_format; const auto depth_format = is_depth ? pixel_format : PixelFormat::Invalid; - const auto image_view = ImageView(ViewType::Mip0, type); + boost::container::small_vector image_views; + if (sample_count > 1) { + // Main surface + MSAA surface + image_views.emplace_back(ImageView(ViewType::Mip0)); + image_views.emplace_back(ImageView(ViewType::Mip0, Type::MultiSampled)); + } else { + image_views.emplace_back(ImageView(ViewType::Mip0, type)); + } + const vk::FramebufferCreateInfo framebuffer_info = { - .renderPass = runtime.renderpass_cache.GetRenderpass(color_format, depth_format, false), - .attachmentCount = 1u, - .pAttachments = &image_view, + .renderPass = + runtime.renderpass_cache.GetRenderpass(color_format, depth_format, false, sample_count), + .attachmentCount = static_cast(image_views.size()), + .pAttachments = image_views.data(), .width = handle.width, .height = handle.height, .layers = handle.layers, @@ -1458,7 +1600,8 @@ void Surface::BlitScale(const VideoCore::TextureBlit& blit, bool up_scale) { Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferParams& params, Surface* color, Surface* depth) : VideoCore::FramebufferParams{params}, instance{runtime.GetInstance()}, - res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)} { + res_scale{color ? color->res_scale : (depth ? depth->res_scale : 1u)}, + sample_count{params.sample_count} { auto& renderpass_cache = runtime.GetRenderpassCache(); if (shadow_rendering && !color) { return; @@ -1467,7 +1610,7 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa width = height = std::numeric_limits::max(); u32 num_attachments{}; - std::array attachments; + std::array attachments; const auto prepare = [&](u32 index, Surface* surface) { const auto extent = surface->RealExtent(); @@ -1483,8 +1626,8 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa const auto extent = color->RealExtent(); width = extent.width; height = extent.height; - render_pass = - renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, false); + render_pass = renderpass_cache.GetRenderpass(PixelFormat::Invalid, PixelFormat::Invalid, + false, sample_count); images[0] = color->Image(); image_views[0] = color->StorageView(); aspects[0] = vk::ImageAspectFlagBits::eColor; @@ -1497,7 +1640,23 @@ Framebuffer::Framebuffer(TextureRuntime& runtime, const VideoCore::FramebufferPa prepare(1, depth); attachments[num_attachments++] = image_views[1]; } - render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false); + + // MSAA attachments + if (sample_count > 1) { + if (color) { + images[2] = color->Image(Type::MultiSampled); + image_views[2] = color->ImageView(ViewType::Mip0, Type::MultiSampled); + aspects[2] = color->Aspect(); + attachments[num_attachments++] = image_views[2]; + } + if (depth) { + images[3] = depth->Image(Type::MultiSampled); + image_views[3] = depth->ImageView(ViewType::Mip0, Type::MultiSampled); + aspects[3] = depth->Aspect(); + attachments[num_attachments++] = image_views[3]; + } + } + render_pass = renderpass_cache.GetRenderpass(formats[0], formats[1], false, sample_count); } const vk::FramebufferCreateInfo framebuffer_info = { diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index 98b140f96..f6314185b 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -80,8 +80,9 @@ struct Handle { } void Create(u32 width, u32 height, u32 levels, VideoCore::TextureType type, vk::Format format, - vk::ImageUsageFlags usage, vk::ImageCreateFlags flags, vk::ImageAspectFlags aspect, - bool need_format_list, std::string_view debug_name = {}); + vk::SampleCountFlagBits samples, vk::ImageUsageFlags usage, + vk::ImageCreateFlags flags, vk::ImageAspectFlags aspect, bool need_format_list, + std::string_view debug_name = {}); void Destroy(); @@ -338,11 +339,11 @@ public: return framebuffer; } - [[nodiscard]] std::array Images() const noexcept { + [[nodiscard]] std::array Images() const noexcept { return images; } - [[nodiscard]] std::array Aspects() const noexcept { + [[nodiscard]] std::array Aspects() const noexcept { return aspects; } @@ -350,23 +351,30 @@ public: return render_pass; } + u8 Samples() const noexcept { + return sample_count; + } + u32 Scale() const noexcept { return res_scale; } private: const Instance& instance; - std::array images{}; - std::array image_views{}; + // Color, Depth, ColorMSAA, DepthMSAA + std::array images{}; + std::array image_views{}; vk::Framebuffer framebuffer{}; vk::RenderPass render_pass{}; std::vector framebuffer_views; - std::array aspects{}; - std::array formats{VideoCore::PixelFormat::Invalid, - VideoCore::PixelFormat::Invalid}; + std::array aspects{}; + std::array formats{ + VideoCore::PixelFormat::Invalid, VideoCore::PixelFormat::Invalid, + VideoCore::PixelFormat::Invalid, VideoCore::PixelFormat::Invalid}; u32 width{}; u32 height{}; u32 res_scale{1}; + u8 sample_count{1}; }; class Sampler {