diff --git a/src/video_core/custom_textures/custom_tex_manager.cpp b/src/video_core/custom_textures/custom_tex_manager.cpp index a3086c4df..2b76bca79 100644 --- a/src/video_core/custom_textures/custom_tex_manager.cpp +++ b/src/video_core/custom_textures/custom_tex_manager.cpp @@ -13,6 +13,7 @@ #include "core/frontend/image_interface.h" #include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/rasterizer_cache/surface_params.h" +#include "video_core/rasterizer_cache/utils.h" namespace VideoCore { diff --git a/src/video_core/rasterizer_cache/framebuffer_base.cpp b/src/video_core/rasterizer_cache/framebuffer_base.cpp index 7573254af..da6e2d909 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.cpp +++ b/src/video_core/rasterizer_cache/framebuffer_base.cpp @@ -10,9 +10,9 @@ namespace VideoCore { FramebufferBase::FramebufferBase() = default; -FramebufferBase::FramebufferBase(const Pica::Regs& regs, const SurfaceBase* const color, - u32 color_level, const SurfaceBase* const depth_stencil, - u32 depth_level, Common::Rectangle surfaces_rect) { +FramebufferBase::FramebufferBase(const Pica::Regs& regs, const SurfaceBase* color, u32 color_level, + const SurfaceBase* depth_stencil, u32 depth_level, + Common::Rectangle surfaces_rect) { res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u); // Determine the draw rectangle (render area + scissor) diff --git a/src/video_core/rasterizer_cache/framebuffer_base.h b/src/video_core/rasterizer_cache/framebuffer_base.h index 23efebb8b..c13e52956 100644 --- a/src/video_core/rasterizer_cache/framebuffer_base.h +++ b/src/video_core/rasterizer_cache/framebuffer_base.h @@ -29,8 +29,8 @@ struct ViewportInfo { class FramebufferBase { public: FramebufferBase(); - FramebufferBase(const Pica::Regs& regs, const SurfaceBase* const color, u32 color_level, - const SurfaceBase* const depth_stencil, u32 depth_level, + FramebufferBase(const Pica::Regs& regs, const SurfaceBase* color, u32 color_level, + const SurfaceBase* depth_stencil, u32 depth_level, Common::Rectangle surfaces_rect); SurfaceParams ColorParams() const noexcept { @@ -66,6 +66,7 @@ protected: switch (type) { case VideoCore::SurfaceType::Color: return 0; + case VideoCore::SurfaceType::Depth: case VideoCore::SurfaceType::DepthStencil: return 1; default: diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 2c7cc48b3..8bdd7b55e 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include "common/alignment.h" @@ -29,7 +30,7 @@ constexpr auto RangeFromInterval(const auto& map, const auto& interval) { template RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, - CustomTexManager& custom_tex_manager_, TextureRuntime& runtime_, + CustomTexManager& custom_tex_manager_, Runtime& runtime_, Pica::Regs& regs_, RendererBase& renderer_) : memory{memory_}, custom_tex_manager{custom_tex_manager_}, runtime{runtime_}, regs{regs_}, renderer{renderer_}, resolution_scale_factor{renderer.GetResolutionScaleFactor()}, @@ -39,6 +40,22 @@ 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_surfaces.insert(runtime, SurfaceParams{ + .width = 1, + .height = 1, + .stride = 1, + .texture_type = TextureType::CubeMap, + .pixel_format = PixelFormat::RGBA8, + .type = SurfaceType::Color, + })); void(slot_samplers.insert(runtime, SamplerParams{ .mag_filter = TextureConfig::TextureFilter::Linear, .min_filter = TextureConfig::TextureFilter::Linear, @@ -50,10 +67,7 @@ RasterizerCache::RasterizerCache(Memory::MemorySystem& memory_, template RasterizerCache::~RasterizerCache() { -#ifndef ANDROID - // This is for switching renderers, which is unsupported on Android, and costly on shutdown ClearAll(false); -#endif } template @@ -120,49 +134,56 @@ 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; - const auto [src_surface, src_rect] = GetTexCopySurface(src_params); - if (!src_surface) { + const auto [src_surface_id, src_rect] = GetTexCopySurface(src_params); + if (!src_surface_id) { return false; } + const 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; - const auto [dst_surface, dst_rect] = + const auto [dst_surface_id, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, load_gap); + if (!dst_surface_id) { + return false; + } - if (!dst_surface || dst_surface->type == SurfaceType::Texture || - !CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) { + 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; } @@ -187,38 +208,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) { + auto [src_surface_id, src_rect] = GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true); + if (!src_surface_id) { return false; } - dst_params.res_scale = src_surface->res_scale; + dst_params.res_scale = slot_surfaces[src_surface_id].res_scale; - const auto [dst_surface, dst_rect] = GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); - if (!dst_surface) { + const auto [dst_surface_id, dst_rect] = + GetSurfaceSubRect(dst_params, ScaleMatch::Upscale, false); + if (!dst_surface_id) { return false; } - if (src_surface->is_tiled != dst_surface->is_tiled) { + 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; } @@ -231,29 +256,35 @@ bool RasterizerCache::AccelerateFill(const GPU::Regs::MemoryFillConfig& confi params.type = SurfaceType::Fill; params.res_scale = std::numeric_limits::max(); - SurfaceRef fill_surface = std::make_shared(runtime, params); + SurfaceId fill_surface_id = slot_surfaces.insert(runtime, params); + Surface& fill_surface = slot_surfaces[fill_surface_id]; - std::memcpy(&fill_surface->fill_data[0], &config.value_32bit, sizeof(u32)); + std::memcpy(&fill_surface.fill_data[0], &config.value_32bit, sizeof(u32)); 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 -RasterizerCache::Sampler& RasterizerCache::GetSampler(SamplerId sampler_id) { +typename T::Surface& RasterizerCache::GetSurface(SurfaceId surface_id) { + return slot_surfaces[surface_id]; +} + +template +typename T::Sampler& RasterizerCache::GetSampler(SamplerId sampler_id) { return slot_samplers[sampler_id]; } template -RasterizerCache::Sampler& RasterizerCache::GetSampler( +typename T::Sampler& RasterizerCache::GetSampler( const Pica::TexturingRegs::TextureConfig& config) { const SamplerParams params = { .mag_filter = config.mag_filter, @@ -276,101 +307,105 @@ RasterizerCache::Sampler& RasterizerCache::GetSampler( } template -void RasterizerCache::CopySurface(const SurfaceRef& src_surface, const SurfaceRef& dst_surface, +void RasterizerCache::CopySurface(Surface& src_surface, Surface& dst_surface, SurfaceInterval copy_interval) { MICROPROFILE_SCOPE(RasterizerCache_CopySurface); const PAddr copy_addr = copy_interval.lower(); - const SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); - const auto dst_rect = dst_surface->GetScaledSubRect(subrect_params); - ASSERT(subrect_params.GetInterval() == copy_interval && src_surface != dst_surface); + const SurfaceParams subrect_params = dst_surface.FromInterval(copy_interval); + const auto dst_rect = dst_surface.GetScaledSubRect(subrect_params); + ASSERT(subrect_params.GetInterval() == copy_interval); - if (src_surface->type == SurfaceType::Fill) { + if (src_surface.type == SurfaceType::Fill) { const TextureClear 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, clear); + runtime.ClearTexture(dst_surface, clear); return; } const TextureBlit 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, blit); + runtime.BlitTextures(src_surface, dst_surface, blit); } template -RasterizerCache::SurfaceRef RasterizerCache::GetSurface(const SurfaceParams& params, - ScaleMatch match_res_scale, - bool load_if_create) { +SurfaceId RasterizerCache::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, + bool load_if_create) { if (params.addr == 0 || params.height * params.width == 0) { - return nullptr; + return {}; } // Use GetSurfaceSubRect instead ASSERT(params.width == params.stride); - ASSERT(!params.is_tiled || (params.width % 8 == 0 && params.height % 8 == 0)); // Check for an exact match in existing surfaces - SurfaceRef 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; - SurfaceRef 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 -RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSubRect( +typename RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSubRect( const SurfaceParams& params, ScaleMatch match_res_scale, bool load_if_create) { if (params.addr == 0 || params.height * params.width == 0) { - return std::make_tuple(nullptr, Common::Rectangle{}); + return std::make_pair(SurfaceId{}, Common::Rectangle{}); } // Attempt to find encompassing surface - SurfaceRef 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; + // 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_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); } } @@ -383,58 +418,66 @@ RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetSurfaceSubRect( } // 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); - SurfaceRef new_surface = CreateSurface(new_params); - DuplicateSurface(surface, new_surface); - UnregisterSurface(surface); - - surface = new_surface; - RegisterSurface(new_surface); + SurfaceId new_surface_id = CreateSurface(new_params); + DuplicateSurface(surface_id, new_surface_id); + UnregisterSurface(surface_id); + RegisterSurface(new_surface_id); + 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 -RasterizerCache::SurfaceRef RasterizerCache::GetTextureSurface( +typename T::Surface& RasterizerCache::GetTextureSurface( const Pica::TexturingRegs::FullTextureConfig& config) { 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); + const SurfaceId surface_id = GetTextureSurface(info, max_level); + return slot_surfaces[surface_id]; } template -RasterizerCache::SurfaceRef RasterizerCache::GetTextureSurface( - const Pica::Texture::TextureInfo& info, u32 max_level) { +SurfaceId RasterizerCache::GetTextureSurface(const Pica::Texture::TextureInfo& info, + u32 max_level) { 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 NULL_SURFACE_ID; } SurfaceParams params; @@ -451,34 +494,41 @@ RasterizerCache::SurfaceRef RasterizerCache::GetTextureSurface( const u32 min_height = info.height >> max_level; if (min_width % 8 != 0 || min_height % 8 != 0) { if (min_width % 4 == 0 && min_height % 4 == 0 && min_width * min_height <= 32) { - const auto [src_surface, rect] = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); - params.res_scale = src_surface->res_scale; - SurfaceRef tmp_surface = CreateSurface(params); + const auto [src_surface_id, rect] = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); + Surface& src_surface = slot_surfaces[src_surface_id]; + + params.res_scale = src_surface.res_scale; + SurfaceId tmp_surface_id = CreateSurface(params); + Surface& tmp_surface = slot_surfaces[tmp_surface_id]; const TextureBlit blit = { - .src_level = src_surface->LevelOf(params.addr), + .src_level = src_surface.LevelOf(params.addr), .dst_level = 0, .src_rect = rect, - .dst_rect = tmp_surface->GetScaledRect(), + .dst_rect = tmp_surface.GetScaledRect(), }; - runtime.BlitTextures(*src_surface, *tmp_surface, blit); - return tmp_surface; + runtime.BlitTextures(src_surface, tmp_surface, blit); + return tmp_surface_id; } LOG_CRITICAL(HW_GPU, "Texture size ({}x{}) is not multiple of 4", min_width, min_height); - return nullptr; + return 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 NULL_SURFACE_ID; } return GetSurface(params, ScaleMatch::Ignore, true); } template -RasterizerCache::SurfaceRef RasterizerCache::GetTextureCube(const TextureCubeConfig& config) { +typename T::Surface& RasterizerCache::GetTextureCube(const TextureCubeConfig& config) { + if (config.width == 0) [[unlikely]] { + return slot_surfaces[NULL_SURFACE_CUBE_ID]; + } + auto [it, new_surface] = texture_cube_cache.try_emplace(config); TextureCube& cube = it->second; @@ -495,53 +545,61 @@ RasterizerCache::SurfaceRef RasterizerCache::GetTextureCube(const TextureC .type = SurfaceType::Texture, }; cube_params.UpdateParams(); - cube.surface = CreateSurface(cube_params); + cube.surface_id = CreateSurface(cube_params); } - const u32 scaled_size = cube.surface->GetScaledWidth(); + const u32 scaled_size = slot_surfaces[cube.surface_id].GetScaledWidth(); const std::array addresses = {config.px, config.nx, config.py, config.ny, config.pz, config.nz}; + Pica::Texture::TextureInfo info = { + .width = config.width, + .height = config.width, + .format = config.format, + }; + info.SetDefaultStride(); + for (u32 i = 0; i < addresses.size(); i++) { if (!addresses[i]) { continue; } - Pica::Texture::TextureInfo info = { - .physical_address = addresses[i], - .width = config.width, - .height = config.width, - .format = config.format, - }; - info.SetDefaultStride(); + SurfaceId& face_surface_id = cube.face_ids[i]; + if (!face_surface_id) { + info.physical_address = addresses[i]; + face_surface_id = GetTextureSurface(info, config.levels - 1); + ASSERT_MSG(slot_surfaces[face_surface_id].levels == config.levels, + "Texture cube face levels do not match with the levels requested"); + } - SurfaceRef& face_surface = cube.faces[i]; - if (!face_surface || !face_surface->registered) { - face_surface = GetTextureSurface(info, config.levels - 1); - ASSERT(face_surface->levels == config.levels); + Surface& face_surface = slot_surfaces[face_surface_id]; + face_surface.flags |= SurfaceFlagBits::Tracked; + if (cube.ticks[i] == face_surface.ModificationTick()) { + continue; } - if (cube.ticks[i] != face_surface->ModificationTick()) { - for (u32 level = 0; level < face_surface->levels; level++) { - const TextureCopy texture_copy = { - .src_level = level, - .dst_level = level, - .src_layer = 0, - .dst_layer = i, - .src_offset = {0, 0}, - .dst_offset = {0, 0}, - .extent = {scaled_size >> level, scaled_size >> level}, - }; - runtime.CopyTextures(*face_surface, *cube.surface, texture_copy); - } - cube.ticks[i] = face_surface->ModificationTick(); + + Surface& cube_surface = slot_surfaces[cube.surface_id]; + for (u32 level = 0; level < face_surface.levels; level++) { + const u32 width_lod = scaled_size >> level; + const TextureCopy texture_copy = { + .src_level = level, + .dst_level = level, + .src_layer = 0, + .dst_layer = i, + .src_offset = {0, 0}, + .dst_offset = {0, 0}, + .extent = {width_lod, width_lod}, + }; + runtime.CopyTextures(face_surface, cube_surface, texture_copy); } + cube.ticks[i] = face_surface.ModificationTick(); } - return cube.surface; + return slot_surfaces[cube.surface_id]; } template -RasterizerCache::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, - bool using_depth_fb) { +typename T::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool using_color_fb, + bool using_depth_fb) { const auto& config = regs.framebuffer.framebuffer; const s32 framebuffer_width = config.GetWidth(); @@ -582,103 +640,111 @@ RasterizerCache::Framebuffer RasterizerCache::GetFramebufferSurfaces(bool } Common::Rectangle color_rect{}; - SurfaceRef color_surface = nullptr; + SurfaceId color_id{}; u32 color_level{}; if (using_color_fb) - std::tie(color_surface, color_rect) = - GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); + std::tie(color_id, color_rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); Common::Rectangle depth_rect{}; - SurfaceRef depth_surface = nullptr; + SurfaceId depth_id{}; u32 depth_level{}; if (using_depth_fb) - std::tie(depth_surface, depth_rect) = - GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); + std::tie(depth_id, depth_rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); Common::Rectangle fb_rect{}; - if (color_surface && depth_surface) { + if (color_id && depth_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_id = GetSurface(color_params, ScaleMatch::Exact, false); + depth_id = GetSurface(depth_params, ScaleMatch::Exact, false); + fb_rect = slot_surfaces[color_id].GetScaledRect(); } - } else if (color_surface) { + } else if (color_id) { fb_rect = color_rect; - } else if (depth_surface) { + } else if (depth_id) { fb_rect = depth_rect; } - if (color_surface) { + const Surface* color_surface = color_id ? &slot_surfaces[color_id] : nullptr; + const Surface* depth_surface = depth_id ? &slot_surfaces[depth_id] : nullptr; + + if (color_id) { color_level = color_surface->LevelOf(color_params.addr); - ValidateSurface(color_surface, boost::icl::first(color_vp_interval), + ValidateSurface(color_id, boost::icl::first(color_vp_interval), boost::icl::length(color_vp_interval)); } - if (depth_surface) { + if (depth_id) { depth_level = depth_surface->LevelOf(depth_params.addr); - ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), + ValidateSurface(depth_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_id = color_id, + .depth_id = depth_id, }; - return Framebuffer{ - runtime, color_surface.get(), color_level, depth_surface.get(), depth_level, regs, fb_rect}; + return Framebuffer{runtime, color_surface, color_level, depth_surface, + depth_level, regs, fb_rect}; } template void RasterizerCache::InvalidateFramebuffer(const Framebuffer& framebuffer) { + const auto invalidate = [&](SurfaceId surface_id) { + if (!surface_id) { + return; + } + Surface& surface = slot_surfaces[surface_id]; + const SurfaceInterval interval = framebuffer.Interval(surface.type); + const PAddr addr = boost::icl::first(interval); + const u32 size = boost::icl::length(interval); + InvalidateRegion(addr, size, surface_id); + }; if (framebuffer.HasAttachment(SurfaceType::Color)) { - const auto interval = framebuffer.Interval(SurfaceType::Color); - InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), - render_targets.color_surface); + invalidate(render_targets.color_id); } if (framebuffer.HasAttachment(SurfaceType::DepthStencil)) { - const auto interval = framebuffer.Interval(SurfaceType::DepthStencil); - InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval), - render_targets.depth_surface); + invalidate(render_targets.depth_id); } } template -RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetTexCopySurface( +typename RasterizerCache::SurfaceRect_Tuple RasterizerCache::GetTexCopySurface( const SurfaceParams& params) { Common::Rectangle rect{}; - SurfaceRef 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) { + ValidateSurface(match_id, params.addr, params.size); SurfaceParams match_subrect; + Surface& match_surface = slot_surfaces[match_id]; 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 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()) { @@ -688,52 +754,51 @@ void RasterizerCache::ForEachSurfaceInRegion(PAddr addr, size_t size, Func&& return; } } - for (const SurfaceRef& surface : it->second) { - if (surface->picked) { + for (const SurfaceId surface_id : it->second) { + Surface& surface = slot_surfaces[surface_id]; + if (True(surface.flags & SurfaceFlagBits::Picked)) { continue; } - if (!surface->Overlaps(addr, size)) { + if (!surface.Overlaps(addr, size)) { continue; } - surface->picked = true; - surfaces.push_back(surface); + surface.flags |= SurfaceFlagBits::Picked; + 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 SurfaceRef& surface : surfaces) { - surface->picked = false; + for (const SurfaceId surface_id : surfaces) { + slot_surfaces[surface_id].flags &= ~SurfaceFlagBits::Picked; } } template template -RasterizerCache::SurfaceRef RasterizerCache::FindMatch( - const SurfaceParams& params, ScaleMatch match_scale_type, - std::optional validate_interval) { - SurfaceRef match_surface = nullptr; +SurfaceId RasterizerCache::FindMatch(const SurfaceParams& params, ScaleMatch match_scale_type, + std::optional validate_interval) { + SurfaceId match_id{}; bool match_valid = false; u32 match_scale = 0; SurfaceInterval match_interval{}; - ForEachSurfaceInRegion(params.addr, params.size, [&](SurfaceRef 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); - // Validity will be checked in GetCopyableInterval + ? (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())); auto IsMatch_Helper = [&](auto check_type, auto match_fn) { if (False(find_flags & check_type)) @@ -746,21 +811,21 @@ RasterizerCache::SurfaceRef RasterizerCache::FindMatch( 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 auto UpdateMatch = [&] { - match_surface = surface; + match_id = 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; } @@ -776,36 +841,37 @@ RasterizerCache::SurfaceRef RasterizerCache::FindMatch( } }; 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()); }); }); - return match_surface; + return match_id; } template -void RasterizerCache::DuplicateSurface(const SurfaceRef& src_surface, - const SurfaceRef& dest_surface) { - ASSERT(dest_surface->addr <= src_surface->addr && dest_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 auto src_rect = src_surface->GetScaledRect(); - const auto dst_rect = dest_surface->GetScaledSubRect(*src_surface); + const auto src_rect = src_surface.GetScaledRect(); + const auto dst_rect = dst_surface.GetScaledSubRect(src_surface); ASSERT(src_rect.GetWidth() == dst_rect.GetWidth()); const TextureCopy copy = { @@ -815,61 +881,63 @@ void RasterizerCache::DuplicateSurface(const SurfaceRef& src_surface, .dst_offset = {dst_rect.left, dst_rect.bottom}, .extent = {src_rect.GetWidth(), src_rect.GetHeight()}, }; - runtime.CopyTextures(*src_surface, *dest_surface, copy); + runtime.CopyTextures(src_surface, dst_surface, copy); - dest_surface->invalid_regions -= src_surface->GetInterval(); - dest_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, dest_surface}); + dirty_regions.set({interval, dst_id}); } } template -void RasterizerCache::ValidateSurface(const SurfaceRef& 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 SurfaceInterval validate_interval(addr, addr + size); - if (surface->type == SurfaceType::Fill) { - ASSERT(surface->IsRegionValid(validate_interval)); + + if (surface.type == SurfaceType::Fill) { + ASSERT_MSG(surface.IsRegionValid(validate_interval), + "Attempted to validate a non-valid fill surface"); return; } - SurfaceRegions validate_regions = surface->invalid_regions & validate_interval; + SurfaceRegions validate_regions = surface.invalid_regions & validate_interval; auto notify_validated = [&](SurfaceInterval interval) { - surface->MarkValid(interval); + surface.MarkValid(interval); validate_regions.erase(interval); }; - u32 level = surface->LevelOf(addr); - SurfaceInterval level_interval = surface->LevelInterval(level); + u32 level = surface.LevelOf(addr); + SurfaceInterval level_interval = surface.LevelInterval(level); while (!validate_regions.empty()) { // Take an invalid interval from the validation regions and clamp it - // to the current level interval since FromInterval cannot process - // intervals that span multiple levels. If the interval is empty + // to the current level interval. If the interval is empty // then we have validated the entire level so move to the next. const auto interval = *validate_regions.begin() & level_interval; if (boost::icl::is_empty(interval)) { - level_interval = surface->LevelInterval(++level); + level_interval = surface.LevelInterval(++level); continue; } // Look for a valid surface to copy from. - const SurfaceParams params = surface->FromInterval(interval); - const SurfaceRef copy_surface = + const SurfaceParams params = surface.FromInterval(interval); + const SurfaceId copy_surface_id = FindMatch(params, ScaleMatch::Ignore, interval); - - if (copy_surface) { - const SurfaceInterval copy_interval = copy_surface->GetCopyableInterval(params); + if (copy_surface_id && copy_surface_id != surface_id) { + Surface& copy_surface = slot_surfaces[copy_surface_id]; + const SurfaceInterval copy_interval = copy_surface.GetCopyableInterval(params); CopySurface(copy_surface, surface, copy_interval); notify_validated(copy_interval); continue; @@ -898,26 +966,28 @@ void RasterizerCache::ValidateSurface(const SurfaceRef& surface, PAddr addr, // Load data from 3DS memory FlushRegion(params.addr, params.size); - UploadSurface(surface, interval); + if (!use_custom_textures || !UploadCustomSurface(surface_id, interval)) { + UploadSurface(surface, interval); + } notify_validated(params.GetInterval()); } // Filtered mipmaps often look really bad. We can achieve better quality by // generating them from the base level. - if (surface->res_scale != 1 && level != 0) { - runtime.GenerateMipmaps(*surface); + if (surface.res_scale != 1 && level != 0) { + runtime.GenerateMipmaps(surface); } } template -void RasterizerCache::UploadSurface(const SurfaceRef& surface, SurfaceInterval interval) { +void RasterizerCache::UploadSurface(Surface& surface, SurfaceInterval interval) { MICROPROFILE_SCOPE(RasterizerCache_UploadSurface); - const SurfaceParams load_info = surface->FromInterval(interval); - ASSERT(load_info.addr >= surface->addr && load_info.end <= surface->end); + const SurfaceParams load_info = surface.FromInterval(interval); + ASSERT(load_info.addr >= surface.addr && load_info.end <= surface.end); const auto staging = runtime.FindStaging( - load_info.width * load_info.height * surface->GetInternalBytesPerPixel(), true); + load_info.width * load_info.height * surface.GetInternalBytesPerPixel(), true); MemoryRef source_ptr = memory.GetPhysicalRef(load_info.addr); if (!source_ptr) [[unlikely]] { @@ -926,54 +996,76 @@ void RasterizerCache::UploadSurface(const SurfaceRef& surface, SurfaceInterva const auto upload_data = source_ptr.GetWriteBytes(load_info.end - load_info.addr); DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, staging.mapped, - runtime.NeedsConversion(surface->pixel_format)); + runtime.NeedsConversion(surface.pixel_format)); - if (use_custom_textures) { - const u64 hash = ComputeCustomHash(load_info, staging.mapped, upload_data); - if (UploadCustomSurface(surface, load_info, hash)) { - return; - } - } - if (dump_textures && !surface->is_custom) { + if (dump_textures && False(surface.flags & SurfaceFlagBits::Custom)) { const u64 hash = Common::ComputeHash64(upload_data.data(), upload_data.size()); - const u32 level = surface->LevelOf(load_info.addr); + const u32 level = surface.LevelOf(load_info.addr); custom_tex_manager.DumpTexture(load_info, level, upload_data, hash); } 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 SurfaceRef& surface, - const SurfaceParams& load_info, u64 hash) { - const u32 level = surface->LevelOf(load_info.addr); - const bool is_base_level = level == 0; +bool RasterizerCache::UploadCustomSurface(SurfaceId surface_id, SurfaceInterval interval) { + MICROPROFILE_SCOPE(RasterizerCache_UploadSurface); + + Surface& surface = slot_surfaces[surface_id]; + const SurfaceParams load_info = surface.FromInterval(interval); + ASSERT(load_info.addr >= surface.addr && load_info.end <= surface.end); + + MemoryRef source_ptr = memory.GetPhysicalRef(load_info.addr); + if (!source_ptr) [[unlikely]] { + return false; + } + + const auto upload_data = source_ptr.GetWriteBytes(load_info.end - load_info.addr); + const u64 hash = [&] { + if (!custom_tex_manager.UseNewHash()) { + const u32 width = load_info.width; + const u32 height = load_info.height; + const u32 bpp = surface.GetInternalBytesPerPixel(); + auto decoded = std::vector(width * height * bpp); + DecodeTexture(load_info, load_info.addr, load_info.end, upload_data, decoded, false); + return Common::ComputeHash64(decoded.data(), decoded.size()); + } else { + return Common::ComputeHash64(upload_data.data(), upload_data.size()); + } + }(); + + const u32 level = surface.LevelOf(load_info.addr); Material* material = custom_tex_manager.GetMaterial(hash); if (!material) { - return surface->IsCustom(); + return surface.IsCustom(); } - if (!is_base_level && custom_tex_manager.SkipMipmaps()) { + if (level != 0 && custom_tex_manager.SkipMipmaps()) { return true; } - surface->is_custom = true; + surface.flags |= SurfaceFlagBits::Custom; - const auto upload = [this, level, surface, material]() -> bool { - if (!surface->IsCustom() && !surface->Swap(material)) { + const auto upload = [this, level, surface_id, material]() -> bool { + Surface& surface = slot_surfaces[surface_id]; + if (False(surface.flags & SurfaceFlagBits::Custom)) { + LOG_ERROR(HW_GPU, "Surface is not suitable for custom upload, aborting!"); + return false; + } + if (!surface.IsCustom() && !surface.Swap(material)) { LOG_ERROR(HW_GPU, "Custom compressed format {} unsupported by host GPU", material->format); return false; } - surface->UploadCustom(material, level); + surface.UploadCustom(material, level); if (custom_tex_manager.SkipMipmaps()) { - runtime.GenerateMipmaps(*surface); + runtime.GenerateMipmaps(surface); } return true; }; @@ -981,35 +1073,24 @@ bool RasterizerCache::UploadCustomSurface(const SurfaceRef& surface, } template -u64 RasterizerCache::ComputeCustomHash(const SurfaceParams& load_info, std::span decoded, - std::span upload_data) { - MICROPROFILE_SCOPE(RasterizerCache_ComputeHash); - - if (custom_tex_manager.UseNewHash()) { - return Common::ComputeHash64(upload_data.data(), upload_data.size()); - } - return Common::ComputeHash64(decoded.data(), decoded.size()); -} - -template -void RasterizerCache::DownloadSurface(const SurfaceRef& surface, SurfaceInterval interval) { +void RasterizerCache::DownloadSurface(Surface& surface, SurfaceInterval interval) { MICROPROFILE_SCOPE(RasterizerCache_DownloadSurface); - const SurfaceParams flush_info = surface->FromInterval(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 auto staging = runtime.FindStaging( - flush_info.width * flush_info.height * surface->GetInternalBytesPerPixel(), false); + flush_info.width * flush_info.height * surface.GetInternalBytesPerPixel(), 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); MemoryRef dest_ptr = memory.GetPhysicalRef(flush_start); if (!dest_ptr) [[unlikely]] { @@ -1018,34 +1099,34 @@ void RasterizerCache::DownloadSurface(const SurfaceRef& surface, SurfaceInter const auto download_dest = dest_ptr.GetWriteBytes(flush_end - flush_start); EncodeTexture(flush_info, flush_start, flush_end, staging.mapped, download_dest, - runtime.NeedsConversion(surface->pixel_format)); + runtime.NeedsConversion(surface.pixel_format)); } template -void RasterizerCache::DownloadFillSurface(const SurfaceRef& 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) { @@ -1054,7 +1135,7 @@ void RasterizerCache::DownloadFillSurface(const SurfaceRef& surface, SurfaceI } template -bool RasterizerCache::NoUnimplementedReinterpretations(const SurfaceRef& surface, +bool RasterizerCache::NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams params, const SurfaceInterval& interval) { static constexpr std::array all_formats{ @@ -1066,16 +1147,15 @@ bool RasterizerCache::NoUnimplementedReinterpretations(const SurfaceRef& surf }; 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 - SurfaceRef test_surface = + SurfaceId test_surface_id = FindMatch(params, ScaleMatch::Ignore, interval); - if (test_surface) { + if (test_surface_id) { LOG_WARNING(HW_GPU, "Missing pixel_format reinterpreter: {} -> {}", - PixelFormatAsString(format), - PixelFormatAsString(surface->pixel_format)); + PixelFormatAsString(format), PixelFormatAsString(surface.pixel_format)); implemented = false; } } @@ -1087,9 +1167,9 @@ template bool RasterizerCache::IntervalHasInvalidPixelFormat(const SurfaceParams& params, const SurfaceInterval& interval) { bool invalid_format_found = false; - ForEachSurfaceInRegion(params.addr, params.end, [&](SurfaceRef 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; } @@ -1099,20 +1179,21 @@ bool RasterizerCache::IntervalHasInvalidPixelFormat(const SurfaceParams& para } template -bool RasterizerCache::ValidateByReinterpretation(const SurfaceRef& surface, SurfaceParams params, +bool RasterizerCache::ValidateByReinterpretation(Surface& surface, SurfaceParams params, const 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(); - SurfaceRef reinterpret_surface = + SurfaceId reinterpret_surface_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); - reinterpreter->Reinterpret(*reinterpret_surface, src_rect, *surface, dest_rect); + if (reinterpret_surface_id) { + Surface& reinterpret_surface = slot_surfaces[reinterpret_surface_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); return true; } @@ -1147,27 +1228,28 @@ void RasterizerCache::ClearAll(bool flush) { } template -void RasterizerCache::FlushRegion(PAddr addr, u32 size, SurfaceRef flush_surface) { - if (size == 0) +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; - for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) { + for (const auto& [region, 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 auto interval = size <= 8 ? region : region & flush_interval; + if (flush_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); @@ -1186,86 +1268,91 @@ void RasterizerCache::FlushAll() { } template -void RasterizerCache::InvalidateRegion(PAddr addr, u32 size, const SurfaceRef& region_owner) { - if (size == 0) +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) { - ASSERT(region_owner->type != SurfaceType::Texture); - 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->MarkValid(invalid_interval); + if (region_owner_id) { + Surface& region_owner = slot_surfaces[region_owner_id]; + ASSERT(region_owner.type != SurfaceType::Texture); + ASSERT(addr >= region_owner.addr && addr + size <= region_owner.end); + ASSERT(region_owner.width == region_owner.stride); + region_owner.MarkValid(invalid_interval); } - ForEachSurfaceInRegion(addr, size, [&](SurfaceRef 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 auto interval = surface->GetInterval() & invalid_interval; - surface->MarkInvalid(interval); + surface.MarkInvalid(surface.GetInterval() & invalid_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 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_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 (const SurfaceRef remove_surface : remove_surfaces) { - UnregisterSurface(remove_surface); + for (const SurfaceId remove_surface_id : remove_surfaces) { + UnregisterSurface(remove_surface_id); } remove_surfaces.clear(); } template -RasterizerCache::SurfaceRef RasterizerCache::CreateSurface(const SurfaceParams& params) { - SurfaceRef surface = std::make_shared(runtime, params); - surface->MarkInvalid(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.MarkInvalid(surface.GetInterval()); + return surface_id; } template -void RasterizerCache::RegisterSurface(const SurfaceRef& 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(False(surface.flags & SurfaceFlagBits::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.flags |= SurfaceFlagBits::Registered; + UpdatePagesCachedCount(surface.addr, surface.size, 1); + ForEachPage(surface.addr, surface.size, + [this, surface_id](u64 page) { page_table[page].push_back(surface_id); }); } template -void RasterizerCache::UnregisterSurface(const SurfaceRef& 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(True(surface.flags & SurfaceFlagBits::Registered), + "Trying to unregister an already unregistered surface"); - surface->registered = false; - UpdatePagesCachedCount(surface->addr, surface->size, -1); - ForEachPage(surface->addr, surface->size, [this, surface](u64 page) { - auto page_it = page_table.find(page); + surface.flags &= ~SurfaceFlagBits::Registered; + UpdatePagesCachedCount(surface.addr, surface.size, -1); + ForEachPage(surface.addr, surface.size, [this, surface_id](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.value(); - const auto vector_it = std::find(surfaces.begin(), surfaces.end(), surface); + std::vector& surfaces = page_it.value(); + const auto vector_it = std::find(surfaces.begin(), surfaces.end(), surface_id); if (vector_it == surfaces.end()) { ASSERT_MSG(false, "Unregistering unregistered surface in page=0x{:x}", page << CITRA_PAGEBITS); @@ -1273,6 +1360,18 @@ void RasterizerCache::UnregisterSurface(const SurfaceRef& surface) { } surfaces.erase(vector_it); }); + + if (True(surface.flags & SurfaceFlagBits::Tracked)) { + for (auto& [config, cube] : texture_cube_cache) { + std::array& face_ids = cube.face_ids; + const auto it = std::find(face_ids.begin(), face_ids.end(), surface_id); + if (it != face_ids.end()) { + *it = SurfaceId{}; + } + } + } + + slot_surfaces.erase(surface_id); } template diff --git a/src/video_core/rasterizer_cache/rasterizer_cache_base.h b/src/video_core/rasterizer_cache/rasterizer_cache_base.h index 604e88b9d..dd4402cf8 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache_base.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache_base.h @@ -51,32 +51,32 @@ class RasterizerCache { /// Address shift for caching surfaces into a hash table static constexpr u64 CITRA_PAGEBITS = 18; - using TextureRuntime = typename T::TextureRuntime; + using Runtime = typename T::Runtime; using Sampler = typename T::Sampler; - using SurfaceRef = std::shared_ptr; + using Surface = typename T::Surface; using Framebuffer = typename T::Framebuffer; - using SurfaceMap = boost::icl::interval_map; - using SurfaceRect_Tuple = std::tuple>; + using SurfaceRect_Tuple = std::pair>; using PageMap = boost::icl::interval_map; struct RenderTargets { - SurfaceRef color_surface; - SurfaceRef depth_surface; + SurfaceId color_id; + SurfaceId depth_id; }; struct TextureCube { - SurfaceRef surface{}; - std::array faces{}; - std::array ticks{}; + SurfaceId surface_id; + std::array face_ids; + std::array ticks; }; public: explicit RasterizerCache(Memory::MemorySystem& memory, CustomTexManager& custom_tex_manager, - TextureRuntime& runtime, Pica::Regs& regs, RendererBase& renderer); + Runtime& runtime, Pica::Regs& regs, RendererBase& renderer); ~RasterizerCache(); /// Notify the cache that a new frame has been queued @@ -91,17 +91,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 SurfaceRef& src_surface, const SurfaceRef& 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) - SurfaceRef 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) @@ -109,11 +111,11 @@ public: bool load_if_create); /// Get a surface based on the texture configuration - SurfaceRef GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); - SurfaceRef GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0); + Surface& GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); + SurfaceId GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0); /// Get a texture cube based on the texture configuration - SurfaceRef 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); @@ -125,10 +127,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, SurfaceRef flush_surface = nullptr); + void FlushRegion(PAddr addr, u32 size, SurfaceId flush_surface = {}); /// Mark region as being invalidated by region_owner (nullptr if 3DS memory) - void InvalidateRegion(PAddr addr, u32 size, const SurfaceRef& region_owner); + void InvalidateRegion(PAddr addr, u32 size, SurfaceId region_owner = {}); /// Flush all cached resources tracked by this cache manager void FlushAll(); @@ -159,33 +161,29 @@ private: /// Get the best surface match (and its match type) for the given flags template - SurfaceRef 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); /// Transfers ownership of a memory region from src_surface to dest_surface - void DuplicateSurface(const SurfaceRef& src_surface, const SurfaceRef& dest_surface); + void DuplicateSurface(SurfaceId src_id, SurfaceId dst_id); /// Update surface's texture for given region when necessary - void ValidateSurface(const SurfaceRef& surface, PAddr addr, u32 size); + void ValidateSurface(SurfaceId surface, PAddr addr, u32 size); /// Copies pixel data in interval from the guest VRAM to the host GPU surface - void UploadSurface(const SurfaceRef& surface, SurfaceInterval interval); + void UploadSurface(Surface& surface, SurfaceInterval interval); /// Uploads a custom texture identified with hash to the target surface - bool UploadCustomSurface(const SurfaceRef& surface, const SurfaceParams& load_info, u64 hash); - - /// Returns the hash used to lookup the custom surface. - u64 ComputeCustomHash(const SurfaceParams& load_info, std::span decoded, - std::span upload_data); + bool UploadCustomSurface(SurfaceId surface_id, SurfaceInterval interval); /// Copies pixel data in interval from the host GPU surface to the guest VRAM - void DownloadSurface(const SurfaceRef& surface, SurfaceInterval interval); + void DownloadSurface(Surface& surface, SurfaceInterval interval); /// Downloads a fill surface to guest VRAM - void DownloadFillSurface(const SurfaceRef& 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 SurfaceRef& surface, SurfaceParams params, + bool NoUnimplementedReinterpretations(const Surface& surface, SurfaceParams params, const SurfaceInterval& interval); /// Return true if a surface with an invalid pixel format exists at the interval @@ -193,17 +191,17 @@ private: const SurfaceInterval& interval); /// Attempt to find a reinterpretable surface in the cache and use it to copy for validation - bool ValidateByReinterpretation(const SurfaceRef& surface, SurfaceParams params, + bool ValidateByReinterpretation(Surface& surface, SurfaceParams params, const SurfaceInterval& interval); /// Create a new surface - SurfaceRef CreateSurface(const SurfaceParams& params); + SurfaceId CreateSurface(const SurfaceParams& params); /// Register surface into the cache - void RegisterSurface(const SurfaceRef& surface); + void RegisterSurface(SurfaceId surface); /// Remove surface from the cache - void UnregisterSurface(const SurfaceRef& surface); + void UnregisterSurface(SurfaceId surface); /// Unregisters all surfaces from the cache void UnregisterAll(); @@ -214,16 +212,17 @@ private: private: Memory::MemorySystem& memory; CustomTexManager& custom_tex_manager; - TextureRuntime& runtime; + Runtime& runtime; Pica::Regs& regs; RendererBase& renderer; std::unordered_map texture_cube_cache; - tsl::robin_pg_map, Common::IdentityHash> page_table; + tsl::robin_pg_map, Common::IdentityHash> page_table; std::unordered_map samplers; + SlotVector slot_surfaces; SlotVector slot_samplers; SurfaceMap dirty_regions; PageMap cached_pages; - std::vector remove_surfaces; + std::vector remove_surfaces; u32 resolution_scale_factor; RenderTargets render_targets; bool use_filter; diff --git a/src/video_core/rasterizer_cache/sampler_params.h b/src/video_core/rasterizer_cache/sampler_params.h index e0e3c2b57..fcb47394e 100644 --- a/src/video_core/rasterizer_cache/sampler_params.h +++ b/src/video_core/rasterizer_cache/sampler_params.h @@ -4,6 +4,7 @@ #pragma once +#include #include "common/hash.h" #include "video_core/regs_texturing.h" diff --git a/src/video_core/rasterizer_cache/slot_vector.h b/src/video_core/rasterizer_cache/slot_vector.h index 85189f466..c1dc47e4f 100644 --- a/src/video_core/rasterizer_cache/slot_vector.h +++ b/src/video_core/rasterizer_cache/slot_vector.h @@ -29,60 +29,6 @@ struct SlotId { template class SlotVector { public: - class Iterator { - friend SlotVector; - - public: - constexpr Iterator() = default; - - Iterator& operator++() noexcept { - const u64* const bitset = slot_vector->stored_bitset.data(); - const u32 size = static_cast(slot_vector->stored_bitset.size()) * 64; - if (id.index < size) { - do { - ++id.index; - } while (id.index < size && !IsValid(bitset)); - if (id.index == size) { - id.index = SlotId::INVALID_INDEX; - } - } - return *this; - } - - Iterator operator++(int) noexcept { - const Iterator copy{*this}; - ++*this; - return copy; - } - - bool operator==(const Iterator& other) const noexcept { - return id.index == other.id.index; - } - - bool operator!=(const Iterator& other) const noexcept { - return id.index != other.id.index; - } - - std::pair operator*() const noexcept { - return {id, std::addressof((*slot_vector)[id])}; - } - - T* operator->() const noexcept { - return std::addressof((*slot_vector)[id]); - } - - private: - Iterator(SlotVector* slot_vector_, SlotId id_) noexcept - : slot_vector{slot_vector_}, id{id_} {} - - bool IsValid(const u64* bitset) const noexcept { - return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0; - } - - SlotVector* slot_vector; - SlotId id; - }; - ~SlotVector() noexcept { size_t index = 0; for (u64 bits : stored_bitset) { @@ -121,21 +67,6 @@ public: ResetStorageBit(id.index); } - [[nodiscard]] Iterator begin() noexcept { - const auto it = std::find_if(stored_bitset.begin(), stored_bitset.end(), - [](u64 value) { return value != 0; }); - if (it == stored_bitset.end()) { - return end(); - } - const u32 word_index = static_cast(std::distance(it, stored_bitset.begin())); - const SlotId first_id{word_index * 64 + static_cast(std::countr_zero(*it))}; - return Iterator(this, first_id); - } - - [[nodiscard]] Iterator end() noexcept { - return Iterator(this, SlotId{SlotId::INVALID_INDEX}); - } - private: struct NonTrivialDummy { NonTrivialDummy() noexcept {} diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h index 15e92c531..61b30aa29 100644 --- a/src/video_core/rasterizer_cache/surface_base.h +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -13,6 +13,14 @@ using SurfaceRegions = boost::icl::interval_set MakeFillBuffer(PAddr copy_addr); public: - bool registered = false; - bool picked = false; - bool is_custom = false; + SurfaceFlagBits flags{}; const Material* material = nullptr; SurfaceRegions invalid_regions; u32 fill_size = 0; diff --git a/src/video_core/rasterizer_cache/surface_params.cpp b/src/video_core/rasterizer_cache/surface_params.cpp index 7caae3c9e..77aa3ea7f 100644 --- a/src/video_core/rasterizer_cache/surface_params.cpp +++ b/src/video_core/rasterizer_cache/surface_params.cpp @@ -11,7 +11,7 @@ bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { return std::tie(other_surface.addr, other_surface.width, other_surface.height, other_surface.stride, other_surface.pixel_format, other_surface.is_tiled) == std::tie(addr, width, height, stride, pixel_format, is_tiled) && - pixel_format != PixelFormat::Invalid && levels >= other_surface.levels; + pixel_format != PixelFormat::Invalid /*&& levels >= other_surface.levels*/; } bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index 5b9ce5639..9b6e76ff0 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -17,8 +17,13 @@ namespace VideoCore { using SurfaceInterval = boost::icl::right_open_interval; +using SurfaceId = SlotId; using SamplerId = SlotId; +/// Fake surface ID for null surfaces +constexpr SurfaceId NULL_SURFACE_ID{0}; +/// Fake surface ID for null cube surfaces +constexpr SurfaceId NULL_SURFACE_CUBE_ID{1}; /// Fake sampler ID for null samplers constexpr SamplerId NULL_SAMPLER_ID{0}; @@ -77,9 +82,9 @@ struct BufferTextureCopy { }; struct StagingData { - u32 size = 0; - std::span mapped{}; - u64 buffer_offset = 0; + u32 size; + std::span mapped; + u64 buffer_offset; }; struct TextureCubeConfig { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1b35b52db..c74c63bbe 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -25,7 +25,7 @@ MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(255, 128, MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(192, 128, 128)); MICROPROFILE_DEFINE(OpenGL_GS, "OpenGL", "Geometry Shader Setup", MP_RGB(128, 192, 128)); MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); -MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); +MICROPROFILE_DEFINE(OpenGL_Display, "OpenGL", "Display", MP_RGB(128, 128, 192)); using VideoCore::SurfaceType; @@ -241,14 +241,14 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, } } - PAddr data_addr = + const PAddr data_addr = base_address + loader.data_offset + (vs_input_index_min * loader.byte_count); - u32 vertex_num = vs_input_index_max - vs_input_index_min + 1; - u32 data_size = loader.byte_count * vertex_num; + const u32 vertex_num = vs_input_index_max - vs_input_index_min + 1; + const u32 data_size = loader.byte_count * vertex_num; - res_cache.FlushRegion(data_addr, data_size, nullptr); - std::memcpy(array_ptr, VideoCore::g_memory->GetPhysicalPointer(data_addr), data_size); + res_cache.FlushRegion(data_addr, data_size); + std::memcpy(array_ptr, memory.GetPhysicalPointer(data_addr), data_size); array_ptr += data_size; buffer_offset += data_size; @@ -277,8 +277,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset, bool RasterizerOpenGL::SetupVertexShader() { MICROPROFILE_SCOPE(OpenGL_VS); - return shader_program_manager->UseProgrammableVertexShader(Pica::g_state.regs, - Pica::g_state.vs); + return shader_program_manager->UseProgrammableVertexShader(regs, Pica::g_state.vs); } bool RasterizerOpenGL::SetupGeometryShader() { @@ -510,8 +509,8 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) { if (texture_index == 0) { switch (texture.config.type.Value()) { case TextureType::Shadow2D: { - auto surface = res_cache.GetTextureSurface(texture); - state.image_shadow_texture_px = surface->Handle(); + Surface& surface = res_cache.GetTextureSurface(texture); + state.image_shadow_texture_px = surface.Handle(); continue; } case TextureType::ShadowCube: { @@ -528,23 +527,14 @@ void RasterizerOpenGL::SyncTextureUnits(const Framebuffer& framebuffer) { } // Sync texture unit sampler - const Sampler& sampler = res_cache.GetSampler(texture.config); + Sampler& sampler = res_cache.GetSampler(texture.config); state.texture_units[texture_index].sampler = sampler.Handle(); // Bind the texture provided by the rasterizer cache - auto surface = res_cache.GetTextureSurface(texture); - if (!surface) { - // 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; - } else if (!IsFeedbackLoop(texture_index, framebuffer, *surface)) { - BindMaterial(texture_index, *surface); - state.texture_units[texture_index].texture_2d = surface->Handle(); + Surface& surface = res_cache.GetTextureSurface(texture); + if (!IsFeedbackLoop(texture_index, framebuffer, surface)) { + BindMaterial(texture_index, surface); + state.texture_units[texture_index].texture_2d = surface.Handle(); } } } @@ -561,8 +551,9 @@ void RasterizerOpenGL::BindShadowCube(const Pica::TexturingRegs::FullTextureConf const u32 binding = static_cast(face); info.physical_address = regs.texturing.GetCubePhysicalAddress(face); - auto surface = res_cache.GetTextureSurface(info); - state.image_shadow_texture[binding] = surface->Handle(); + VideoCore::SurfaceId surface_id = res_cache.GetTextureSurface(info); + Surface& surface = res_cache.GetSurface(surface_id); + state.image_shadow_texture[binding] = surface.Handle(); } } @@ -580,10 +571,10 @@ void RasterizerOpenGL::BindTextureCube(const Pica::TexturingRegs::FullTextureCon .format = texture.format, }; - auto surface = res_cache.GetTextureCube(config); + 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.texture_cube = surface.Handle(); state.texture_cube_unit.sampler = sampler.Handle(); state.texture_units[0].texture_2d = 0; } @@ -718,24 +709,20 @@ void RasterizerOpenGL::NotifyFixedFunctionPicaRegisterChanged(u32 id) { } void RasterizerOpenGL::FlushAll() { - MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushAll(); } void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { - MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, 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) { @@ -760,7 +747,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con if (framebuffer_addr == 0) { return false; } - MICROPROFILE_SCOPE(OpenGL_CacheManagement); + MICROPROFILE_SCOPE(OpenGL_Display); VideoCore::SurfaceParams src_params; src_params.addr = framebuffer_addr; @@ -771,27 +758,27 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con src_params.pixel_format = VideoCore::PixelFormatFromGPUPixelFormat(config.color_format); src_params.UpdateParams(); - 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.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; } void RasterizerOpenGL::SyncClipEnabled() { - state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0; + state.clip_distance[1] = regs.rasterizer.clip_enable != 0; } void RasterizerOpenGL::SyncCullMode() { @@ -819,7 +806,7 @@ void RasterizerOpenGL::SyncCullMode() { } void RasterizerOpenGL::SyncBlendEnabled() { - state.blend.enabled = (Pica::g_state.regs.framebuffer.output_merger.alphablend_enable == 1); + state.blend.enabled = (regs.framebuffer.output_merger.alphablend_enable == 1); } void RasterizerOpenGL::SyncBlendFuncs() { @@ -838,8 +825,7 @@ void RasterizerOpenGL::SyncBlendFuncs() { } void RasterizerOpenGL::SyncBlendColor() { - auto blend_color = - PicaToGL::ColorRGBA8(Pica::g_state.regs.framebuffer.output_merger.blend_const.raw); + auto blend_color = PicaToGL::ColorRGBA8(regs.framebuffer.output_merger.blend_const.raw); state.blend.color.red = blend_color[0]; state.blend.color.green = blend_color[1]; state.blend.color.blue = blend_color[2]; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index af3da8883..e31f2c01f 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -573,7 +573,6 @@ bool Surface::Swap(const VideoCore::Material* mat) { GetScaledWidth(), GetScaledHeight(), VideoCore::PixelFormatAsString(pixel_format), addr, width, height, VideoCore::CustomPixelFormatAsString(format)); - is_custom = true; custom_format = format; material = mat; @@ -615,13 +614,13 @@ HostTextureTag Surface::MakeTag() const noexcept { .res_scale = alloc.res_scale, .tuple = alloc.tuple, .type = texture_type, - .is_custom = is_custom, + .is_custom = True(flags & VideoCore::SurfaceFlagBits::Custom), .has_normal = HasNormalMap(), }; } -Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level, - Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs, +Framebuffer::Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level, + const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs, Common::Rectangle surfaces_rect) : VideoCore::FramebufferBase{regs, color, color_level, depth_stencil, depth_level, surfaces_rect} { diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index 13b2584f3..a1563f72d 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -201,8 +201,8 @@ private: class Framebuffer : public VideoCore::FramebufferBase { public: - explicit Framebuffer(TextureRuntime& runtime, Surface* const color, u32 color_level, - Surface* const depth_stencil, u32 depth_level, const Pica::Regs& regs, + explicit Framebuffer(TextureRuntime& runtime, const Surface* color, u32 color_level, + const Surface* depth_stencil, u32 depth_level, const Pica::Regs& regs, Common::Rectangle surfaces_rect); ~Framebuffer(); @@ -243,7 +243,7 @@ private: }; struct Traits { - using TextureRuntime = OpenGL::TextureRuntime; + using Runtime = OpenGL::TextureRuntime; using Sampler = OpenGL::Sampler; using Surface = OpenGL::Surface; using Framebuffer = OpenGL::Framebuffer;