diff --git a/src/video_core/rasterizer_cache/framebuffer_base.h b/src/video_core/rasterizer_cache/framebuffer_base.h index 2f2545849..f7387b79f 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.h +++ b/src/video_core/rasterizer_cache/framebuffer_base.h @@ -27,7 +27,8 @@ struct FramebufferParams { u32 color_level; u32 depth_level; bool shadow_rendering; - INSERT_PADDING_BYTES(3); + u8 sample_count; + INSERT_PADDING_BYTES(2); bool operator==(const FramebufferParams& params) const noexcept { return std::memcmp(this, ¶ms, sizeof(FramebufferParams)) == 0; diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 26156cb56..fbf53205a 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -38,7 +38,7 @@ RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, Pica::RegsInternal& regs_, RendererBase& renderer_) : memory{memory_}, custom_tex_manager{custom_tex_manager_}, runtime{runtime_}, regs{regs_}, renderer{renderer_}, resolution_scale_factor{renderer.GetResolutionScaleFactor()}, - filter{Settings::values.texture_filter.GetValue()}, + sample_count{renderer.GetSampleCount()}, filter{Settings::values.texture_filter.GetValue()}, dump_textures{Settings::values.dump_textures.GetValue()}, use_custom_textures{Settings::values.custom_textures.GetValue()} { using TextureConfig = Pica::TexturingRegs::TextureConfig; @@ -96,12 +96,15 @@ void RasterizerCache::TickFrame() { } const u32 scale_factor = renderer.GetResolutionScaleFactor(); + const u32 samples = renderer.GetSampleCount(); const bool resolution_scale_changed = resolution_scale_factor != scale_factor; + const bool sample_count_changed = sample_count != samples; const bool use_custom_texture_changed = Settings::values.custom_textures.GetValue() != use_custom_textures; - if (resolution_scale_changed || use_custom_texture_changed) { + if (resolution_scale_changed || use_custom_texture_changed || sample_count_changed) { resolution_scale_factor = scale_factor; + sample_count = renderer.GetSampleCount(); use_custom_textures = Settings::values.custom_textures.GetValue(); if (use_custom_textures) { custom_tex_manager.FindCustomTextures(); @@ -287,6 +290,7 @@ bool RasterizerCache::AccelerateDisplayTransfer(const Pica::DisplayTransferCo : config.output_height.Value(); dst_params.is_tiled = config.input_linear != config.dont_swizzle; dst_params.pixel_format = PixelFormatFromGPUPixelFormat(config.output_format); + dst_params.sample_count = sample_count; dst_params.UpdateParams(); // Using flip_vertically alongside crop_input_lines produces skewed output on hardware. @@ -302,6 +306,7 @@ bool RasterizerCache::AccelerateDisplayTransfer(const Pica::DisplayTransferCo } dst_params.res_scale = slot_surfaces[src_surface_id].res_scale; + dst_params.sample_count = slot_surfaces[src_surface_id].sample_count; const auto [dst_surface_id, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); @@ -432,8 +437,10 @@ void RasterizerCache::CopySurface(Surface& src_surface, Surface& dst_surface, const u32 src_scale = src_surface.res_scale; const u32 dst_scale = dst_surface.res_scale; - if (src_scale > dst_scale) { - dst_surface.ScaleUp(src_scale); + const u32 src_sample_count = src_surface.sample_count; + const u32 dst_sample_count = dst_surface.sample_count; + if ((src_scale > dst_scale) || (src_sample_count > dst_sample_count)) { + dst_surface.ScaleUp(src_scale, src_sample_count); } const auto src_rect = src_surface.GetScaledSubRect(subrect_params); @@ -502,6 +509,7 @@ typename RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSub if (surface_id) { SurfaceParams new_params = slot_surfaces[surface_id]; new_params.res_scale = params.res_scale; + new_params.sample_count = params.sample_count; surface_id = CreateSurface(new_params, create_initial_flags); RegisterSurface(surface_id); @@ -706,6 +714,7 @@ FramebufferHelper RasterizerCache::GetFramebufferSurfaces(bool using_color SurfaceParams color_params; color_params.is_tiled = true; color_params.res_scale = resolution_scale_factor; + color_params.sample_count = sample_count; color_params.width = config.GetWidth(); color_params.height = config.GetHeight(); SurfaceParams depth_params = color_params; @@ -771,6 +780,7 @@ FramebufferHelper RasterizerCache::GetFramebufferSurfaces(bool using_color .color_level = color_level, .depth_level = depth_level, .shadow_rendering = regs.framebuffer.IsShadowRendering(), + .sample_count = sample_count, }; auto [it, new_framebuffer] = framebuffers.try_emplace(fb_params); @@ -861,12 +871,16 @@ SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch SurfaceId match_id{}; bool match_valid = false; u32 match_scale = 0; + u8 match_sample_count = 0; SurfaceInterval match_interval{}; ForEachSurfaceInRegion(params.addr, params.size, [&](SurfaceId surface_id, Surface& surface) { const bool res_scale_matched = match_scale_type == ScaleMatch::Exact ? (params.res_scale == surface.res_scale) : (params.res_scale <= surface.res_scale); + const bool sample_count_matched = match_scale_type == ScaleMatch::Exact + ? (params.sample_count == surface.sample_count) + : (params.sample_count <= surface.sample_count); const bool is_valid = True(find_flags & MatchFlags::Copy) ? true @@ -886,11 +900,16 @@ SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch surface.type != SurfaceType::Fill) return; + if (!sample_count_matched && match_scale_type != ScaleMatch::Ignore && + surface.type != SurfaceType::Fill) + return; + // Found a match, update only if this is better than the previous one auto UpdateMatch = [&] { match_id = surface_id; match_valid = is_valid; match_scale = surface.res_scale; + match_sample_count = surface.sample_count; match_interval = surface_interval; }; @@ -901,6 +920,13 @@ SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch return; } + if (surface.sample_count > match_sample_count) { + UpdateMatch(); + return; + } else if (surface.sample_count < match_sample_count) { + return; + } + if (is_valid && !match_valid) { UpdateMatch(); return; @@ -1189,8 +1215,9 @@ bool RasterizerCache::ValidateByReinterpretation(Surface& surface, SurfacePar return false; } const u32 res_scale = src_surface.res_scale; - if (res_scale > surface.res_scale) { - surface.ScaleUp(res_scale); + const u8 sample_count = src_surface.sample_count; + if ((res_scale > surface.res_scale) || (sample_count > surface.sample_count)) { + surface.ScaleUp(res_scale, sample_count); } const PAddr addr = boost::icl::lower(interval); const SurfaceParams copy_params = surface.FromInterval(copy_interval); @@ -1357,8 +1384,8 @@ SurfaceId RasterizerCache::CreateSurface(const SurfaceParams& params, return surface_id; }(); Surface& surface = slot_surfaces[surface_id]; - if (params.res_scale > surface.res_scale) { - surface.ScaleUp(params.res_scale); + if ((params.res_scale > surface.res_scale) || (params.sample_count > surface.sample_count)) { + surface.ScaleUp(params.res_scale, params.sample_count); } surface.MarkInvalid(surface.GetInterval()); return surface_id; diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_base.h b/src/video_core/rasterizer_cache/rasterizer_cache_base.h index 406c374dc..0ddf6a713 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache_base.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache_base.h @@ -227,6 +227,7 @@ private: SurfaceMap dirty_regions; PageMap cached_pages; u32 resolution_scale_factor; + u8 sample_count; u64 frame_tick{}; FramebufferParams fb_params; Settings::TextureFilter filter; diff --git a/src/video_core/rasterizer_cache/surface_params.cpp b/src/video_core/rasterizer_cache/surface_params.cpp index 74ae8205c..fe1db9ca2 100644 --- a/src/video_core/rasterizer_cache/surface_params.cpp +++ b/src/video_core/rasterizer_cache/surface_params.cpp @@ -219,12 +219,13 @@ u32 SurfaceParams::LevelOf(PAddr level_addr) const { return level; } -std::string SurfaceParams::DebugName(bool scaled, bool custom) const noexcept { +std::string SurfaceParams::DebugName(bool scaled, bool custom, u8 sample_count) const noexcept { const u32 scaled_width = scaled ? GetScaledWidth() : width; const u32 scaled_height = scaled ? GetScaledHeight() : height; - return fmt::format("Surface: {}x{} {} {} levels from {:#x} to {:#x} ({}{})", scaled_width, - scaled_height, PixelFormatAsString(pixel_format), levels, addr, end, - custom ? "custom," : "", scaled ? "scaled" : "unscaled"); + return fmt::format("Surface: {}x{} {} samples {} levels from {:#x} to {:#x} ({}{})", + scaled_width, scaled_height, PixelFormatAsString(pixel_format), + static_cast(sample_count), levels, addr, end, custom ? "custom," : "", + scaled ? "scaled" : "unscaled"); } bool SurfaceParams::operator==(const SurfaceParams& other) const noexcept { diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 23e4db7e2..9ae8aadb0 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -51,7 +51,7 @@ public: u32 LevelOf(PAddr addr) const; /// Returns a string identifier of the params object - std::string DebugName(bool scaled, bool custom = false) const noexcept; + std::string DebugName(bool scaled, bool custom = false, u8 sample_count = 1) const noexcept; bool operator==(const SurfaceParams& other) const noexcept; @@ -71,6 +71,10 @@ public: return height * res_scale; } + [[nodiscard]] u8 GetSampleCount() const noexcept { + return sample_count; + } + [[nodiscard]] Common::Rectangle GetRect(u32 level = 0) const noexcept { return {0, height >> level, width >> level, 0}; } @@ -104,6 +108,7 @@ public: u32 stride = 0; u32 levels = 1; u32 res_scale = 1; + u8 sample_count = 1; bool is_tiled = false; TextureType texture_type = TextureType::Texture2D; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index fc14e949f..4ce41a0fc 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -556,23 +556,31 @@ void Surface::Attach(GLenum target, u32 level, u32 layer, bool scaled) { } } -void Surface::ScaleUp(u32 new_scale) { - if (res_scale == new_scale || new_scale == 1) { +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; - textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, - DebugName(true)); + sample_count = new_sample_count; - for (u32 level = 0; level < levels; level++) { - const VideoCore::TextureBlit blit = { - .src_level = level, - .dst_level = level, - .src_rect = GetRect(level), - .dst_rect = GetScaledRect(level), - }; - BlitScale(blit, true); + if (res_scale > 1) { + + textures[1] = MakeHandle(GL_TEXTURE_2D, GetScaledWidth(), GetScaledHeight(), levels, tuple, + DebugName(true)); + for (u32 level = 0; level < levels; level++) { + const VideoCore::TextureBlit blit = { + .src_level = level, + .dst_level = level, + .src_rect = GetRect(level), + .dst_rect = GetScaledRect(level), + }; + BlitScale(blit, true); + } + } + + if (new_sample_count > 1) { + // Todo(wunk): OpenGL MSAA } } diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index d25ba102c..434a3c464 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -132,8 +132,8 @@ public: /// Attaches a handle of surface to the specified framebuffer target void Attach(GLenum target, u32 level, u32 layer, bool scaled = true); - /// Scales up the surface to match the new resolution scale. - void ScaleUp(u32 new_scale); + /// Scales up the surface to match the new resolution scale and sample-count. + void ScaleUp(u32 new_scale, u8 new_sample_count); /// Returns the bpp of the internal surface format u32 GetInternalBytesPerPixel() const; diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index c62757384..51adebb87 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -731,7 +731,8 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& param const VideoCore::SurfaceFlagBits& initial_flag_bits) : SurfaceBase{params, initial_flag_bits}, runtime{runtime_}, instance{runtime_.GetInstance()}, scheduler{runtime_.GetScheduler()}, traits{instance.GetTraits(pixel_format)}, - handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance)} { + handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance), + Handle(instance)} { if (pixel_format == VideoCore::PixelFormat::Invalid || !traits.transfer_support) { return; @@ -800,7 +801,8 @@ Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceBase& surface const VideoCore::Material* mat) : SurfaceBase{surface}, runtime{runtime_}, instance{runtime_.GetInstance()}, scheduler{runtime_.GetScheduler()}, traits{instance.GetTraits(mat->format)}, - handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance)} { + handles{Handle(instance), Handle(instance), Handle(instance), Handle(instance), + Handle(instance)} { if (!traits.transfer_support) { return; } @@ -1096,12 +1098,13 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, }); } -void Surface::ScaleUp(u32 new_scale) { - if (res_scale == new_scale || new_scale == 1) { +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; @@ -1113,29 +1116,36 @@ void Surface::ScaleUp(u32 new_scale) { flags |= vk::ImageCreateFlagBits::eMutableFormat; } - handles[Type::Scaled].Create(GetScaledWidth(), GetScaledHeight(), levels, texture_type, - traits.native, traits.usage, flags, traits.aspect, false, - DebugName(true)); - current = Type::Scaled; + if (res_scale > 1) { - 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); - }); + handles[Type::Scaled].Create(GetScaledWidth(), GetScaledHeight(), levels, texture_type, + traits.native, traits.usage, flags, traits.aspect, false, + DebugName(true)); + current = Type::Scaled; - for (u32 level = 0; level < levels; level++) { - const VideoCore::TextureBlit blit = { - .src_level = level, - .dst_level = level, - .src_rect = GetRect(level), - .dst_rect = GetScaledRect(level), - }; - BlitScale(blit, true); + 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); + }); + + for (u32 level = 0; level < levels; level++) { + const VideoCore::TextureBlit blit = { + .src_level = level, + .dst_level = level, + .src_rect = GetRect(level), + .dst_rect = GetScaledRect(level), + }; + BlitScale(blit, true); + } + } + + if (new_sample_count > 1) { + // Todo(wunk): Vulkan MSAA } } diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index b46479c58..98b140f96 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -29,6 +29,7 @@ enum Type { Current = -1, Base = 0, Scaled, + MultiSampled, Custom, Copy, Num, @@ -252,8 +253,8 @@ public: void Download(const VideoCore::BufferTextureCopy& download, const VideoCore::StagingData& staging); - /// Scales up the surface to match the new resolution scale. - void ScaleUp(u32 new_scale); + /// Scales up the surface to match the new resolution scale and sample-count. + void ScaleUp(u32 new_scale, u8 new_sample_count); /// Returns the bpp of the internal surface format u32 GetInternalBytesPerPixel() const;