From 06caa535d60e71c53f0ea835c9dc5766b50f18e2 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Mon, 27 Feb 2023 18:37:39 +0200 Subject: [PATCH] rasterizer_cache: Remove usage of shared_ptr --- src/video_core/rasterizer_accelerated.h | 2 +- .../rasterizer_cache/framebuffer_base.cpp | 9 +- .../rasterizer_cache/framebuffer_base.h | 14 +- .../rasterizer_cache/rasterizer_cache.h | 638 ++++++++++-------- .../rasterizer_cache/rasterizer_cache_base.h | 65 +- .../rasterizer_cache/surface_base.h | 3 +- .../rasterizer_cache/surface_params.h | 1 + src/video_core/rasterizer_cache/utils.cpp | 43 +- src/video_core/rasterizer_cache/utils.h | 7 +- .../renderer_opengl/gl_format_reinterpreter.h | 1 + .../renderer_opengl/gl_rasterizer.cpp | 221 +++--- .../renderer_opengl/gl_rasterizer.h | 13 +- src/video_core/renderer_opengl/gl_state.h | 17 +- .../renderer_opengl/gl_texture_runtime.cpp | 44 +- .../renderer_opengl/gl_texture_runtime.h | 14 +- src/video_core/renderer_vulkan/vk_common.h | 1 - .../renderer_vulkan/vk_format_reinterpreter.h | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 235 +++---- .../renderer_vulkan/vk_rasterizer.h | 12 +- .../renderer_vulkan/vk_texture_runtime.cpp | 126 ++-- .../renderer_vulkan/vk_texture_runtime.h | 59 +- 21 files changed, 777 insertions(+), 749 deletions(-) diff --git a/src/video_core/rasterizer_accelerated.h b/src/video_core/rasterizer_accelerated.h index 94e700151..acf711b26 100644 --- a/src/video_core/rasterizer_accelerated.h +++ b/src/video_core/rasterizer_accelerated.h @@ -143,7 +143,7 @@ protected: Memory::MemorySystem& memory; Pica::Regs& regs; - VertexArrayInfo vertex_info; + VertexArrayInfo vertex_info{}; std::vector vertex_batch; bool shader_dirty = true; diff --git a/src/video_core/rasterizer_cache/framebuffer_base.cpp b/src/video_core/rasterizer_cache/framebuffer_base.cpp index ce03b26d2..fafd5a38f 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.cpp +++ b/src/video_core/rasterizer_cache/framebuffer_base.cpp @@ -10,10 +10,9 @@ namespace VideoCore { FramebufferBase::FramebufferBase() = default; -FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color_, - SurfaceBase* const depth_stencil_, - Common::Rectangle surfaces_rect) - : color{color_}, depth_stencil{depth_stencil_} { +FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color, + SurfaceBase* const depth_stencil, + Common::Rectangle surfaces_rect) { res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u); // Determine the draw rectangle (render area + scissor) @@ -54,9 +53,11 @@ FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const colo // Query surface invalidation intervals const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale}; if (color) { + color_params = *color; intervals[0] = color->GetSubRectInterval(draw_rect_unscaled); } if (depth_stencil) { + depth_params = *depth_stencil; intervals[1] = depth_stencil->GetSubRectInterval(draw_rect_unscaled); } } diff --git a/src/video_core/rasterizer_cache/framebuffer_base.h b/src/video_core/rasterizer_cache/framebuffer_base.h index 4e27a0343..14783bbdf 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.h +++ b/src/video_core/rasterizer_cache/framebuffer_base.h @@ -5,7 +5,7 @@ #pragma once #include "common/math_util.h" -#include "video_core/rasterizer_cache/utils.h" +#include "video_core/rasterizer_cache/surface_params.h" namespace Pica { struct Regs; @@ -32,12 +32,12 @@ public: FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color, SurfaceBase* const depth_stencil, Common::Rectangle surfaces_rect); - SurfaceBase* Color() const noexcept { - return color; + SurfaceParams ColorParams() const noexcept { + return color_params; } - SurfaceBase* DepthStencil() const noexcept { - return depth_stencil; + SurfaceParams DepthParams() const noexcept { + return depth_params; } SurfaceInterval Interval(SurfaceType type) const noexcept { @@ -74,8 +74,8 @@ protected: } protected: - SurfaceBase* color{}; - SurfaceBase* depth_stencil{}; + SurfaceParams color_params{}; + SurfaceParams depth_params{}; std::array intervals{}; Common::Rectangle scissor_rect{}; Common::Rectangle draw_rect{}; diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index e6233ec3b..26e75f5da 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -37,6 +37,14 @@ RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, using TextureConfig = Pica::TexturingRegs::TextureConfig; // Create null handles for all cached resources + void(slot_surfaces.insert(runtime, SurfaceParams{ + .width = 1, + .height = 1, + .stride = 1, + .texture_type = VideoCore::TextureType::Texture2D, + .pixel_format = VideoCore::PixelFormat::RGBA8, + .type = VideoCore::SurfaceType::Color, + })); void(slot_samplers.insert(runtime, SamplerParams{ .mag_filter = TextureConfig::TextureFilter::Linear, .min_filter = TextureConfig::TextureFilter::Linear, @@ -95,48 +103,57 @@ bool RasterizerCache::AccelerateTextureCopy(const GPU::Regs::DisplayTransferC src_params.size = ((src_params.height - 1) * src_params.stride) + src_params.width; src_params.end = src_params.addr + src_params.size; - auto [src_surface, src_rect] = GetTexCopySurface(src_params); - if (src_surface == nullptr) { + const auto [src_surface_id, src_rect] = GetTexCopySurface(src_params); + if (!src_surface_id) { return false; } + SurfaceParams src_info = slot_surfaces[src_surface_id]; if (output_gap != 0 && - (output_width != src_surface->BytesInPixels(src_rect.GetWidth() / src_surface->res_scale) * - (src_surface->is_tiled ? 8 : 1) || - output_gap % src_surface->BytesInPixels(src_surface->is_tiled ? 64 : 1) != 0)) { + (output_width != src_info.BytesInPixels(src_rect.GetWidth() / src_info.res_scale) * + (src_info.is_tiled ? 8 : 1) || + output_gap % src_info.BytesInPixels(src_info.is_tiled ? 64 : 1) != 0)) { return false; } - SurfaceParams dst_params = *src_surface; + SurfaceParams dst_params = src_info; dst_params.addr = config.GetPhysicalOutputAddress(); - dst_params.width = src_rect.GetWidth() / src_surface->res_scale; - dst_params.stride = dst_params.width + src_surface->PixelsInBytes( - src_surface->is_tiled ? output_gap / 8 : output_gap); - dst_params.height = src_rect.GetHeight() / src_surface->res_scale; - dst_params.res_scale = src_surface->res_scale; + dst_params.width = src_rect.GetWidth() / src_info.res_scale; + dst_params.stride = + dst_params.width + src_info.PixelsInBytes(src_info.is_tiled ? output_gap / 8 : output_gap); + dst_params.height = src_rect.GetHeight() / src_info.res_scale; + dst_params.res_scale = src_info.res_scale; dst_params.UpdateParams(); // Since we are going to invalidate the gap if there is one, we will have to load it first const bool load_gap = output_gap != 0; - auto [dst_surface, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap); + const auto [dst_surface_id, dst_rect] = + GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap); - if (!dst_surface || dst_surface->type == SurfaceType::Texture || - !CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + if (!dst_surface_id) { + return false; + } + + Surface& src_surface = slot_surfaces[src_surface_id]; + Surface& dst_surface = slot_surfaces[dst_surface_id]; + + if (dst_surface.type == SurfaceType::Texture || + !CheckFormatsBlittable(src_surface.pixel_format, dst_surface.pixel_format)) { return false; } ASSERT(src_rect.GetWidth() == dst_rect.GetWidth()); const TextureCopy texture_copy = { - .src_level = src_surface->LevelOf(src_params.addr), - .dst_level = dst_surface->LevelOf(dst_params.addr), + .src_level = src_surface.LevelOf(src_params.addr), + .dst_level = dst_surface.LevelOf(dst_params.addr), .src_offset = {src_rect.left, src_rect.bottom}, .dst_offset = {dst_rect.left, dst_rect.bottom}, .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, }; - runtime.CopyTextures(*src_surface, *dst_surface, texture_copy); + runtime.CopyTextures(src_surface, dst_surface, texture_copy); - InvalidateRegion(dst_params.addr, dst_params.size, dst_surface); + InvalidateRegion(dst_params.addr, dst_params.size, dst_surface_id); return true; } @@ -161,37 +178,42 @@ bool RasterizerCache::AccelerateDisplayTransfer(const GPU::Regs::DisplayTrans dst_params.pixel_format = PixelFormatFromGPUPixelFormat(config.output_format); dst_params.UpdateParams(); - auto [src_surface, src_rect] = GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); - if (src_surface == nullptr) - return false; - - dst_params.res_scale = src_surface->res_scale; - - auto [dst_surface, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); - if (dst_surface == nullptr) { + auto [src_surface_id, src_rect] = GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); + if (!src_surface_id) { return false; } - if (src_surface->is_tiled != dst_surface->is_tiled) { + dst_params.res_scale = slot_surfaces[src_surface_id].res_scale; + + const auto [dst_surface_id, dst_rect] = + GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); + if (!dst_surface_id) { + return false; + } + + Surface& src_surface = slot_surfaces[src_surface_id]; + Surface& dst_surface = slot_surfaces[dst_surface_id]; + + if (src_surface.is_tiled != dst_surface.is_tiled) { std::swap(src_rect.top, src_rect.bottom); } if (config.flip_vertically) { std::swap(src_rect.top, src_rect.bottom); } - if (!CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + if (!CheckFormatsBlittable(src_surface.pixel_format, dst_surface.pixel_format)) { return false; } const TextureBlit texture_blit = { - .src_level = src_surface->LevelOf(src_params.addr), - .dst_level = dst_surface->LevelOf(dst_params.addr), + .src_level = src_surface.LevelOf(src_params.addr), + .dst_level = dst_surface.LevelOf(dst_params.addr), .src_rect = src_rect, .dst_rect = dst_rect, }; - runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); + runtime.BlitTextures(src_surface, dst_surface, texture_blit); - InvalidateRegion(dst_params.addr, dst_params.size, dst_surface); + InvalidateRegion(dst_params.addr, dst_params.size, dst_surface_id); return true; } @@ -204,22 +226,28 @@ bool RasterizerCache::AccelerateFill(const GPU::Regs::MemoryFillConfig& confi params.type = SurfaceType::Fill; params.res_scale = std::numeric_limits::max(); - Surface fill_surface = std::make_shared(params, runtime); + SurfaceId fill_surface_id = CreateSurface(params); + Surface& fill_surface = slot_surfaces[fill_surface_id]; - std::memcpy(&fill_surface->fill_data[0], &config.value_32bit, 4); + std::memcpy(&fill_surface.fill_data[0], &config.value_32bit, 4); if (config.fill_32bit) { - fill_surface->fill_size = 4; + fill_surface.fill_size = 4; } else if (config.fill_24bit) { - fill_surface->fill_size = 3; + fill_surface.fill_size = 3; } else { - fill_surface->fill_size = 2; + fill_surface.fill_size = 2; } - RegisterSurface(fill_surface); - InvalidateRegion(fill_surface->addr, fill_surface->size, fill_surface); + RegisterSurface(fill_surface_id); + InvalidateRegion(fill_surface.addr, fill_surface.size, fill_surface_id); return true; } +template +auto RasterizerCache::GetSurface(SurfaceId surface_id) -> Surface& { + return slot_surfaces[surface_id]; +} + template auto RasterizerCache::GetSampler(SamplerId sampler_id) -> Sampler& { return slot_samplers[sampler_id]; @@ -250,9 +278,9 @@ auto RasterizerCache::GetSampler(const Pica::TexturingRegs::TextureConfig& co template template void RasterizerCache::ForEachSurfaceInRegion(PAddr addr, size_t size, Func&& func) { - using FuncReturn = typename std::invoke_result::type; + using FuncReturn = typename std::invoke_result::type; static constexpr bool BOOL_BREAK = std::is_same_v; - boost::container::small_vector surfaces; + boost::container::small_vector surfaces; ForEachPage(addr, size, [this, &surfaces, addr, size, func](u64 page) { const auto it = page_table.find(page); if (it == page_table.end()) { @@ -262,50 +290,51 @@ void RasterizerCache::ForEachSurfaceInRegion(PAddr addr, size_t size, Func&& return; } } - for (const Surface& surface : it->second) { - if (surface->picked) { + for (const SurfaceId surface_id : it->second) { + Surface& surface = slot_surfaces[surface_id]; + if (surface.picked) { continue; } - if (!surface->Overlaps(addr, size)) { + if (!surface.Overlaps(addr, size)) { continue; } - surface->picked = true; - surfaces.push_back(surface); + surface.picked = true; + surfaces.push_back(surface_id); if constexpr (BOOL_BREAK) { - if (func(surface)) { + if (func(surface_id, surface)) { return true; } } else { - func(surface); + func(surface_id, surface); } } if constexpr (BOOL_BREAK) { return false; } }); - for (const Surface surface : surfaces) { - surface->picked = false; + for (const SurfaceId surface_id : surfaces) { + slot_surfaces[surface_id].picked = false; } } template template -auto RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, - std::optional validate_interval) -> Surface { - Surface match_surface = nullptr; +SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, + std::optional validate_interval) { + SurfaceId match_surface{}; bool match_valid = false; u32 match_scale = 0; SurfaceInterval match_interval{}; - ForEachSurfaceInRegion(params.addr, params.size, [&](Surface surface) { + 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); + ? (params.res_scale == surface.res_scale) + : (params.res_scale <= surface.res_scale); const bool is_valid = True(find_flags & MatchFlags::Copy) ? true - : surface->IsRegionValid(validate_interval.value_or(params.GetInterval())); + : surface.IsRegionValid(validate_interval.value_or(params.GetInterval())); const auto IsMatch_Helper = [&](auto check_type, auto match_fn) { if (False(find_flags & check_type)) @@ -318,21 +347,21 @@ auto RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match return; if (!res_scale_matched && match_scale_type != ScaleMatch::Ignore && - surface->type != SurfaceType::Fill) + surface.type != SurfaceType::Fill) return; // Found a match, update only if this is better than the previous one const auto UpdateMatch = [&] { - match_surface = surface; + match_surface = surface_id; match_valid = is_valid; - match_scale = surface->res_scale; + match_scale = surface.res_scale; match_interval = surface_interval; }; - if (surface->res_scale > match_scale) { + if (surface.res_scale > match_scale) { UpdateMatch(); return; - } else if (surface->res_scale < match_scale) { + } else if (surface.res_scale < match_scale) { return; } @@ -349,24 +378,24 @@ auto RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match }; IsMatch_Helper(std::integral_constant{}, [&] { - return std::make_pair(surface->ExactMatch(params), surface->GetInterval()); + return std::make_pair(surface.ExactMatch(params), surface.GetInterval()); }); IsMatch_Helper(std::integral_constant{}, [&] { - return std::make_pair(surface->CanSubRect(params), surface->GetInterval()); + return std::make_pair(surface.CanSubRect(params), surface.GetInterval()); }); IsMatch_Helper(std::integral_constant{}, [&] { ASSERT(validate_interval); auto copy_interval = - surface->GetCopyableInterval(params.FromInterval(*validate_interval)); + surface.GetCopyableInterval(params.FromInterval(*validate_interval)); bool matched = boost::icl::length(copy_interval & *validate_interval) != 0 && - surface->CanCopy(params, copy_interval); + surface.CanCopy(params, copy_interval); return std::make_pair(matched, copy_interval); }); IsMatch_Helper(std::integral_constant{}, [&] { - return std::make_pair(surface->CanExpand(params), surface->GetInterval()); + return std::make_pair(surface.CanExpand(params), surface.GetInterval()); }); IsMatch_Helper(std::integral_constant{}, [&] { - return std::make_pair(surface->CanTexCopy(params), surface->GetInterval()); + return std::make_pair(surface.CanTexCopy(params), surface.GetInterval()); }); }); @@ -374,39 +403,39 @@ auto RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match } template -void RasterizerCache::CopySurface(const Surface& src_surface, const Surface& dst_surface, +void RasterizerCache::CopySurface(Surface& src_surface, Surface& dst_surface, SurfaceInterval copy_interval) { MICROPROFILE_SCOPE(RasterizerCache_CopySurface); - const auto subrect_params = dst_surface->FromInterval(copy_interval); - const Rect2D dst_rect = dst_surface->GetScaledSubRect(subrect_params); + const auto subrect_params = dst_surface.FromInterval(copy_interval); + const Rect2D dst_rect = dst_surface.GetScaledSubRect(subrect_params); const PAddr copy_addr = copy_interval.lower(); - ASSERT(subrect_params.GetInterval() == copy_interval && src_surface != dst_surface); + ASSERT(subrect_params.GetInterval() == copy_interval); - if (src_surface->type == SurfaceType::Fill) { + if (src_surface.type == SurfaceType::Fill) { const TextureClear texture_clear = { - .texture_level = dst_surface->LevelOf(copy_addr), + .texture_level = dst_surface.LevelOf(copy_addr), .texture_rect = dst_rect, - .value = src_surface->MakeClearValue(copy_addr, dst_surface->pixel_format), + .value = src_surface.MakeClearValue(copy_addr, dst_surface.pixel_format), }; - runtime.ClearTexture(*dst_surface, texture_clear); + runtime.ClearTexture(dst_surface, texture_clear); return; } const TextureBlit texture_blit = { - .src_level = src_surface->LevelOf(copy_addr), - .dst_level = dst_surface->LevelOf(copy_addr), - .src_rect = src_surface->GetScaledSubRect(subrect_params), + .src_level = src_surface.LevelOf(copy_addr), + .dst_level = dst_surface.LevelOf(copy_addr), + .src_rect = src_surface.GetScaledSubRect(subrect_params), .dst_rect = dst_rect, }; - runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); + runtime.BlitTextures(src_surface, dst_surface, texture_blit); } template -auto RasterizerCache::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create) -> Surface { +SurfaceId RasterizerCache::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, + bool load_if_create) { if (params.addr == 0 || params.height * params.width == 0) [[unlikely]] { - return nullptr; + return SurfaceId{}; } // Use GetSurfaceSubRect instead @@ -414,64 +443,70 @@ auto RasterizerCache::GetSurface(const SurfaceParams& params, ScaleMatch matc ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0)); // Check for an exact match in existing surfaces - Surface surface = FindMatch(params, match_res_scale); + SurfaceId surface_id = FindMatch(params, match_res_scale); - if (!surface) { + if (!surface_id) { u16 target_res_scale = params.res_scale; if (match_res_scale != ScaleMatch::Exact) { // This surface may have a subrect of another surface with a higher res_scale, find // it to adjust our params SurfaceParams find_params = params; - Surface expandable = FindMatch(find_params, match_res_scale); - if (expandable && expandable->res_scale > target_res_scale) { - target_res_scale = expandable->res_scale; + SurfaceId expandable_id = FindMatch(find_params, match_res_scale); + if (expandable_id) { + Surface& expandable = slot_surfaces[expandable_id]; + if (expandable.res_scale > target_res_scale) { + target_res_scale = expandable.res_scale; + } } // Keep res_scale when reinterpreting d24s8 -> rgba8 if (params.pixel_format == PixelFormat::RGBA8) { find_params.pixel_format = PixelFormat::D24S8; - expandable = FindMatch(find_params, match_res_scale); - if (expandable && expandable->res_scale > target_res_scale) { - target_res_scale = expandable->res_scale; + expandable_id = FindMatch(find_params, match_res_scale); + if (expandable_id) { + Surface& expandable = slot_surfaces[expandable_id]; + if (expandable.res_scale > target_res_scale) { + target_res_scale = expandable.res_scale; + } } } } SurfaceParams new_params = params; new_params.res_scale = target_res_scale; - surface = CreateSurface(new_params); - RegisterSurface(surface); + surface_id = CreateSurface(new_params); + RegisterSurface(surface_id); } if (load_if_create) { - ValidateSurface(surface, params.addr, params.size); + ValidateSurface(surface_id, params.addr, params.size); } - return surface; + return surface_id; } template auto RasterizerCache::GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale, bool load_if_create) -> SurfaceRect_Tuple { if (params.addr == 0 || params.height * params.width == 0) [[unlikely]] { - return std::make_tuple(nullptr, Common::Rectangle{}); + return std::make_pair(SurfaceId{}, Common::Rectangle{}); } // Attempt to find encompassing surface - Surface surface = FindMatch(params, match_res_scale); + SurfaceId surface_id = FindMatch(params, match_res_scale); // Check if FindMatch failed because of res scaling // If that's the case create a new surface with // the dimensions of the lower res_scale surface // to suggest it should not be used again - if (!surface && match_res_scale != ScaleMatch::Ignore) { - surface = FindMatch(params, ScaleMatch::Ignore); - if (surface) { - SurfaceParams new_params = *surface; + if (!surface_id && match_res_scale != ScaleMatch::Ignore) { + surface_id = FindMatch(params, ScaleMatch::Ignore); + if (surface_id) { + SurfaceParams new_params = slot_surfaces[surface_id]; new_params.res_scale = params.res_scale; - surface = CreateSurface(new_params); - RegisterSurface(surface); + surface_id = CreateSurface(new_params); + RegisterSurface(surface_id); } } @@ -484,48 +519,49 @@ auto RasterizerCache::GetSurfaceSubRect(const SurfaceParams& params, ScaleMat } // Check for a surface we can expand before creating a new one - if (!surface) { - surface = FindMatch(aligned_params, match_res_scale); - if (surface) { + if (!surface_id) { + surface_id = FindMatch(aligned_params, match_res_scale); + if (surface_id) { + Surface& surface = slot_surfaces[surface_id]; aligned_params.width = aligned_params.stride; aligned_params.UpdateParams(); - SurfaceParams new_params = *surface; - new_params.addr = std::min(aligned_params.addr, surface->addr); - new_params.end = std::max(aligned_params.end, surface->end); + SurfaceParams new_params = surface; + new_params.addr = std::min(aligned_params.addr, surface.addr); + new_params.end = std::max(aligned_params.end, surface.end); new_params.size = new_params.end - new_params.addr; new_params.height = new_params.size / aligned_params.BytesInPixels(aligned_params.stride); new_params.UpdateParams(); ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0); - Surface new_surface = CreateSurface(new_params); - DuplicateSurface(surface, new_surface); - UnregisterSurface(surface); + SurfaceId new_surface_id = CreateSurface(new_params); + DuplicateSurface(surface_id, new_surface_id); + UnregisterSurface(surface_id); + RegisterSurface(new_surface_id); - surface = new_surface; - RegisterSurface(new_surface); + surface_id = new_surface_id; } } // No subrect found - create and return a new surface - if (!surface) { + if (!surface_id) { SurfaceParams new_params = aligned_params; // Can't have gaps in a surface new_params.width = aligned_params.stride; new_params.UpdateParams(); // GetSurface will create the new surface and possibly adjust res_scale if necessary - surface = GetSurface(new_params, match_res_scale, load_if_create); + surface_id = GetSurface(new_params, match_res_scale, load_if_create); } else if (load_if_create) { - ValidateSurface(surface, aligned_params.addr, aligned_params.size); + ValidateSurface(surface_id, aligned_params.addr, aligned_params.size); } - return std::make_tuple(surface, surface->GetScaledSubRect(params)); + return std::make_pair(surface_id, slot_surfaces[surface_id].GetScaledSubRect(params)); } template auto RasterizerCache::GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config) - -> Surface { + -> Surface& { const auto info = Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); const u32 max_level = MipLevels(info.width, info.height, config.config.lod.max_level) - 1; return GetTextureSurface(info, max_level); @@ -533,9 +569,16 @@ auto RasterizerCache::GetTextureSurface(const Pica::TexturingRegs::FullTextur template auto RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level) - -> Surface { + -> Surface& { if (info.physical_address == 0) [[unlikely]] { - return nullptr; + // Can occur when texture addr is null or its memory is unmapped/invalid + // HACK: In this case, the correct behaviour for the PICA is to use the last + // rendered colour. But because this would be impractical to implement, the + // next best alternative is to use a clear texture, essentially skipping + // the geometry in question. + // For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn + // on the male character's face, which in the OpenGL default appear black. + return slot_surfaces[NULL_SURFACE_ID]; } SurfaceParams params; @@ -551,23 +594,24 @@ auto RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& inf const u32 min_height = info.height >> max_level; if (min_width % 8 != 0 || min_height % 8 != 0) { LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) is not multiple of 8", min_width, min_height); - return nullptr; + return slot_surfaces[NULL_SURFACE_ID]; } if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) { LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) does not support required mipmap level ({})", params.width, params.height, max_level); - return nullptr; + return slot_surfaces[NULL_SURFACE_ID]; } - return GetSurface(params, ScaleMatch::Ignore, true); + SurfaceId surface_id = GetSurface(params, ScaleMatch::Ignore, true); + return slot_surfaces[surface_id]; } template -auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> const Surface& { +auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> Surface& { auto [it, new_surface] = texture_cube_cache.try_emplace(config); if (new_surface) { - SurfaceParams cube_params = { + const SurfaceParams cube_params = { .addr = config.px, .width = config.width, .height = config.width, @@ -577,13 +621,10 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons .pixel_format = PixelFormatFromTextureFormat(config.format), .type = SurfaceType::Texture, }; - it->second = CreateSurface(cube_params); } - Surface& cube = it->second; - - const u32 scaled_size = cube->GetScaledWidth(); + const SurfaceId cube_id = it->second; const std::array addresses = {config.px, config.nx, config.py, config.ny, config.pz, config.nz}; for (std::size_t i = 0; i < addresses.size(); i++) { @@ -595,14 +636,12 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons }; info.SetDefaultStride(); - Surface face_surface = GetTextureSurface(info, config.levels - 1); - if (!face_surface) { - continue; - } + Surface& face_surface = GetTextureSurface(info, config.levels - 1); + Surface& cube = slot_surfaces[cube_id]; - ASSERT(face_surface->levels == config.levels); const u32 face = static_cast(i); - for (u32 level = 0; level < face_surface->levels; level++) { + const u32 scaled_size = cube.GetScaledWidth(); + for (u32 level = 0; level < face_surface.levels; level++) { const TextureCopy texture_copy = { .src_level = level, .dst_level = level, @@ -612,11 +651,11 @@ auto RasterizerCache::GetTextureCube(const TextureCubeConfig& config) -> cons .dst_offset = {0, 0}, .extent = {scaled_size >> level, scaled_size >> level}, }; - runtime.CopyTextures(*face_surface, *cube, texture_copy); + runtime.CopyTextures(face_surface, cube, texture_copy); } } - return cube; + return slot_surfaces[cube_id]; } template @@ -678,59 +717,60 @@ auto RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, bool using_ } Common::Rectangle color_rect{}; - Surface color_surface{}; + SurfaceId color_surface_id{}; if (using_color_fb) - std::tie(color_surface, color_rect) = + std::tie(color_surface_id, color_rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); Common::Rectangle depth_rect{}; - Surface depth_surface{}; + SurfaceId depth_surface_id{}; if (using_depth_fb) - std::tie(depth_surface, depth_rect) = + std::tie(depth_surface_id, depth_rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); Common::Rectangle fb_rect{}; - if (color_surface && depth_surface) { + if (color_surface_id && depth_surface_id) { fb_rect = color_rect; // Color and Depth surfaces must have the same dimensions and offsets if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top || color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) { - color_surface = GetSurface(color_params, ScaleMatch::Exact, false); - depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); - fb_rect = color_surface->GetScaledRect(); + color_surface_id = GetSurface(color_params, ScaleMatch::Exact, false); + depth_surface_id = GetSurface(depth_params, ScaleMatch::Exact, false); + fb_rect = slot_surfaces[color_surface_id].GetScaledRect(); } - } else if (color_surface) { + } else if (color_surface_id) { fb_rect = color_rect; - } else if (depth_surface) { + } else if (depth_surface_id) { fb_rect = depth_rect; } - if (color_surface) { - ASSERT_MSG(color_surface->LevelOf(color_params.addr) == 0, + Surface* const color = color_surface_id ? &slot_surfaces[color_surface_id] : nullptr; + Surface* const depth_stencil = depth_surface_id ? &slot_surfaces[depth_surface_id] : nullptr; + + if (color_surface_id) { + ASSERT_MSG(color->LevelOf(color_params.addr) == 0, "Rendering to mipmap of color surface unsupported"); - ValidateSurface(color_surface, boost::icl::first(color_vp_interval), + ValidateSurface(color_surface_id, boost::icl::first(color_vp_interval), boost::icl::length(color_vp_interval)); } - if (depth_surface) { - ASSERT_MSG(depth_surface->LevelOf(depth_params.addr) == 0, + if (depth_surface_id) { + ASSERT_MSG(depth_stencil->LevelOf(depth_params.addr) == 0, "Rendering to mipmap of depth surface unsupported"); - ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), + ValidateSurface(depth_surface_id, boost::icl::first(depth_vp_interval), boost::icl::length(depth_vp_interval)); } render_targets = RenderTargets{ - .color_surface = color_surface, - .depth_surface = depth_surface, + .color_surface_id = color_surface_id, + .depth_surface_id = depth_surface_id, }; - auto* const color = color_surface ? color_surface.get() : nullptr; - auto* const depth_stencil = depth_surface ? depth_surface.get() : nullptr; return Framebuffer{runtime, color, depth_stencil, regs, fb_rect}; } template void RasterizerCache::InvalidateRenderTargets(const Framebuffer& framebuffer) { - const auto Invalidate = [&](SurfaceType type, Surface region_owner) { + const auto Invalidate = [&](SurfaceType type, SurfaceId region_owner) { const bool has_attachment = framebuffer.HasAttachment(type); if (has_attachment) { const SurfaceInterval interval = framebuffer.Interval(type); @@ -739,43 +779,46 @@ void RasterizerCache::InvalidateRenderTargets(const Framebuffer& framebuffer) } }; - Invalidate(SurfaceType::Color, render_targets.color_surface); - Invalidate(SurfaceType::DepthStencil, render_targets.depth_surface); + Invalidate(SurfaceType::Color, render_targets.color_surface_id); + Invalidate(SurfaceType::DepthStencil, render_targets.depth_surface_id); } template auto RasterizerCache::GetTexCopySurface(const SurfaceParams& params) -> SurfaceRect_Tuple { Common::Rectangle rect{}; - Surface match_surface = FindMatch(params, ScaleMatch::Ignore); + SurfaceId match_id = FindMatch(params, ScaleMatch::Ignore); - if (match_surface) { - ValidateSurface(match_surface, params.addr, params.size); + if (match_id) { + Surface& match_surface = slot_surfaces[match_id]; + ValidateSurface(match_id, params.addr, params.size); SurfaceParams match_subrect; if (params.width != params.stride) { - const u32 tiled_size = match_surface->is_tiled ? 8 : 1; + const u32 tiled_size = match_surface.is_tiled ? 8 : 1; match_subrect = params; - match_subrect.width = match_surface->PixelsInBytes(params.width) / tiled_size; - match_subrect.stride = match_surface->PixelsInBytes(params.stride) / tiled_size; + match_subrect.width = match_surface.PixelsInBytes(params.width) / tiled_size; + match_subrect.stride = match_surface.PixelsInBytes(params.stride) / tiled_size; match_subrect.height *= tiled_size; } else { - match_subrect = match_surface->FromInterval(params.GetInterval()); + match_subrect = match_surface.FromInterval(params.GetInterval()); ASSERT(match_subrect.GetInterval() == params.GetInterval()); } - rect = match_surface->GetScaledSubRect(match_subrect); + rect = match_surface.GetScaledSubRect(match_subrect); } - return std::make_tuple(match_surface, rect); + return std::make_pair(match_id, rect); } template -void RasterizerCache::DuplicateSurface(const Surface& src_surface, const Surface& dst_surface) { - ASSERT(dst_surface->addr <= src_surface->addr && dst_surface->end >= src_surface->end); +void RasterizerCache::DuplicateSurface(SurfaceId src_id, SurfaceId dst_id) { + Surface& src_surface = slot_surfaces[src_id]; + Surface& dst_surface = slot_surfaces[dst_id]; + ASSERT(dst_surface.addr <= src_surface.addr && dst_surface.end >= src_surface.end); - const Rect2D src_rect = src_surface->GetScaledRect(); - const Rect2D dst_rect = dst_surface->GetScaledSubRect(*src_surface); + const Rect2D src_rect = src_surface.GetScaledRect(); + const Rect2D dst_rect = dst_surface.GetScaledSubRect(src_surface); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth()); const TextureCopy texture_copy = { @@ -785,57 +828,59 @@ void RasterizerCache::DuplicateSurface(const Surface& src_surface, const Surf .dst_offset = {dst_rect.left, dst_rect.bottom}, .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, }; - runtime.CopyTextures(*src_surface, *dst_surface, texture_copy); + runtime.CopyTextures(src_surface, dst_surface, texture_copy); - dst_surface->invalid_regions -= src_surface->GetInterval(); - dst_surface->invalid_regions += src_surface->invalid_regions; + dst_surface.invalid_regions -= src_surface.GetInterval(); + dst_surface.invalid_regions += src_surface.invalid_regions; SurfaceRegions regions; - for (const auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) { - if (pair.second == src_surface) { + for (const auto& pair : RangeFromInterval(dirty_regions, src_surface.GetInterval())) { + if (pair.second == src_id) { regions += pair.first; } } for (const auto& interval : regions) { - dirty_regions.set({interval, dst_surface}); + dirty_regions.set({interval, dst_id}); } } template -void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 size) { +void RasterizerCache::ValidateSurface(SurfaceId surface_id, PAddr addr, u32 size) { if (size == 0) [[unlikely]] { return; } + Surface& surface = slot_surfaces[surface_id]; const auto validate_interval = SurfaceInterval(addr, addr + size); - const auto validate_regions = surface->invalid_regions & validate_interval; + const auto validate_regions = surface.invalid_regions & validate_interval; if (validate_regions.empty()) { return; } // Fill surfaces must always be valid when used - if (surface->type == SurfaceType::Fill) { - ASSERT(surface->IsRegionValid(validate_interval)); + if (surface.type == SurfaceType::Fill) { + ASSERT(surface.IsRegionValid(validate_interval)); return; } - for (u32 level = surface->LevelOf(addr); level <= surface->LevelOf(addr + size); level++) { - auto level_regions = validate_regions & surface->LevelInterval(level); + for (u32 level = surface.LevelOf(addr); level <= surface.LevelOf(addr + size); level++) { + auto level_regions = validate_regions & surface.LevelInterval(level); const auto NotifyValidated = [&](SurfaceInterval interval) { level_regions.erase(interval); - surface->invalid_regions.erase(interval); + surface.invalid_regions.erase(interval); }; while (!level_regions.empty()) { const SurfaceInterval interval = *level_regions.begin(); - const SurfaceParams params = surface->FromInterval(interval); + const SurfaceParams params = surface.FromInterval(interval); - Surface copy_surface = + SurfaceId copy_surface_id = FindMatch(params, ScaleMatch::Ignore, interval); - if (copy_surface) { - const SurfaceInterval copy_interval = copy_surface->GetCopyableInterval(params); + if (copy_surface_id) { + Surface& copy_surface = slot_surfaces[copy_surface_id]; + const SurfaceInterval copy_interval = copy_surface.GetCopyableInterval(params); CopySurface(copy_surface, surface, copy_interval); NotifyValidated(copy_interval); continue; @@ -871,9 +916,9 @@ void RasterizerCache::ValidateSurface(const Surface& surface, PAddr addr, u32 } template -void RasterizerCache::UploadSurface(const Surface& surface, SurfaceInterval interval) { - const SurfaceParams load_info = surface->FromInterval(interval); - ASSERT(load_info.addr >= surface->addr && load_info.end <= surface->end); +void RasterizerCache::UploadSurface(Surface& surface, SurfaceInterval interval) { + const SurfaceParams load_info = surface.FromInterval(interval); + ASSERT(load_info.addr >= surface.addr && load_info.end <= surface.end); MICROPROFILE_SCOPE(RasterizerCache_SurfaceLoad); @@ -886,7 +931,7 @@ void RasterizerCache::UploadSurface(const Surface& surface, SurfaceInterval i // Check if we need to dump the texture if (dump_textures) { - custom_tex_manager.DumpTexture(load_info, surface->LevelOf(load_info.addr), upload_data); + custom_tex_manager.DumpTexture(load_info, surface.LevelOf(load_info.addr), upload_data); } // Check if we need to replace the texture @@ -895,34 +940,33 @@ void RasterizerCache::UploadSurface(const Surface& surface, SurfaceInterval i } // Upload the 3DS texture to the host GPU - const u32 upload_size = - load_info.width * load_info.height * surface->GetInternalBytesPerPixel(); + const u32 upload_size = load_info.width * load_info.height * surface.GetInternalBytesPerPixel(); const StagingData staging = runtime.FindStaging(upload_size, true); DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, staging.mapped, - runtime.NeedsConvertion(surface->pixel_format)); + runtime.NeedsConvertion(surface.pixel_format)); const BufferTextureCopy upload = { .buffer_offset = 0, .buffer_size = staging.size, - .texture_rect = surface->GetSubRect(load_info), - .texture_level = surface->LevelOf(load_info.addr), + .texture_rect = surface.GetSubRect(load_info), + .texture_level = surface.LevelOf(load_info.addr), }; - surface->Upload(upload, staging); + surface.Upload(upload, staging); } template -bool RasterizerCache::UploadCustomSurface(const Surface& surface, const SurfaceParams& load_info, +bool RasterizerCache::UploadCustomSurface(Surface& surface, const SurfaceParams& load_info, std::span upload_data) { - const u32 level = surface->LevelOf(load_info.addr); + const u32 level = surface.LevelOf(load_info.addr); const bool is_base_level = level == 0; const u64 hash = custom_tex_manager.ComputeHash(load_info, upload_data); const Texture& texture = custom_tex_manager.GetTexture(hash); // The old texture pack system did not support mipmaps so older packs might do - // wonky things. For example Henriko's pack has mipmaps larger than the base - // level. To avoid crashes just don't upload mipmaps for custom surfaces - if (custom_tex_manager.CompatibilityMode() && surface->IsCustom() && !is_base_level) { + // wonky things. For example many packs have mipmaps larger than the base + // level. To avoid crashes just don't upload mipmaps for custom surfaces in compatiblity mode. + if (custom_tex_manager.CompatibilityMode() && surface.IsCustom() && !is_base_level) { return true; } if (!texture) { @@ -930,7 +974,7 @@ bool RasterizerCache::UploadCustomSurface(const Surface& surface, const Surfa } // Swap the internal surface allocation to the desired dimentions and format - if (is_base_level && !surface->Swap(texture.width, texture.height, texture.format)) { + if (is_base_level && !surface.Swap(texture.width, texture.height, texture.format)) { // This means the backend doesn't support the custom compression format. // We could implement a CPU/GPU decoder but it's always better for packs to // have compatible compression formats. @@ -939,7 +983,7 @@ bool RasterizerCache::UploadCustomSurface(const Surface& surface, const Surfa } // Ensure surface has a compatible allocation before proceeding - if (!surface->IsCustom() || surface->CustomFormat() != texture.format) { + if (!surface.IsCustom() || surface.CustomFormat() != texture.format) { LOG_ERROR(HW_GPU, "Surface does not have a compatible allocation, ignoring"); return true; } @@ -956,34 +1000,34 @@ bool RasterizerCache::UploadCustomSurface(const Surface& surface, const Surfa .texture_rect = {0, texture.height, texture.width, 0}, .texture_level = level, }; - surface->Upload(upload, staging); + surface.Upload(upload, staging); // Manually generate mipmaps in compatibility mode if (custom_tex_manager.CompatibilityMode()) { - runtime.GenerateMipmaps(*surface); + runtime.GenerateMipmaps(surface); } return true; } template -void RasterizerCache::DownloadSurface(const Surface& surface, SurfaceInterval interval) { - const SurfaceParams flush_info = surface->FromInterval(interval); +void RasterizerCache::DownloadSurface(Surface& surface, SurfaceInterval interval) { + const SurfaceParams flush_info = surface.FromInterval(interval); const u32 flush_start = boost::icl::first(interval); const u32 flush_end = boost::icl::last_next(interval); - ASSERT(flush_start >= surface->addr && flush_end <= surface->end); + ASSERT(flush_start >= surface.addr && flush_end <= surface.end); const u32 flush_size = - flush_info.width * flush_info.height * surface->GetInternalBytesPerPixel(); + flush_info.width * flush_info.height * surface.GetInternalBytesPerPixel(); const StagingData staging = runtime.FindStaging(flush_size, false); const BufferTextureCopy download = { .buffer_offset = 0, .buffer_size = staging.size, - .texture_rect = surface->GetSubRect(flush_info), - .texture_level = surface->LevelOf(flush_start), + .texture_rect = surface.GetSubRect(flush_info), + .texture_level = surface.LevelOf(flush_start), }; - surface->Download(download, staging); + surface.Download(download, staging); runtime.Finish(); @@ -994,34 +1038,34 @@ void RasterizerCache::DownloadSurface(const Surface& surface, SurfaceInterval const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start); EncodeTexture(flush_info, flush_start, flush_end, staging.mapped, download_dest, - runtime.NeedsConvertion(surface->pixel_format)); + runtime.NeedsConvertion(surface.pixel_format)); } template -void RasterizerCache::DownloadFillSurface(const Surface& surface, SurfaceInterval interval) { +void RasterizerCache::DownloadFillSurface(Surface& surface, SurfaceInterval interval) { const u32 flush_start = boost::icl::first(interval); const u32 flush_end = boost::icl::last_next(interval); - ASSERT(flush_start >= surface->addr && flush_end <= surface->end); + ASSERT(flush_start >= surface.addr && flush_end <= surface.end); MemoryRef dest_ptr = memory.GetPhysicalRef(flush_start); if (!dest_ptr) [[unlikely]] { return; } - const u32 start_offset = flush_start - surface->addr; + const u32 start_offset = flush_start - surface.addr; const u32 download_size = std::clamp(flush_end - flush_start, 0u, static_cast(dest_ptr.GetSize())); - const u32 coarse_start_offset = start_offset - (start_offset % surface->fill_size); - const u32 backup_bytes = start_offset % surface->fill_size; + const u32 coarse_start_offset = start_offset - (start_offset % surface.fill_size); + const u32 backup_bytes = start_offset % surface.fill_size; std::array backup_data; if (backup_bytes) { std::memcpy(backup_data.data(), &dest_ptr[coarse_start_offset], backup_bytes); } - for (u32 offset = coarse_start_offset; offset < download_size; offset += surface->fill_size) { - std::memcpy(&dest_ptr[offset], &surface->fill_data[0], - std::min(surface->fill_size, download_size - offset)); + for (u32 offset = coarse_start_offset; offset < download_size; offset += surface.fill_size) { + std::memcpy(&dest_ptr[offset], &surface.fill_data[0], + std::min(surface.fill_size, download_size - offset)); } if (backup_bytes) { @@ -1030,8 +1074,7 @@ void RasterizerCache::DownloadFillSurface(const Surface& surface, SurfaceInte } template -bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface, - SurfaceParams params, +bool RasterizerCache::NoUnimplementedReinterpretations(Surface& surface, SurfaceParams params, SurfaceInterval interval) { static constexpr std::array all_formats = { PixelFormat::RGBA8, PixelFormat::RGB8, PixelFormat::RGB5A1, PixelFormat::RGB565, @@ -1043,16 +1086,14 @@ bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface bool implemented = true; for (PixelFormat format : all_formats) { - if (GetFormatBpp(format) == surface->GetFormatBpp()) { + if (GetFormatBpp(format) == surface.GetFormatBpp()) { params.pixel_format = format; // This could potentially be expensive, although experimentally it hasn't been too bad - Surface test_surface = - FindMatch(params, ScaleMatch::Ignore, interval); + SurfaceId test_id = FindMatch(params, ScaleMatch::Ignore, interval); - if (test_surface) { + if (test_id) { LOG_WARNING(HW_GPU, "Missing pixel_format reinterpreter: {} -> {}", - PixelFormatAsString(format), - PixelFormatAsString(surface->pixel_format)); + PixelFormatAsString(format), PixelFormatAsString(surface.pixel_format)); implemented = false; } } @@ -1065,9 +1106,9 @@ template bool RasterizerCache::IntervalHasInvalidPixelFormat(SurfaceParams params, SurfaceInterval interval) { bool invalid_format_found = false; - ForEachSurfaceInRegion(params.addr, params.end, [&](Surface surface) { - if (surface->pixel_format == PixelFormat::Invalid) { - LOG_DEBUG(HW_GPU, "Surface {:#x} found with invalid pixel format", surface->addr); + ForEachSurfaceInRegion(params.addr, params.end, [&](SurfaceId surface_id, Surface& surface) { + if (surface.pixel_format == PixelFormat::Invalid) { + LOG_DEBUG(HW_GPU, "Surface {:#x} found with invalid pixel format", surface.addr); invalid_format_found = true; return true; } @@ -1078,21 +1119,22 @@ bool RasterizerCache::IntervalHasInvalidPixelFormat(SurfaceParams params, } template -bool RasterizerCache::ValidateByReinterpretation(const Surface& surface, SurfaceParams params, +bool RasterizerCache::ValidateByReinterpretation(Surface& surface, SurfaceParams params, SurfaceInterval interval) { - const PixelFormat dest_format = surface->pixel_format; + const PixelFormat dest_format = surface.pixel_format; for (const auto& reinterpreter : runtime.GetPossibleReinterpretations(dest_format)) { params.pixel_format = reinterpreter->GetSourceFormat(); - Surface reinterpret_surface = + SurfaceId reinterpret_id = FindMatch(params, ScaleMatch::Ignore, interval); - if (reinterpret_surface) { - auto reinterpret_interval = reinterpret_surface->GetCopyableInterval(params); - auto reinterpret_params = surface->FromInterval(reinterpret_interval); - auto src_rect = reinterpret_surface->GetScaledSubRect(reinterpret_params); - auto dest_rect = surface->GetScaledSubRect(reinterpret_params); + if (reinterpret_id) { + Surface& reinterpret_surface = slot_surfaces[reinterpret_id]; + auto reinterpret_interval = reinterpret_surface.GetCopyableInterval(params); + auto reinterpret_params = surface.FromInterval(reinterpret_interval); + auto src_rect = reinterpret_surface.GetScaledSubRect(reinterpret_params); + auto dest_rect = surface.GetScaledSubRect(reinterpret_params); - reinterpreter->Reinterpret(*reinterpret_surface, src_rect, *surface, dest_rect); + reinterpreter->Reinterpret(reinterpret_surface, src_rect, surface, dest_rect); return true; } } @@ -1126,29 +1168,30 @@ void RasterizerCache::ClearAll(bool flush) { } template -void RasterizerCache::FlushRegion(PAddr addr, u32 size, Surface flush_surface) { +void RasterizerCache::FlushRegion(PAddr addr, u32 size, SurfaceId flush_surface_id) { if (size == 0) [[unlikely]] { return; } const SurfaceInterval flush_interval(addr, addr + size); - SurfaceRegions flushed_intervals; + SurfaceRegions flushed_intervals{}; - for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) { + for (const auto& [dirty_interval, surface_id] : + RangeFromInterval(dirty_regions, flush_interval)) { // Small sizes imply that this most likely comes from the cpu, flush the entire region // the point is to avoid thousands of small writes every frame if the cpu decides to // access that region, anything higher than 8 you're guaranteed it comes from a service - const auto interval = size <= 8 ? pair.first : pair.first & flush_interval; - auto& surface = pair.second; - - if (flush_surface && surface != flush_surface) { + const SurfaceInterval interval = + size <= 8 ? dirty_interval : dirty_interval & flush_interval; + if (surface_id && surface_id != flush_surface_id) { continue; } // Sanity check, this surface is the last one that marked this region dirty - ASSERT(surface->IsRegionValid(interval)); + Surface& surface = slot_surfaces[surface_id]; + ASSERT(surface.IsRegionValid(interval)); - if (surface->type == SurfaceType::Fill) { + if (surface.type == SurfaceType::Fill) { DownloadFillSurface(surface, interval); } else { DownloadSurface(surface, interval); @@ -1167,94 +1210,102 @@ void RasterizerCache::FlushAll() { } template -void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) { +void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, SurfaceId region_owner_id) { if (size == 0) [[unlikely]] { return; } const SurfaceInterval invalid_interval{addr, addr + size}; - if (region_owner != nullptr) { - ASSERT(region_owner->type != SurfaceType::Texture); - ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end); + if (region_owner_id) { + Surface& region_owner = slot_surfaces[region_owner_id]; + // Texture surfaces are not renderable + ASSERT(region_owner.type != SurfaceType::Texture); + // Region owner must overlap with the invalidation region + ASSERT(addr >= region_owner.addr && addr + size <= region_owner.end); // Surfaces can't have a gap - ASSERT(region_owner->width == region_owner->stride); - region_owner->invalid_regions.erase(invalid_interval); + ASSERT(region_owner.width == region_owner.stride); + region_owner.invalid_regions.erase(invalid_interval); } - ForEachSurfaceInRegion(addr, size, [&](Surface surface) { - if (surface == region_owner) { + ForEachSurfaceInRegion(addr, size, [&](SurfaceId surface_id, Surface& surface) { + if (surface_id == region_owner_id) { return; } // If the CPU is invalidating this region we want to remove it // to (likely) mark the memory pages as uncached - if (!region_owner && size <= 8) { - FlushRegion(surface->addr, surface->size, surface); - remove_surfaces.push_back(surface); + if (!region_owner_id && size <= 8) { + FlushRegion(surface.addr, surface.size, surface_id); + remove_surfaces.push_back(surface_id); return; } - const SurfaceInterval interval = surface->GetInterval() & invalid_interval; - surface->invalid_regions.insert(interval); + const SurfaceInterval interval = surface.GetInterval() & invalid_interval; + surface.invalid_regions.insert(interval); // If the surface has no salvageable data it should be removed from the cache to avoid // clogging the data structure - if (surface->IsFullyInvalid()) { - remove_surfaces.push_back(surface); + if (surface.IsFullyInvalid()) { + remove_surfaces.push_back(surface_id); } }); - if (region_owner) { - dirty_regions.set({invalid_interval, region_owner}); + if (region_owner_id) { + dirty_regions.set({invalid_interval, region_owner_id}); } else { dirty_regions.erase(invalid_interval); } - for (Surface remove_surface : remove_surfaces) { - UnregisterSurface(remove_surface); + for (SurfaceId remove_id : remove_surfaces) { + UnregisterSurface(remove_id); } remove_surfaces.clear(); } template -auto RasterizerCache::CreateSurface(SurfaceParams& params) -> Surface { - Surface surface = std::make_shared(params, runtime); - surface->invalid_regions.insert(surface->GetInterval()); - return surface; +SurfaceId RasterizerCache::CreateSurface(const SurfaceParams& params) { + SurfaceId surface_id = slot_surfaces.insert(runtime, params); + Surface& surface = slot_surfaces[surface_id]; + surface.invalid_regions.insert(surface.GetInterval()); + return surface_id; } template -void RasterizerCache::RegisterSurface(const Surface& surface) { - ASSERT_MSG(!surface->registered, "Trying to register an already registered surface"); +void RasterizerCache::RegisterSurface(SurfaceId surface_id) { + Surface& surface = slot_surfaces[surface_id]; + ASSERT_MSG(!surface.registered, "Trying to register an already registered surface"); - surface->registered = true; - UpdatePagesCachedCount(surface->addr, surface->size, 1); - ForEachPage(surface->addr, surface->size, - [this, surface](u64 page) { page_table[page].push_back(surface); }); + surface.registered = true; + UpdatePagesCachedCount(surface.addr, surface.size, 1); + ForEachPage(surface.addr, surface.size, + [&](u64 page) { page_table[page].push_back(surface_id); }); } template -void RasterizerCache::UnregisterSurface(const Surface& surface) { - ASSERT_MSG(surface->registered, "Trying to unregister an already unregistered surface"); +void RasterizerCache::UnregisterSurface(SurfaceId surface_id) { + Surface& surface = slot_surfaces[surface_id]; + ASSERT_MSG(surface.registered, "Trying to unregister an already unregistered surface"); - surface->registered = false; - UpdatePagesCachedCount(surface->addr, surface->size, -1); + surface.registered = false; + UpdatePagesCachedCount(surface.addr, surface.size, -1); - ForEachPage(surface->addr, surface->size, [this, surface](u64 page) { + ForEachPage(surface.addr, surface.size, [&](u64 page) { const auto page_it = page_table.find(page); if (page_it == page_table.end()) { ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << CITRA_PAGEBITS); return; } - std::vector& surfaces = page_it->second; - const auto vector_it = std::find(surfaces.begin(), surfaces.end(), surface); - if (vector_it == surfaces.end()) { + std::vector& surface_ids = page_it->second; + const auto vector_it = std::find(surface_ids.begin(), surface_ids.end(), surface_id); + if (vector_it == surface_ids.end()) { ASSERT_MSG(false, "Unregistering unregistered surface in page=0x{:x}", page << CITRA_PAGEBITS); return; } - surfaces.erase(vector_it); + surface_ids.erase(vector_it); }); + + slot_surfaces.erase(surface_id); } template @@ -1265,6 +1316,7 @@ void RasterizerCache::UnregisterAll() { UnregisterSurface(surfaces.back()); } } + page_table.clear(); texture_cube_cache.clear(); remove_surfaces.clear(); runtime.Clear(); diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_base.h b/src/video_core/rasterizer_cache/rasterizer_cache_base.h index b9d969bb1..c3baa2a10 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache_base.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache_base.h @@ -46,21 +46,21 @@ class RasterizerCache : NonCopyable { static constexpr u64 CITRA_PAGEBITS = 18; using Runtime = typename T::Runtime; - using Surface = std::shared_ptr; + using Surface = typename T::Surface; using Sampler = typename T::Sampler; using Framebuffer = typename T::Framebuffer; /// Declare rasterizer interval types - using SurfaceMap = boost::icl::interval_map; - using SurfaceRect_Tuple = std::tuple>; + using SurfaceRect_Tuple = std::pair>; using PageMap = boost::icl::interval_map; struct RenderTargets { - Surface color_surface; - Surface depth_surface; + SurfaceId color_surface_id; + SurfaceId depth_surface_id; }; public: @@ -77,17 +77,19 @@ public: /// Perform hardware accelerated memory fill according to the provided configuration bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config); + /// Returns a reference to the surface object assigned to surface_id + Surface& GetSurface(SurfaceId surface_id); + /// Returns a reference to the sampler object matching the provided configuration Sampler& GetSampler(const Pica::TexturingRegs::TextureConfig& config); Sampler& GetSampler(SamplerId sampler_id); /// Copy one surface's region to another - void CopySurface(const Surface& src_surface, const Surface& dst_surface, - SurfaceInterval copy_interval); + void CopySurface(Surface& src_surface, Surface& dst_surface, SurfaceInterval copy_interval); /// Load a texture from 3DS memory to OpenGL and cache it (if not already cached) - Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, - bool load_if_create); + SurfaceId GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, + bool load_if_create); /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from /// 3DS memory to OpenGL and caches it (if not already cached) @@ -95,11 +97,11 @@ public: bool load_if_create); /// Get a surface based on the texture configuration - Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); - Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0); + Surface& GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); + Surface& GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0); /// Get a texture cube based on the texture configuration - const Surface& GetTextureCube(const TextureCubeConfig& config); + Surface& GetTextureCube(const TextureCubeConfig& config); /// Get the color and depth surfaces based on the framebuffer configuration Framebuffer GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb); @@ -111,10 +113,10 @@ public: SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params); /// Write any cached resources overlapping the region back to memory (if dirty) - void FlushRegion(PAddr addr, u32 size, Surface flush_surface = nullptr); + void FlushRegion(PAddr addr, u32 size, SurfaceId flush_surface_id = {}); /// Mark region as being invalidated by region_owner (nullptr if 3DS memory) - void InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner); + void InvalidateRegion(PAddr addr, u32 size, SurfaceId region_owner_id = {}); /// Flush all cached resources tracked by this cache manager void FlushAll(); @@ -145,47 +147,47 @@ private: /// Get the best surface match (and its match type) for the given flags template - Surface FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, - std::optional validate_interval = std::nullopt); + SurfaceId FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, + std::optional validate_interval = std::nullopt); /// Duplicates src_surface contents to dest_surface - void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface); + void DuplicateSurface(SurfaceId src_id, SurfaceId dst_id); /// Update surface's texture for given region when necessary - void ValidateSurface(const Surface& surface, PAddr addr, u32 size); + void ValidateSurface(SurfaceId surface_id, PAddr addr, u32 size); /// Copies pixel data in interval from the guest VRAM to the host GPU surface - void UploadSurface(const Surface& surface, SurfaceInterval interval); + void UploadSurface(Surface& surface, SurfaceInterval interval); /// Uploads a custom texture associated with upload_data to the target surface - bool UploadCustomSurface(const Surface& surface, const SurfaceParams& load_info, + bool UploadCustomSurface(Surface& surface, const SurfaceParams& load_info, std::span upload_data); /// Copies pixel data in interval from the host GPU surface to the guest VRAM - void DownloadSurface(const Surface& surface, SurfaceInterval interval); + void DownloadSurface(Surface& surface, SurfaceInterval interval); /// Downloads a fill surface to guest VRAM - void DownloadFillSurface(const Surface& surface, SurfaceInterval interval); + void DownloadFillSurface(Surface& surface, SurfaceInterval interval); /// Returns false if there is a surface in the cache at the interval with the same bit-width, - bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams params, + bool NoUnimplementedReinterpretations(Surface& surface, SurfaceParams params, SurfaceInterval interval); /// Return true if a surface with an invalid pixel format exists at the interval - bool IntervalHasInvalidPixelFormat(SurfaceParams params, SurfaceInterval interval); + bool IntervalHasInvalidPixelFormat(const SurfaceParams params, SurfaceInterval interval); /// Attempt to find a reinterpretable surface in the cache and use it to copy for validation - bool ValidateByReinterpretation(const Surface& surface, SurfaceParams params, + bool ValidateByReinterpretation(Surface& surface, SurfaceParams params, SurfaceInterval interval); /// Create a new surface - Surface CreateSurface(SurfaceParams& params); + SurfaceId CreateSurface(const SurfaceParams& params); /// Register surface into the cache - void RegisterSurface(const Surface& surface); + void RegisterSurface(SurfaceId surface_id); /// Remove surface from the cache - void UnregisterSurface(const Surface& surface); + void UnregisterSurface(SurfaceId surface_id); /// Unregisters all surfaces from the cache void UnregisterAll(); @@ -199,16 +201,17 @@ private: CustomTexManager& custom_tex_manager; PageMap cached_pages; SurfaceMap dirty_regions; - std::vector remove_surfaces; + std::vector remove_surfaces; u16 resolution_scale_factor; - std::unordered_map texture_cube_cache; + std::unordered_map texture_cube_cache; // The internal surface cache is based on buckets of 256KB. // This fits better for the purpose of this cache as textures are normaly // large in size. - std::unordered_map, Common::IdentityHash> page_table; + std::unordered_map, Common::IdentityHash> page_table; std::unordered_map samplers; + SlotVector slot_surfaces; SlotVector slot_samplers; RenderTargets render_targets; diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h index 5bb0ae784..85d2da6c5 100644 --- a/src/video_core/rasterizer_cache/surface_base.h +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -20,8 +20,7 @@ public: /// Returns true when this surface can be used to fill the fill_interval of dest_surface bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; - /// Returns true when copy_interval of dest_surface can be validated by copying from this - /// surface + /// Returns true when surface can validate copy_interval of dest_surface bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; /// Returns the region of the biggest valid rectange within interval diff --git a/src/video_core/rasterizer_cache/surface_params.h b/src/video_core/rasterizer_cache/surface_params.h index 647145cab..3a96d4b84 100644 --- a/src/video_core/rasterizer_cache/surface_params.h +++ b/src/video_core/rasterizer_cache/surface_params.h @@ -4,6 +4,7 @@ #pragma once +#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/utils.h" namespace VideoCore { diff --git a/src/video_core/rasterizer_cache/utils.cpp b/src/video_core/rasterizer_cache/utils.cpp index 2a486310a..f580a693e 100644 --- a/src/video_core/rasterizer_cache/utils.cpp +++ b/src/video_core/rasterizer_cache/utils.cpp @@ -10,46 +10,10 @@ namespace VideoCore { -ClearValue MakeClearValue(SurfaceType type, PixelFormat format, const u8* fill_data) { - ClearValue result{}; - switch (type) { - case SurfaceType::Color: - case SurfaceType::Texture: - case SurfaceType::Fill: { - Pica::Texture::TextureInfo tex_info{}; - tex_info.format = static_cast(format); - const auto color = Pica::Texture::LookupTexture(fill_data, 0, 0, tex_info); - result.color = color / 255.f; - break; - } - case SurfaceType::Depth: { - u32 depth_uint = 0; - if (format == PixelFormat::D16) { - std::memcpy(&depth_uint, fill_data, 2); - result.depth = depth_uint / 65535.0f; // 2^16 - 1 - } else if (format == PixelFormat::D24) { - std::memcpy(&depth_uint, fill_data, 3); - result.depth = depth_uint / 16777215.0f; // 2^24 - 1 - } - break; - } - case SurfaceType::DepthStencil: { - u32 clear_value_uint; - std::memcpy(&clear_value_uint, fill_data, sizeof(u32)); - result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 - result.stencil = (clear_value_uint >> 24); - break; - } - default: - UNREACHABLE_MSG("Invalid surface type!"); - } - - return result; -} - void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr, std::span source, std::span dest, bool convert) { - const u32 func_index = static_cast(surface_info.pixel_format); + const PixelFormat format = surface_info.pixel_format; + const u32 func_index = static_cast(format); if (surface_info.is_tiled) { const MortonFunc SwizzleImpl = @@ -87,7 +51,8 @@ u32 MipLevels(u32 width, u32 height, u32 max_level) { void DecodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr, std::span source, std::span dest, bool convert) { - const u32 func_index = static_cast(surface_info.pixel_format); + const PixelFormat format = surface_info.pixel_format; + const u32 func_index = static_cast(format); if (surface_info.is_tiled) { const MortonFunc UnswizzleImpl = diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index ac0006a2e..b1f7dc043 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -9,15 +9,18 @@ #include "common/hash.h" #include "common/math_util.h" #include "common/vector_math.h" -#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/slot_vector.h" +#include "video_core/regs_texturing.h" namespace VideoCore { using Rect2D = Common::Rectangle; +using SurfaceId = SlotId; using SamplerId = SlotId; +/// Fake surface ID for null surfaces +constexpr SurfaceId NULL_SURFACE_ID{0}; /// Fake sampler ID for null samplers constexpr SamplerId NULL_SAMPLER_ID{0}; @@ -103,8 +106,6 @@ struct TextureCubeConfig { class SurfaceParams; -[[nodiscard]] ClearValue MakeClearValue(SurfaceType type, PixelFormat format, const u8* fill_data); - u32 MipLevels(u32 width, u32 height, u32 max_level); /** diff --git a/src/video_core/renderer_opengl/gl_format_reinterpreter.h b/src/video_core/renderer_opengl/gl_format_reinterpreter.h index 5ffa81460..49006113c 100644 --- a/src/video_core/renderer_opengl/gl_format_reinterpreter.h +++ b/src/video_core/renderer_opengl/gl_format_reinterpreter.h @@ -4,6 +4,7 @@ #pragma once +#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/utils.h" #include "video_core/renderer_opengl/gl_resource_manager.h" diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 5f736422c..9858f03c9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -52,13 +52,6 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 state.clip_distance[0] = true; - // Create a 1x1 clear texture to use in the NULL case, - // instead of OpenGL's default of solid black - default_texture.Create(); - // For some reason alpha 0 wraps around to 1.0, so use 1/255 instead - u8 framebuffer_data[4] = {0, 0, 0, 1}; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); - // Generate VAO sw_vao.Create(); hw_vao.Create(); @@ -212,7 +205,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, u32 vertex_num = vs_input_index_max - vs_input_index_min + 1; u32 data_size = loader.byte_count * vertex_num; - res_cache.FlushRegion(data_addr, data_size, nullptr); + res_cache.FlushRegion(data_addr, data_size); std::memcpy(array_ptr, memory.GetPhysicalPointer(data_addr), data_size); array_ptr += data_size; @@ -409,95 +402,8 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { uniform_block_data.dirty = true; } - const auto BindCubeFace = [&](GLuint& target, Pica::TexturingRegs::CubeFace face, - Pica::Texture::TextureInfo& info) { - info.physical_address = regs.texturing.GetCubePhysicalAddress(face); - auto surface = res_cache.GetTextureSurface(info); - - if (surface != nullptr) { - target = surface->Handle(); - } else { - target = 0; - } - }; - // Sync and bind the texture surfaces - const auto pica_textures = regs.texturing.GetTextures(); - for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { - const auto& texture = pica_textures[texture_index]; - - if (texture.enabled) { - Sampler& sampler = res_cache.GetSampler(texture.config); - if (texture_index == 0) { - using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; - switch (texture.config.type.Value()) { - case TextureType::Shadow2D: { - const auto surface = res_cache.GetTextureSurface(texture); - if (surface) { - state.image_shadow_texture_px = surface->Handle(); - } else { - state.image_shadow_texture_px = 0; - } - continue; - } - case TextureType::ShadowCube: { - using CubeFace = Pica::TexturingRegs::CubeFace; - auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, - texture.format); - BindCubeFace(state.image_shadow_texture_px, CubeFace::PositiveX, info); - BindCubeFace(state.image_shadow_texture_nx, CubeFace::NegativeX, info); - BindCubeFace(state.image_shadow_texture_py, CubeFace::PositiveY, info); - BindCubeFace(state.image_shadow_texture_ny, CubeFace::NegativeY, info); - BindCubeFace(state.image_shadow_texture_pz, CubeFace::PositiveZ, info); - BindCubeFace(state.image_shadow_texture_nz, CubeFace::NegativeZ, info); - continue; - } - case TextureType::TextureCube: { - using CubeFace = Pica::TexturingRegs::CubeFace; - const VideoCore::TextureCubeConfig config = { - .px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX), - .nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX), - .py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY), - .ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY), - .pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ), - .nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ), - .width = texture.config.width, - .levels = texture.config.lod.max_level + 1, - .format = texture.format}; - - state.texture_cube_unit.texture_cube = - res_cache.GetTextureCube(config)->Handle(); - - state.texture_cube_unit.sampler = sampler.Handle(); - state.texture_units[texture_index].texture_2d = 0; - continue; // Texture unit 0 setup finished. Continue to next unit - } - default: - break; - } - - state.texture_cube_unit.texture_cube = 0; - } - - state.texture_units[texture_index].sampler = sampler.Handle(); - - auto surface = res_cache.GetTextureSurface(texture); - if (surface != nullptr) { - state.texture_units[texture_index].texture_2d = surface->Handle(); - } else { - // Can occur when texture addr is null or its memory is unmapped/invalid - // HACK: In this case, the correct behaviour for the PICA is to use the last - // rendered colour. But because this would be impractical to implement, the - // next best alternative is to use a clear texture, essentially skipping - // the geometry in question. - // For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn - // on the male character's face, which in the OpenGL default appear black. - state.texture_units[texture_index].texture_2d = default_texture.handle; - } - } else { - state.texture_units[texture_index].texture_2d = 0; - } - } + SyncTextureUnits(framebuffer); // Sync and bind the shader if (shader_dirty) { @@ -562,6 +468,114 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) { return succeeded; } +void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) { + using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; + + const auto pica_textures = regs.texturing.GetTextures(); + for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { + const auto& texture = pica_textures[texture_index]; + + // If the texture unit is disabled unbind the corresponding gl unit + if (!texture.enabled) { + state.texture_units[texture_index].texture_2d = 0; + continue; + } + + // Handle special tex0 configurations + if (texture_index == 0) { + switch (texture.config.type.Value()) { + case TextureType::Shadow2D: { + Surface& surface = res_cache.GetTextureSurface(texture); + state.image_shadow_texture_px = surface.Handle(); + continue; + } + case TextureType::ShadowCube: { + BindShadowCube(texture); + continue; + } + case TextureType::TextureCube: { + BindTextureCube(texture); + continue; + } + default: + break; + } + } + + // Bind texture unit sampler + Sampler& sampler = res_cache.GetSampler(texture.config); + state.texture_units[texture_index].sampler = sampler.Handle(); + + // Bind the texture provided by the rasterizer cache + Surface& surface = res_cache.GetTextureSurface(texture); + if (!IsFeedbackLoop(texture_index, framebuffer, surface)) { + state.texture_units[texture_index].texture_2d = surface.Handle(); + } + } +} + +void RasterizerOpenGL::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) { + using CubeFace = Pica::TexturingRegs::CubeFace; + auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); + constexpr std::array faces = { + CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY, + CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ, + }; + + for (CubeFace face : faces) { + const u32 binding = static_cast(face); + info.physical_address = regs.texturing.GetCubePhysicalAddress(face); + + Surface& surface = res_cache.GetTextureSurface(info); + state.image_shadow_texture[binding] = surface.Handle(); + } +} + +void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture) { + using CubeFace = Pica::TexturingRegs::CubeFace; + const VideoCore::TextureCubeConfig config = { + .px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX), + .nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX), + .py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY), + .ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY), + .pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ), + .nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ), + .width = texture.config.width, + .levels = texture.config.lod.max_level + 1, + .format = texture.format, + }; + + Surface& surface = res_cache.GetTextureCube(config); + Sampler& sampler = res_cache.GetSampler(texture.config); + state.texture_cube_unit.texture_cube = surface.Handle(); + state.texture_cube_unit.sampler = sampler.Handle(); + state.texture_units[0].texture_2d = 0; +} + +bool RasterizerOpenGL::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, + Surface& surface) { + const GLuint color_attachment = framebuffer.Attachment(SurfaceType::Color); + const bool is_feedback_loop = color_attachment == surface.Handle(); + if (!is_feedback_loop) { + return false; + } + + // Make a temporary copy of the framebuffer to sample from + Surface temp_surface{runtime, framebuffer.ColorParams()}; + const VideoCore::TextureCopy copy = { + .src_level = 0, + .dst_level = 0, + .src_layer = 0, + .dst_layer = 0, + .src_offset = {0, 0}, + .dst_offset = {0, 0}, + .extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()}, + }; + runtime.CopyTextures(surface, temp_surface, copy); + state.texture_units[texture_index].texture_2d = temp_surface.Handle(); + return true; +} + void RasterizerOpenGL::NotifyFixedFunctionPicaRegisterChanged(u32 id) { switch (id) { // Clipping plane @@ -641,13 +655,13 @@ void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } void RasterizerOpenGL::ClearAll(bool flush) { @@ -684,21 +698,22 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format); src_params.UpdateParams(); - const auto [src_surface, src_rect] = + const auto [src_surface_id, src_rect] = res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true); - if (src_surface == nullptr) { + if (!src_surface_id) { return false; } - const u32 scaled_width = src_surface->GetScaledWidth(); - const u32 scaled_height = src_surface->GetScaledHeight(); + Surface& src_surface = res_cache.GetSurface(src_surface_id); + const u32 scaled_width = src_surface.GetScaledWidth(); + const u32 scaled_height = src_surface.GetScaledHeight(); screen_info.display_texcoords = Common::Rectangle( (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - screen_info.display_texture = src_surface->Handle(); + screen_info.display_texture = src_surface.Handle(); return true; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 38b8f3a5e..404907dbf 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -94,6 +94,18 @@ private: void SyncAndUploadLUTs(); void SyncAndUploadLUTsLF(); + /// Syncs all enabled PICA texture units + void SyncTextureUnits(const Framebuffer& framebuffer); + + /// Binds the PICA shadow cube required for shadow mapping + void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture); + + /// Binds a texture cube to texture unit 0 + void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture); + + /// Makes a temporary copy of the framebuffer if a feedback loop is detected + bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface); + /// Upload the uniform blocks to the uniform buffer object void UploadUniforms(bool accelerate_draw); @@ -124,7 +136,6 @@ private: OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw std::array hw_vao_enabled_attributes{}; - OGLTexture default_texture; StreamBuffer vertex_buffer; StreamBuffer uniform_buffer; StreamBuffer index_buffer; diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index e3b85a297..418959749 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -116,12 +116,17 @@ public: // GL_IMAGE_BINDING_NAME GLuint image_shadow_buffer; - GLuint image_shadow_texture_px; - GLuint image_shadow_texture_nx; - GLuint image_shadow_texture_py; - GLuint image_shadow_texture_ny; - GLuint image_shadow_texture_pz; - GLuint image_shadow_texture_nz; + union { + std::array image_shadow_texture; + struct { + GLuint image_shadow_texture_px; + GLuint image_shadow_texture_nx; + GLuint image_shadow_texture_py; + GLuint image_shadow_texture_ny; + GLuint image_shadow_texture_pz; + GLuint image_shadow_texture_nz; + }; + }; struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index e6ba83648..d33e97d91 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -332,16 +332,16 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge } } -Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) - : VideoCore::SurfaceBase{params}, runtime{runtime}, driver{runtime.GetDriver()} { +Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params) + : VideoCore::SurfaceBase{params}, runtime{&runtime_}, driver{&runtime_.GetDriver()} { if (pixel_format != VideoCore::PixelFormat::Invalid) { - const auto& tuple = runtime.GetFormatTuple(pixel_format); - alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, tuple, texture_type); + const auto& tuple = runtime->GetFormatTuple(pixel_format); + alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, tuple, texture_type); } } Surface::~Surface() { - if (pixel_format == VideoCore::PixelFormat::Invalid) { + if (pixel_format == VideoCore::PixelFormat::Invalid || !Handle()) { return; } @@ -352,7 +352,7 @@ Surface::~Surface() { .height = alloc.height, .levels = alloc.levels, }; - runtime.Recycle(tag, std::move(alloc)); + runtime->Recycle(tag, std::move(alloc)); } void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) { @@ -398,10 +398,10 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi const VideoCore::Rect2D rect = download.texture_rect; glPixelStorei(GL_PACK_ROW_LENGTH, static_cast(rect.GetWidth())); - runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, - Handle()); + runtime->BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, + Handle()); - const auto& tuple = runtime.GetFormatTuple(pixel_format); + const auto& tuple = runtime->GetFormatTuple(pixel_format); glReadPixels(rect.left, rect.bottom, rect.GetWidth(), rect.GetHeight(), tuple.format, tuple.type, staging.mapped.data()); @@ -413,11 +413,11 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi } bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) { - if (!driver.IsCustomFormatSupported(format)) { + if (!driver->IsCustomFormatSupported(format)) { return false; } - const auto& tuple = runtime.GetFormatTuple(format); + const auto& tuple = runtime->GetFormatTuple(format); if (alloc.Matches(width, height, levels, tuple)) { return true; } @@ -429,11 +429,11 @@ bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) { .height = alloc.height, .levels = alloc.levels, }; - runtime.Recycle(tag, std::move(alloc)); + runtime->Recycle(tag, std::move(alloc)); is_custom = true; custom_format = format; - alloc = runtime.Allocate(width, height, levels, tuple, texture_type); + alloc = runtime->Allocate(width, height, levels, tuple, texture_type); LOG_DEBUG(Render_OpenGL, "Swapped {}x{} {} surface at address {:#x} to {}x{} {}", GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format), @@ -453,7 +453,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta unscaled_params.stride = rect_width; unscaled_params.height = rect_height; unscaled_params.res_scale = 1; - Surface unscaled_surface{unscaled_params, runtime}; + Surface unscaled_surface{*runtime, unscaled_params}; const VideoCore::BufferTextureCopy unscaled_upload = { .buffer_offset = upload.buffer_offset, @@ -462,7 +462,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta }; unscaled_surface.Upload(unscaled_upload, staging); - const auto& filterer = runtime.GetFilterer(); + const auto& filterer = runtime->GetFilterer(); if (!filterer.Filter(unscaled_surface.alloc.texture, unscaled_rect, alloc.texture, scaled_rect, type)) { const VideoCore::TextureBlit blit = { @@ -471,7 +471,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta .src_rect = unscaled_rect, .dst_rect = scaled_rect, }; - runtime.BlitTextures(unscaled_surface, *this, blit); + runtime->BlitTextures(unscaled_surface, *this, blit); } } @@ -488,7 +488,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, unscaled_params.stride = rect_width; unscaled_params.height = rect_height; unscaled_params.res_scale = 1; - Surface unscaled_surface{unscaled_params, runtime}; + Surface unscaled_surface{*runtime, unscaled_params}; // Blit the scaled rectangle to the unscaled texture const VideoCore::TextureBlit blit = { @@ -499,15 +499,15 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, .src_rect = scaled_rect, .dst_rect = unscaled_rect, }; - runtime.BlitTextures(*this, unscaled_surface, blit); + runtime->BlitTextures(*this, unscaled_surface, blit); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, unscaled_surface.Handle()); - const auto& tuple = runtime.GetFormatTuple(pixel_format); - if (driver.IsOpenGLES()) { - runtime.BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, - unscaled_surface.Handle()); + const auto& tuple = runtime->GetFormatTuple(pixel_format); + if (driver->IsOpenGLES()) { + runtime->BindFramebuffer(GL_READ_FRAMEBUFFER, download.texture_level, GL_TEXTURE_2D, type, + unscaled_surface.Handle()); glReadPixels(0, 0, rect_width, rect_height, tuple.format, tuple.type, staging.mapped.data()); } else { diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 3083eeba2..0d0777677 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -153,9 +153,15 @@ private: class Surface : public VideoCore::SurfaceBase { public: - explicit Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime); + explicit Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params); ~Surface(); + Surface(const Surface&) = delete; + Surface& operator=(const Surface&) = delete; + + Surface(Surface&& o) noexcept = default; + Surface& operator=(Surface&& o) noexcept = default; + /// Returns the surface image handle GLuint Handle() const noexcept { return alloc.texture.handle; @@ -186,9 +192,9 @@ private: const VideoCore::StagingData& staging); private: - TextureRuntime& runtime; - const Driver& driver; - Allocation alloc; + TextureRuntime* runtime; + const Driver* driver; + Allocation alloc{}; }; class Framebuffer : public VideoCore::FramebufferBase { diff --git a/src/video_core/renderer_vulkan/vk_common.h b/src/video_core/renderer_vulkan/vk_common.h index bd228fd9f..d5b5d6412 100644 --- a/src/video_core/renderer_vulkan/vk_common.h +++ b/src/video_core/renderer_vulkan/vk_common.h @@ -13,7 +13,6 @@ #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 #define VULKAN_HPP_NO_CONSTRUCTORS #define VULKAN_HPP_NO_STRUCT_SETTERS -#define VULKAN_HPP_NO_SMART_HANDLE #include #define VMA_STATIC_VULKAN_FUNCTIONS 0 diff --git a/src/video_core/renderer_vulkan/vk_format_reinterpreter.h b/src/video_core/renderer_vulkan/vk_format_reinterpreter.h index bd5ae88fe..1ff3a7414 100644 --- a/src/video_core/renderer_vulkan/vk_format_reinterpreter.h +++ b/src/video_core/renderer_vulkan/vk_format_reinterpreter.h @@ -4,6 +4,7 @@ #pragma once +#include "video_core/rasterizer_cache/pixel_format.h" #include "video_core/rasterizer_cache/utils.h" #include "video_core/renderer_vulkan/vk_common.h" diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8b0d27a5f..2470bba29 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -37,20 +37,6 @@ constexpr vk::BufferUsageFlags BUFFER_USAGE = vk::BufferUsageFlagBits::eVertexBu constexpr vk::BufferUsageFlags TEX_BUFFER_USAGE = vk::BufferUsageFlagBits::eUniformTexelBuffer; -constexpr VideoCore::SurfaceParams NULL_PARAMS = { - .width = 1, - .height = 1, - .stride = 1, - .texture_type = VideoCore::TextureType::Texture2D, - .pixel_format = VideoCore::PixelFormat::RGBA8, - .type = VideoCore::SurfaceType::Color, -}; - -constexpr vk::ImageUsageFlags NULL_USAGE = vk::ImageUsageFlagBits::eSampled | - vk::ImageUsageFlagBits::eTransferSrc | - vk::ImageUsageFlagBits::eTransferDst; -constexpr vk::ImageUsageFlags NULL_STORAGE_USAGE = NULL_USAGE | vk::ImageUsageFlagBits::eStorage; - struct DrawParams { u32 vertex_count; s32 vertex_offset; @@ -78,10 +64,6 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory, custom_tex_manager, runtime}, pipeline_cache{instance, scheduler, renderpass_cache, desc_manager}, - null_surface{NULL_PARAMS, vk::Format::eR8G8B8A8Unorm, NULL_USAGE, - vk::ImageAspectFlagBits::eColor, runtime}, - null_storage_surface{NULL_PARAMS, vk::Format::eR32Uint, NULL_STORAGE_USAGE, - vk::ImageAspectFlagBits::eColor, runtime}, stream_buffer{instance, scheduler, BUFFER_USAGE, STREAM_BUFFER_SIZE}, texture_buffer{instance, scheduler, TEX_BUFFER_USAGE, TextureBufferSize(instance)}, texture_lf_buffer{instance, scheduler, TEX_BUFFER_USAGE, TextureBufferSize(instance)}, @@ -127,18 +109,17 @@ RasterizerVulkan::RasterizerVulkan(Memory::MemorySystem& memory, pipeline_cache.BindTexelBuffer(3, texture_rg_view); pipeline_cache.BindTexelBuffer(4, texture_rgba_view); - const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID); + Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID); + Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID); for (u32 i = 0; i < 4; i++) { pipeline_cache.BindTexture(i, null_surface.ImageView(), null_sampler.Handle()); } for (u32 i = 0; i < 7; i++) { - pipeline_cache.BindStorageImage(i, null_storage_surface.ImageView()); + pipeline_cache.BindStorageImage(i, null_surface.StorageView()); } - // Explicitly call the derived version to avoid warnings about calling virtual - // methods in the constructor - RasterizerVulkan::SyncEntireState(); + SyncEntireState(); } RasterizerVulkan::~RasterizerVulkan() { @@ -566,111 +547,111 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { } void RasterizerVulkan::SyncTextureUnits(const Framebuffer& framebuffer) { - const auto pica_textures = regs.texturing.GetTextures(); - const vk::ImageView color_view = framebuffer.ImageView(SurfaceType::Color); + using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; + const auto pica_textures = regs.texturing.GetTextures(); for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { const auto& texture = pica_textures[texture_index]; - if (texture.enabled) { - Sampler& sampler = res_cache.GetSampler(texture.config); - if (texture_index == 0) { - using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; - switch (texture.config.type.Value()) { - case TextureType::Shadow2D: { - auto surface = res_cache.GetTextureSurface(texture); - if (surface) { - pipeline_cache.BindStorageImage(0, surface->StorageView()); - } else { - pipeline_cache.BindStorageImage(0, null_storage_surface.ImageView()); - } - continue; - } - case TextureType::ShadowCube: { - using CubeFace = Pica::TexturingRegs::CubeFace; - auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, - texture.format); - for (CubeFace face : - {CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY, - CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ}) { - info.physical_address = regs.texturing.GetCubePhysicalAddress(face); - auto surface = res_cache.GetTextureSurface(info); - - const u32 binding = static_cast(face); - if (surface) { - pipeline_cache.BindStorageImage(binding, surface->ImageView()); - } else { - pipeline_cache.BindStorageImage(binding, - null_storage_surface.ImageView()); - } - } - continue; - } - case TextureType::TextureCube: { - using CubeFace = Pica::TexturingRegs::CubeFace; - const VideoCore::TextureCubeConfig config = { - .px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX), - .nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX), - .py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY), - .ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY), - .pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ), - .nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ), - .width = texture.config.width, - .levels = texture.config.lod.max_level + 1, - .format = texture.format}; - - auto surface = res_cache.GetTextureCube(config); - if (surface) { - pipeline_cache.BindTexture(3, surface->ImageView(), sampler.Handle()); - } else { - pipeline_cache.BindTexture(3, null_surface.ImageView(), sampler.Handle()); - } - continue; // Texture unit 0 setup finished. Continue to next unit - } - default: - break; - } - } - - auto surface = res_cache.GetTextureSurface(texture); - if (surface) { - if (color_view == surface->ImageView()) { - Surface temp{*framebuffer.Color(), runtime}; - const VideoCore::TextureCopy copy = { - .src_level = 0, - .dst_level = 0, - .src_layer = 0, - .dst_layer = 0, - .src_offset = {0, 0}, - .dst_offset = {0, 0}, - .extent = {temp.GetScaledWidth(), temp.GetScaledHeight()}, - }; - runtime.CopyTextures(static_cast(*framebuffer.Color()), temp, copy); - pipeline_cache.BindTexture(texture_index, temp.ImageView(), sampler.Handle()); - } else { - pipeline_cache.BindTexture(texture_index, surface->ImageView(), - sampler.Handle()); - } - - } else { - // Can occur when texture addr is null or its memory is unmapped/invalid - // HACK: In this case, the correct behaviour for the PICA is to use the last - // rendered colour. But because this would be impractical to implement, the - // next best alternative is to use a clear texture, essentially skipping - // the geometry in question. - // For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn - // on the male character's face, which in the OpenGL default appear black. - pipeline_cache.BindTexture(texture_index, null_surface.ImageView(), - sampler.Handle()); - } - } else { + // If the texture unit is disabled bind a null surface to it + if (!texture.enabled) { + const Surface& null_surface = res_cache.GetSurface(VideoCore::NULL_SURFACE_ID); const Sampler& null_sampler = res_cache.GetSampler(VideoCore::NULL_SAMPLER_ID); pipeline_cache.BindTexture(texture_index, null_surface.ImageView(), null_sampler.Handle()); + continue; + } + + // Handle special tex0 configurations + if (texture_index == 0) { + switch (texture.config.type.Value()) { + case TextureType::Shadow2D: { + Surface& surface = res_cache.GetTextureSurface(texture); + pipeline_cache.BindStorageImage(0, surface.StorageView()); + continue; + } + case TextureType::ShadowCube: { + BindShadowCube(texture); + continue; + } + case TextureType::TextureCube: { + BindTextureCube(texture); + continue; + } + default: + break; + } + } + + // Bind the texture provided by the rasterizer cache + Surface& surface = res_cache.GetTextureSurface(texture); + Sampler& sampler = res_cache.GetSampler(texture.config); + if (!IsFeedbackLoop(texture_index, framebuffer, surface, sampler)) { + pipeline_cache.BindTexture(texture_index, surface.ImageView(), sampler.Handle()); } } } +void RasterizerVulkan::BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture) { + using CubeFace = Pica::TexturingRegs::CubeFace; + auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config, texture.format); + constexpr std::array faces = { + CubeFace::PositiveX, CubeFace::NegativeX, CubeFace::PositiveY, + CubeFace::NegativeY, CubeFace::PositiveZ, CubeFace::NegativeZ, + }; + + for (CubeFace face : faces) { + const u32 binding = static_cast(face); + info.physical_address = regs.texturing.GetCubePhysicalAddress(face); + + Surface& surface = res_cache.GetTextureSurface(info); + pipeline_cache.BindStorageImage(binding, surface.ImageView()); + } +} + +void RasterizerVulkan::BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture) { + using CubeFace = Pica::TexturingRegs::CubeFace; + const VideoCore::TextureCubeConfig config = { + .px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX), + .nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX), + .py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY), + .ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY), + .pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ), + .nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ), + .width = texture.config.width, + .levels = texture.config.lod.max_level + 1, + .format = texture.format, + }; + + Surface& surface = res_cache.GetTextureCube(config); + Sampler& sampler = res_cache.GetSampler(texture.config); + pipeline_cache.BindTexture(3, surface.ImageView(), sampler.Handle()); +} + +bool RasterizerVulkan::IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, + Surface& surface, Sampler& sampler) { + const vk::ImageView color_view = framebuffer.ImageView(SurfaceType::Color); + const bool is_feedback_loop = color_view == surface.ImageView(); + if (!is_feedback_loop) { + return false; + } + + // Make a temporary copy of the framebuffer to sample from + Surface temp_surface{runtime, framebuffer.ColorParams()}; + const VideoCore::TextureCopy copy = { + .src_level = 0, + .dst_level = 0, + .src_layer = 0, + .dst_layer = 0, + .src_offset = {0, 0}, + .dst_offset = {0, 0}, + .extent = {temp_surface.GetScaledWidth(), temp_surface.GetScaledHeight()}, + }; + runtime.CopyTextures(surface, temp_surface, copy); + pipeline_cache.BindTexture(texture_index, temp_surface.ImageView(), sampler.Handle()); + return true; +} + void RasterizerVulkan::NotifyFixedFunctionPicaRegisterChanged(u32 id) { switch (id) { // Clipping plane @@ -750,13 +731,13 @@ void RasterizerVulkan::FlushRegion(PAddr addr, u32 size) { void RasterizerVulkan::InvalidateRegion(PAddr addr, u32 size) { MICROPROFILE_SCOPE(Vulkan_CacheManagement); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } void RasterizerVulkan::FlushAndInvalidateRegion(PAddr addr, u32 size) { MICROPROFILE_SCOPE(Vulkan_CacheManagement); res_cache.FlushRegion(addr, size); - res_cache.InvalidateRegion(addr, size, nullptr); + res_cache.InvalidateRegion(addr, size); } void RasterizerVulkan::ClearAll(bool flush) { @@ -793,21 +774,22 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format); src_params.UpdateParams(); - const auto [src_surface, src_rect] = + const auto [src_surface_id, src_rect] = res_cache.GetSurfaceSubRect(src_params, VideoCore::ScaleMatch::Ignore, true); - if (src_surface == nullptr) { + if (!src_surface_id) { return false; } - const u32 scaled_width = src_surface->GetScaledWidth(); - const u32 scaled_height = src_surface->GetScaledHeight(); + const Surface& src_surface = res_cache.GetSurface(src_surface_id); + const u32 scaled_width = src_surface.GetScaledWidth(); + const u32 scaled_height = src_surface.GetScaledHeight(); screen_info.texcoords = Common::Rectangle( (float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width, (float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - screen_info.image_view = src_surface->ImageView(); + screen_info.image_view = src_surface.ImageView(); return true; } @@ -815,7 +797,10 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con void RasterizerVulkan::MakeSoftwareVertexLayout() { constexpr std::array sizes = {4, 4, 2, 2, 2, 1, 4, 3}; - software_layout = VertexLayout{.binding_count = 1, .attribute_count = 8}; + software_layout = VertexLayout{ + .binding_count = 1, + .attribute_count = 8, + }; for (u32 i = 0; i < software_layout.binding_count; i++) { VertexBinding& binding = software_layout.bindings[i]; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index f1275d605..1b483df3a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -100,6 +100,16 @@ private: /// Syncs all enabled PICA texture units void SyncTextureUnits(const Framebuffer& framebuffer); + /// Binds the PICA shadow cube required for shadow mapping + void BindShadowCube(const Pica::TexturingRegs::FullTextureConfig& texture); + + /// Binds a texture cube to texture unit 0 + void BindTextureCube(const Pica::TexturingRegs::FullTextureConfig& texture); + + /// Makes a temporary copy of the framebuffer if a feedback loop is detected + bool IsFeedbackLoop(u32 texture_index, const Framebuffer& framebuffer, Surface& surface, + Sampler& sampler); + /// Upload the uniform blocks to the uniform buffer object void UploadUniforms(bool accelerate_draw); @@ -140,8 +150,6 @@ private: std::array binding_offsets{}; std::array enable_attributes{}; std::array vertex_buffers; - Surface null_surface; - Surface null_storage_surface; PipelineInfo pipeline_info; StreamBuffer stream_buffer; ///< Vertex+Index+Uniform buffer diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 43af1ec0e..aa27a72f8 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -159,14 +159,6 @@ void TextureRuntime::Clear() { renderpass_cache.ClearFramebuffers(); for (const auto& [key, alloc] : texture_recycler) { vmaDestroyImage(allocator, alloc.image, alloc.allocation); - device.destroyImageView(alloc.image_view); - if (alloc.depth_view) { - device.destroyImageView(alloc.depth_view); - device.destroyImageView(alloc.stencil_view); - } - if (alloc.storage_view) { - device.destroyImageView(alloc.storage_view); - } } texture_recycler.clear(); @@ -269,7 +261,7 @@ Allocation TextureRuntime::Allocate(u32 width, u32 height, u32 levels, bool is_m }; vk::Device device = instance.GetDevice(); - const vk::ImageView image_view = device.createImageView(view_info); + vk::UniqueImageView image_view = device.createImageViewUnique(view_info); renderpass_cache.ExitRenderpass(); scheduler.Record([image, aspect](vk::CommandBuffer cmdbuf) { @@ -297,7 +289,7 @@ Allocation TextureRuntime::Allocate(u32 width, u32 height, u32 levels, bool is_m return Allocation{ .image = image, - .image_view = image_view, + .image_view = std::move(image_view), .allocation = allocation, .aspect = aspect, .format = format, @@ -778,28 +770,28 @@ bool TextureRuntime::NeedsConvertion(VideoCore::PixelFormat format) const { traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil); } -Surface::Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime) - : VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()}, - scheduler{runtime.GetScheduler()} { +Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params) + : VideoCore::SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, + scheduler{&runtime_.GetScheduler()} { if (pixel_format != VideoCore::PixelFormat::Invalid) { - alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, params.pixel_format, - texture_type); + alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, params.pixel_format, + texture_type); } } -Surface::Surface(const VideoCore::SurfaceParams& params, vk::Format format, - vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect, TextureRuntime& runtime) - : VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()}, - scheduler{runtime.GetScheduler()} { +Surface::Surface(TextureRuntime& runtime_, const VideoCore::SurfaceParams& params, + vk::Format format, vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect) + : VideoCore::SurfaceBase{params}, runtime{&runtime_}, instance{&runtime_.GetInstance()}, + scheduler{&runtime_.GetScheduler()} { if (format != vk::Format::eUndefined) { - alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, false, texture_type, - format, usage, aspect); + alloc = runtime->Allocate(GetScaledWidth(), GetScaledHeight(), levels, false, texture_type, + format, usage, aspect); } } Surface::~Surface() { - if (pixel_format == VideoCore::PixelFormat::Invalid) { + if (pixel_format == VideoCore::PixelFormat::Invalid || !alloc.image_view) { return; } @@ -811,11 +803,11 @@ Surface::~Surface() { .levels = alloc.levels, .is_mutable = alloc.is_mutable, }; - runtime.Recycle(tag, std::move(alloc)); + runtime->Recycle(tag, std::move(alloc)); } void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) { - runtime.renderpass_cache.ExitRenderpass(); + runtime->renderpass_cache.ExitRenderpass(); const bool is_scaled = res_scale != 1; if (is_scaled) { @@ -828,8 +820,8 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa .src_image = alloc.image, }; - scheduler.Record([buffer = runtime.upload_buffer.Handle(), format = alloc.format, params, - staging, upload](vk::CommandBuffer cmdbuf) { + scheduler->Record([buffer = runtime->upload_buffer.Handle(), format = alloc.format, params, + staging, upload](vk::CommandBuffer cmdbuf) { u32 num_copies = 1; std::array buffer_image_copies; @@ -901,12 +893,12 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa vk::DependencyFlagBits::eByRegion, {}, {}, write_barrier); }); - runtime.upload_buffer.Commit(staging.size); + runtime->upload_buffer.Commit(staging.size); } } void Surface::Download(const VideoCore::BufferTextureCopy& download, const StagingData& staging) { - runtime.renderpass_cache.ExitRenderpass(); + runtime->renderpass_cache.ExitRenderpass(); // For depth stencil downloads always use the compute shader fallback // to avoid having the interleave the data later. These should(?) be @@ -926,8 +918,8 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi .src_image = alloc.image, }; - scheduler.Record([buffer = runtime.download_buffer.Handle(), params, staging, - download](vk::CommandBuffer cmdbuf) { + scheduler->Record([buffer = runtime->download_buffer.Handle(), params, staging, + download](vk::CommandBuffer cmdbuf) { const VideoCore::Rect2D rect = download.texture_rect; const vk::BufferImageCopy buffer_image_copy = { .bufferOffset = staging.buffer_offset + download.buffer_offset, @@ -990,12 +982,12 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi vk::DependencyFlagBits::eByRegion, memory_write_barrier, {}, image_write_barrier); }); - runtime.download_buffer.Commit(staging.size); + runtime->download_buffer.Commit(staging.size); } } bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) { - const FormatTraits& traits = instance.GetTraits(format); + const FormatTraits& traits = instance->GetTraits(format); if (!traits.transfer_support) { return false; } @@ -1013,12 +1005,12 @@ bool Surface::Swap(u32 width, u32 height, VideoCore::CustomPixelFormat format) { .levels = levels, .is_mutable = alloc.is_mutable, }; - runtime.Recycle(tag, std::move(alloc)); + runtime->Recycle(tag, std::move(alloc)); is_custom = true; custom_format = format; - alloc = runtime.Allocate(width, height, levels, false, texture_type, custom_vk_format, - traits.usage, traits.aspect); + alloc = runtime->Allocate(width, height, levels, false, texture_type, custom_vk_format, + traits.usage, traits.aspect); LOG_DEBUG(Render_Vulkan, "Swapped {}x{} {} surface at address {:#x} to {}x{} {}", GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format), @@ -1065,15 +1057,15 @@ vk::PipelineStageFlags Surface::PipelineStageFlags() const noexcept { } vk::ImageView Surface::DepthView() noexcept { - vk::ImageView& depth_view = alloc.depth_view; + vk::UniqueImageView& depth_view = alloc.depth_view; if (depth_view) { - return depth_view; + return depth_view.get(); } const vk::ImageViewCreateInfo view_info = { .image = alloc.image, .viewType = vk::ImageViewType::e2D, - .format = instance.GetTraits(pixel_format).native, + .format = instance->GetTraits(pixel_format).native, .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eDepth, .baseMipLevel = 0, @@ -1083,20 +1075,20 @@ vk::ImageView Surface::DepthView() noexcept { }, }; - depth_view = instance.GetDevice().createImageView(view_info); - return depth_view; + depth_view = instance->GetDevice().createImageViewUnique(view_info); + return depth_view.get(); } vk::ImageView Surface::StencilView() noexcept { - vk::ImageView& stencil_view = alloc.stencil_view; + vk::UniqueImageView& stencil_view = alloc.stencil_view; if (stencil_view) { - return stencil_view; + return stencil_view.get(); } const vk::ImageViewCreateInfo view_info = { .image = alloc.image, .viewType = vk::ImageViewType::e2D, - .format = instance.GetTraits(pixel_format).native, + .format = instance->GetTraits(pixel_format).native, .subresourceRange{ .aspectMask = vk::ImageAspectFlagBits::eStencil, .baseMipLevel = 0, @@ -1106,14 +1098,14 @@ vk::ImageView Surface::StencilView() noexcept { }, }; - stencil_view = instance.GetDevice().createImageView(view_info); - return stencil_view; + stencil_view = instance->GetDevice().createImageViewUnique(view_info); + return stencil_view.get(); } vk::ImageView Surface::StorageView() noexcept { - vk::ImageView& storage_view = alloc.storage_view; + vk::UniqueImageView& storage_view = alloc.storage_view; if (storage_view) { - return storage_view; + return storage_view.get(); } ASSERT_MSG(pixel_format == VideoCore::PixelFormat::RGBA8, @@ -1132,8 +1124,8 @@ vk::ImageView Surface::StorageView() noexcept { .layerCount = VK_REMAINING_ARRAY_LAYERS, }, }; - storage_view = instance.GetDevice().createImageView(storage_view_info); - return storage_view; + storage_view = instance->GetDevice().createImageViewUnique(storage_view_info); + return storage_view.get(); } void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging) { @@ -1147,14 +1139,13 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta unscaled_params.stride = rect_width; unscaled_params.height = rect_height; unscaled_params.res_scale = 1; - Surface unscaled_surface{unscaled_params, runtime}; + Surface unscaled_surface{*runtime, unscaled_params}; const VideoCore::BufferTextureCopy unscaled_upload = { .buffer_offset = upload.buffer_offset, .buffer_size = upload.buffer_size, .texture_rect = unscaled_rect, }; - unscaled_surface.Upload(unscaled_upload, staging); const VideoCore::TextureBlit blit = { @@ -1165,8 +1156,7 @@ void Surface::ScaledUpload(const VideoCore::BufferTextureCopy& upload, const Sta .src_rect = unscaled_rect, .dst_rect = scaled_rect, }; - - runtime.BlitTextures(unscaled_surface, *this, blit); + runtime->BlitTextures(unscaled_surface, *this, blit); } void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, @@ -1182,7 +1172,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, unscaled_params.stride = rect_width; unscaled_params.height = rect_height; unscaled_params.res_scale = 1; - Surface unscaled_surface{unscaled_params, runtime}; + Surface unscaled_surface{*runtime, unscaled_params}; const VideoCore::TextureBlit blit = { .src_level = download.texture_level, @@ -1192,9 +1182,7 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, .src_rect = scaled_rect, .dst_rect = unscaled_rect, }; - - // Blit the scaled rectangle to the unscaled texture - runtime.BlitTextures(*this, unscaled_surface, blit); + runtime->BlitTextures(*this, unscaled_surface, blit); const VideoCore::BufferTextureCopy unscaled_download = { .buffer_offset = download.buffer_offset, @@ -1202,7 +1190,6 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download, .texture_rect = unscaled_rect, .texture_level = 0, }; - unscaled_surface.Download(unscaled_download, staging); } @@ -1224,10 +1211,10 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download, r32_params.height = scaled_rect.GetHeight(); r32_params.type = VideoCore::SurfaceType::Color; r32_params.res_scale = 1; - Surface r32_surface{r32_params, vk::Format::eR32Uint, + Surface r32_surface{*runtime, r32_params, vk::Format::eR32Uint, vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eStorage, - vk::ImageAspectFlagBits::eColor, runtime}; + vk::ImageAspectFlagBits::eColor}; const VideoCore::TextureBlit blit = { .src_level = download.texture_level, @@ -1237,8 +1224,7 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download, .src_rect = scaled_rect, .dst_rect = r32_scaled_rect, }; - - runtime.blit_helper.BlitD24S8ToR32(*this, r32_surface, blit); + runtime->blit_helper.BlitD24S8ToR32(*this, r32_surface, blit); // Blit the upper mip level to the lower one to scale without additional allocations const bool is_scaled = res_scale != 1; @@ -1251,8 +1237,7 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download, .src_rect = r32_scaled_rect, .dst_rect = unscaled_rect, }; - - runtime.BlitTextures(r32_surface, r32_surface, r32_blit); + runtime->BlitTextures(r32_surface, r32_surface, r32_blit); } const VideoCore::BufferTextureCopy r32_download = { @@ -1261,7 +1246,6 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download, .texture_rect = unscaled_rect, .texture_level = is_scaled ? 1u : 0u, }; - r32_surface.Download(r32_download, staging); } @@ -1309,8 +1293,7 @@ void Framebuffer::PrepareImages(Surface* const color, Surface* const depth_stenc Prepare(depth_stencil); } -Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params) - : device{runtime.GetInstance().GetDevice()} { +Sampler::Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params) { using TextureConfig = VideoCore::SamplerParams::TextureConfig; const Instance& instance = runtime.GetInstance(); @@ -1352,13 +1335,10 @@ Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params) .unnormalizedCoordinates = false, }; - sampler = device.createSampler(sampler_info); + vk::Device device = runtime.GetInstance().GetDevice(); + sampler = device.createSamplerUnique(sampler_info); } -Sampler::~Sampler() { - if (sampler) { - device.destroySampler(sampler); - } -} +Sampler::~Sampler() = default; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index cb93f2b84..9afeafc75 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -25,10 +25,10 @@ namespace Vulkan { struct Allocation { vk::Image image; - vk::ImageView image_view; - vk::ImageView depth_view; - vk::ImageView stencil_view; - vk::ImageView storage_view; + vk::UniqueImageView image_view; + vk::UniqueImageView depth_view; + vk::UniqueImageView stencil_view; + vk::UniqueImageView storage_view; VmaAllocation allocation; vk::ImageAspectFlags aspect; vk::Format format; @@ -85,8 +85,8 @@ class TextureRuntime { friend class Sampler; public: - TextureRuntime(const Instance& instance, Scheduler& scheduler, - RenderpassCache& renderpass_cache, DescriptorManager& desc_manager); + explicit TextureRuntime(const Instance& instance, Scheduler& scheduler, + RenderpassCache& renderpass_cache, DescriptorManager& desc_manager); ~TextureRuntime(); /// Causes a GPU command flush @@ -98,7 +98,7 @@ public: /// Takes back ownership of the allocation for recycling void Recycle(const HostTextureTag tag, Allocation&& alloc); - /// Maps an internal staging buffer of the provided size of pixel uploads/downloads + /// Maps an internal staging buffer of the provided size for pixel uploads/downloads [[nodiscard]] VideoCore::StagingData FindStaging(u32 size, bool upload); /// Allocates a vulkan image possibly resusing an existing one @@ -163,11 +163,17 @@ class Surface : public VideoCore::SurfaceBase { friend class TextureRuntime; public: - Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime); - Surface(const VideoCore::SurfaceParams& params, vk::Format format, vk::ImageUsageFlags usage, - vk::ImageAspectFlags aspect, TextureRuntime& runtime); + Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params); + Surface(TextureRuntime& runtime, const VideoCore::SurfaceParams& params, vk::Format format, + vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect); ~Surface(); + Surface(const Surface&) = delete; + Surface& operator=(const Surface&) = delete; + + Surface(Surface&& o) noexcept = default; + Surface& operator=(Surface&& o) noexcept = default; + /// Returns the surface aspect vk::ImageAspectFlags Aspect() const noexcept { return alloc.aspect; @@ -180,7 +186,7 @@ public: /// Returns an image view used to sample the surface from a shader vk::ImageView ImageView() const noexcept { - return alloc.image_view; + return alloc.image_view.get(); } /// Uploads pixel data in staging to a rectangle region of the surface texture @@ -218,17 +224,17 @@ private: /// Downloads scaled image by downscaling the requested rectangle void ScaledDownload(const VideoCore::BufferTextureCopy& download, - const VideoCore::StagingData& stagings); + const VideoCore::StagingData& staging); /// Downloads scaled depth stencil data void DepthStencilDownload(const VideoCore::BufferTextureCopy& download, const VideoCore::StagingData& staging); private: - TextureRuntime& runtime; - const Instance& instance; - Scheduler& scheduler; - Allocation alloc; + TextureRuntime* runtime; + const Instance* instance; + Scheduler* scheduler; + Allocation alloc{}; bool is_framebuffer{}; bool is_storage{}; }; @@ -282,34 +288,23 @@ private: u32 height{}; }; -/** - * @brief A sampler is used to configure the sampling parameters of a texture unit - */ class Sampler { public: - Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params); + Sampler(TextureRuntime& runtime, const VideoCore::SamplerParams& params); ~Sampler(); Sampler(const Sampler&) = delete; Sampler& operator=(const Sampler&) = delete; - Sampler(Sampler&& o) noexcept { - std::memcpy(this, &o, sizeof(Sampler)); - o.sampler = VK_NULL_HANDLE; - } - Sampler& operator=(Sampler&& o) noexcept { - std::memcpy(this, &o, sizeof(Sampler)); - o.sampler = VK_NULL_HANDLE; - return *this; - } + Sampler(Sampler&& o) noexcept = default; + Sampler& operator=(Sampler&& o) noexcept = default; [[nodiscard]] vk::Sampler Handle() const noexcept { - return sampler; + return *sampler; } private: - vk::Device device; - vk::Sampler sampler; + vk::UniqueSampler sampler; }; struct Traits {