mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	OpenGL Cache: Add the rest of the Cache methods
Fills in the rasterizer cache methods using the helper methods added in the previous commits.
This commit is contained in:
		
							parent
							
								
									81ea32d1e0
								
							
						
					
					
						commit
						e5adb6a26b
					
				
					 1 changed files with 306 additions and 186 deletions
				
			
		|  | @ -6,14 +6,20 @@ | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <iterator> | #include <iterator> | ||||||
|  | #include <memory> | ||||||
| #include <unordered_set> | #include <unordered_set> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <boost/optional.hpp> | ||||||
|  | #include <boost/range/iterator_range.hpp> | ||||||
| #include <glad/glad.h> | #include <glad/glad.h> | ||||||
|  | #include "common/alignment.h" | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
|  | #include "common/color.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "common/math_util.h" | #include "common/math_util.h" | ||||||
| #include "common/microprofile.h" | #include "common/microprofile.h" | ||||||
|  | #include "common/scope_exit.h" | ||||||
| #include "common/vector_math.h" | #include "common/vector_math.h" | ||||||
| #include "core/frontend/emu_window.h" | #include "core/frontend/emu_window.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
|  | @ -155,6 +161,10 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() { | ||||||
| 
 | 
 | ||||||
| RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { | RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { | ||||||
|     FlushAll(); |     FlushAll(); | ||||||
|  |     while (!surface_cache.empty()) | ||||||
|  |         UnregisterSurface(*surface_cache.begin()->second.begin()); | ||||||
|  |     transfer_framebuffers[0].Release(); | ||||||
|  |     transfer_framebuffers[1].Release(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <bool morton_to_gl, PixelFormat format> | template <bool morton_to_gl, PixelFormat format> | ||||||
|  | @ -567,19 +577,45 @@ SurfaceInterval SurfaceParams::GetCopyableInterval(const Surface& src_surface) c | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, | void RasterizerCacheOpenGL::CopySurface(const Surface& src_surface, const Surface& dst_surface, | ||||||
|                                             const MathUtil::Rectangle<int>& src_rect, |                                         SurfaceInterval copy_interval) { | ||||||
|                                             CachedSurface* dst_surface, |     SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); | ||||||
|                                             const MathUtil::Rectangle<int>& dst_rect) { |     ASSERT(subrect_params.GetInterval() == copy_interval); | ||||||
| 
 | 
 | ||||||
|     if (!CachedSurface::CheckFormatsBlittable(src_surface->pixel_format, |     ASSERT(src_surface != dst_surface); | ||||||
|                                               dst_surface->pixel_format)) { | 
 | ||||||
|         return false; |     // This is only called when CanCopy is true, no need to run checks here
 | ||||||
|  |     if (src_surface->type == SurfaceType::Fill) { | ||||||
|  |         // FillSurface needs a 4 bytes buffer
 | ||||||
|  |         const u32 fill_offset = | ||||||
|  |             (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size; | ||||||
|  |         std::array<u8, 4> fill_buffer; | ||||||
|  | 
 | ||||||
|  |         u32 fill_buff_pos = fill_offset; | ||||||
|  |         for (int i : {0, 1, 2, 3}) | ||||||
|  |             fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size]; | ||||||
|  | 
 | ||||||
|  |         FillSurface(dst_surface, &fill_buffer[0], dst_surface->GetScaledSubRect(subrect_params)); | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|  |     if (src_surface->CanSubRect(subrect_params)) { | ||||||
|  |         BlitTextures(src_surface->texture.handle, src_surface->GetScaledSubRect(subrect_params), | ||||||
|  |                      dst_surface->texture.handle, dst_surface->GetScaledSubRect(subrect_params), | ||||||
|  |                      src_surface->type); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     UNREACHABLE(); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, | bool RasterizerCacheOpenGL::BlitSurfaces(const Surface& src_surface, | ||||||
|                  CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect); |                                          const MathUtil::Rectangle<u32>& src_rect, | ||||||
|     return true; |                                          const Surface& dst_surface, | ||||||
|  |                                          const MathUtil::Rectangle<u32>& dst_rect) { | ||||||
|  |     if (!SurfaceParams::CheckFormatsBlittable(src_surface->pixel_format, dst_surface->pixel_format)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     return BlitTextures(src_surface->texture.handle, src_rect, dst_surface->texture.handle, | ||||||
|  |                         dst_rect, src_surface->type); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Allocate an uninitialized texture of appropriate size and format for the surface
 | // Allocate an uninitialized texture of appropriate size and format for the surface
 | ||||||
|  | @ -663,252 +699,336 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CachedSurface* RasterizerCacheOpenGL::GetSurfaceRect(const CachedSurface& params, | Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, | ||||||
|                                                      bool match_res_scale, bool load_if_create, |                                           bool load_if_create) { | ||||||
|                                                      MathUtil::Rectangle<int>& out_rect) { |     if (params.addr == 0 || params.height * params.width == 0) { | ||||||
|     if (params.addr == 0) { |  | ||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     u32 total_pixels = params.width * params.height; |     ASSERT(params.width == params.stride); // Use GetSurfaceSubRect instead
 | ||||||
|     u32 params_size = total_pixels * CachedSurface::GetFormatBpp(params.pixel_format) / 8; |  | ||||||
| 
 | 
 | ||||||
|     // Attempt to find encompassing surfaces
 |     // Check for an exact match in existing surfaces
 | ||||||
|     CachedSurface* best_subrect_surface = nullptr; |     Surface surface = | ||||||
|     float subrect_surface_goodness = -1.f; |         FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale); | ||||||
| 
 | 
 | ||||||
|     auto surface_interval = |     if (surface == nullptr) { | ||||||
|         boost::icl::interval<PAddr>::right_open(params.addr, params.addr + params_size); |         u16 target_res_scale = params.res_scale; | ||||||
|     auto cache_upper_bound = surface_cache.upper_bound(surface_interval); |         if (match_res_scale != ScaleMatch::Exact) { | ||||||
|     for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) { |             // This surface may have a subrect of another surface with a higher res_scale, find it
 | ||||||
|         for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { |             // to adjust our params
 | ||||||
|             CachedSurface* surface = it2->get(); |             SurfaceParams find_params = params; | ||||||
| 
 |             Surface expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>( | ||||||
|             // Check if the request is contained in the surface
 |                 surface_cache, find_params, match_res_scale); | ||||||
|             if (params.addr >= surface->addr && |             if (expandable != nullptr && expandable->res_scale > target_res_scale) { | ||||||
|                 params.addr + params_size - 1 <= surface->addr + surface->size - 1 && |                 target_res_scale = expandable->res_scale; | ||||||
|                 params.pixel_format == surface->pixel_format) { |             } | ||||||
|                 // Make sure optional param-matching criteria are fulfilled
 |             // Keep res_scale when reinterpreting d24s8 -> rgba8
 | ||||||
|                 bool tiling_match = (params.is_tiled == surface->is_tiled); |             if (params.pixel_format == PixelFormat::RGBA8) { | ||||||
|                 bool res_scale_match = (params.res_scale_width == surface->res_scale_width && |                 find_params.pixel_format = PixelFormat::D24S8; | ||||||
|                                         params.res_scale_height == surface->res_scale_height); |                 expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>( | ||||||
|                 if (!match_res_scale || res_scale_match) { |                     surface_cache, find_params, match_res_scale); | ||||||
|                     // Prioritize same-tiling and highest resolution surfaces
 |                 if (expandable != nullptr && expandable->res_scale > target_res_scale) { | ||||||
|                     float match_goodness = |                     target_res_scale = expandable->res_scale; | ||||||
|                         (float)tiling_match + surface->res_scale_width * surface->res_scale_height; |  | ||||||
|                     if (match_goodness > subrect_surface_goodness || surface->dirty) { |  | ||||||
|                         subrect_surface_goodness = match_goodness; |  | ||||||
|                         best_subrect_surface = surface; |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |         SurfaceParams new_params = params; | ||||||
|  |         new_params.res_scale = target_res_scale; | ||||||
|  |         surface = CreateSurface(new_params); | ||||||
|  |         RegisterSurface(surface); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Return the best subrect surface if found
 |     if (load_if_create) { | ||||||
|     if (best_subrect_surface != nullptr) { |         ValidateSurface(surface, params.addr, params.size); | ||||||
|         unsigned int bytes_per_pixel = |     } | ||||||
|             (CachedSurface::GetFormatBpp(best_subrect_surface->pixel_format) / 8); |  | ||||||
| 
 | 
 | ||||||
|         int x0, y0; |     return surface; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|         if (!params.is_tiled) { | SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params, | ||||||
|             u32 begin_pixel_index = (params.addr - best_subrect_surface->addr) / bytes_per_pixel; |                                                            ScaleMatch match_res_scale, | ||||||
|             x0 = begin_pixel_index % best_subrect_surface->width; |                                                            bool load_if_create) { | ||||||
|             y0 = begin_pixel_index / best_subrect_surface->width; |     if (params.addr == 0 || params.height * params.width == 0) { | ||||||
|  |         return {nullptr, {}}; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|             out_rect = MathUtil::Rectangle<int>(x0, y0, x0 + params.width, y0 + params.height); |     // Attempt to find encompassing surface
 | ||||||
|         } else { |     Surface surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, | ||||||
|             u32 bytes_per_tile = 8 * 8 * bytes_per_pixel; |                                                                            match_res_scale); | ||||||
|             u32 tiles_per_row = best_subrect_surface->width / 8; |  | ||||||
| 
 | 
 | ||||||
|             u32 begin_tile_index = (params.addr - best_subrect_surface->addr) / bytes_per_tile; |     // Check if FindMatch failed because of res scaling
 | ||||||
|             x0 = begin_tile_index % tiles_per_row * 8; |     // If that's the case create a new surface with
 | ||||||
|             y0 = begin_tile_index / tiles_per_row * 8; |     // the dimensions of the lower res_scale surface
 | ||||||
|  |     // to suggest it should not be used again
 | ||||||
|  |     if (surface == nullptr && match_res_scale != ScaleMatch::Ignore) { | ||||||
|  |         surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, | ||||||
|  |                                                                        ScaleMatch::Ignore); | ||||||
|  |         if (surface != nullptr) { | ||||||
|  |             ASSERT(surface->res_scale < params.res_scale); | ||||||
|  |             SurfaceParams new_params = *surface; | ||||||
|  |             new_params.res_scale = params.res_scale; | ||||||
| 
 | 
 | ||||||
|             // Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory.
 |             surface = CreateSurface(new_params); | ||||||
|             out_rect = |             RegisterSurface(surface); | ||||||
|                 MathUtil::Rectangle<int>(x0, best_subrect_surface->height - y0, x0 + params.width, |  | ||||||
|                                          best_subrect_surface->height - (y0 + params.height)); |  | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         out_rect.left = (int)(out_rect.left * best_subrect_surface->res_scale_width); |     // Check for a surface we can expand before creating a new one
 | ||||||
|         out_rect.right = (int)(out_rect.right * best_subrect_surface->res_scale_width); |     if (surface == nullptr) { | ||||||
|         out_rect.top = (int)(out_rect.top * best_subrect_surface->res_scale_height); |         surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, params, | ||||||
|         out_rect.bottom = (int)(out_rect.bottom * best_subrect_surface->res_scale_height); |                                                                       match_res_scale); | ||||||
|  |         if (surface != nullptr) { | ||||||
|  |             SurfaceParams new_params = *surface; | ||||||
|  |             new_params.addr = std::min(params.addr, surface->addr); | ||||||
|  |             new_params.end = std::max(params.end, surface->end); | ||||||
|  |             new_params.size = new_params.end - new_params.addr; | ||||||
|  |             new_params.height = new_params.size / params.BytesInPixels(params.stride); | ||||||
|  |             ASSERT(new_params.size % params.BytesInPixels(params.stride) == 0); | ||||||
| 
 | 
 | ||||||
|         return best_subrect_surface; |             Surface new_surface = CreateSurface(new_params); | ||||||
|  |             DuplicateSurface(surface, new_surface); | ||||||
|  | 
 | ||||||
|  |             // Delete the expanded surface, this can't be done safely yet
 | ||||||
|  |             // because it may still be in use
 | ||||||
|  |             remove_surfaces.emplace(surface); | ||||||
|  | 
 | ||||||
|  |             surface = new_surface; | ||||||
|  |             RegisterSurface(new_surface); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // No subrect found - create and return a new surface
 |     // No subrect found - create and return a new surface
 | ||||||
|     if (!params.is_tiled) { |     if (surface == nullptr) { | ||||||
|         out_rect = MathUtil::Rectangle<int>(0, 0, (int)(params.width * params.res_scale_width), |         SurfaceParams new_params = params; | ||||||
|                                             (int)(params.height * params.res_scale_height)); |         new_params.width = params.stride; // Can't have gaps in a surface
 | ||||||
|     } else { |         new_params.UpdateParams(); | ||||||
|         out_rect = MathUtil::Rectangle<int>(0, (int)(params.height * params.res_scale_height), |         // GetSurface will create the new surface and possibly adjust res_scale if necessary
 | ||||||
|                                             (int)(params.width * params.res_scale_width), 0); |         surface = GetSurface(new_params, match_res_scale, load_if_create); | ||||||
|  |     } else if (load_if_create) { | ||||||
|  |         ValidateSurface(surface, params.addr, params.size); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return GetSurface(params, match_res_scale, load_if_create); |     return {surface, surface->GetScaledSubRect(params)}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CachedSurface* RasterizerCacheOpenGL::GetTextureSurface( | Surface RasterizerCacheOpenGL::GetTextureSurface( | ||||||
|     const Pica::TexturingRegs::FullTextureConfig& config) { |     const Pica::TexturingRegs::FullTextureConfig& config) { | ||||||
| 
 |  | ||||||
|     Pica::Texture::TextureInfo info = |     Pica::Texture::TextureInfo info = | ||||||
|         Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); |         Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); | ||||||
| 
 | 
 | ||||||
|     CachedSurface params; |     SurfaceParams params; | ||||||
|     params.addr = info.physical_address; |     params.addr = info.physical_address; | ||||||
|     params.width = info.width; |     params.width = info.width; | ||||||
|     params.height = info.height; |     params.height = info.height; | ||||||
|     params.is_tiled = true; |     params.is_tiled = true; | ||||||
|     params.pixel_format = CachedSurface::PixelFormatFromTextureFormat(info.format); |     params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); | ||||||
|     return GetSurface(params, false, true); |     params.UpdateParams(); | ||||||
|  |     return GetSurface(params, ScaleMatch::Ignore, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // If the resolution
 |  | ||||||
| static u16 GetResolutionScaleFactor() { | static u16 GetResolutionScaleFactor() { | ||||||
|     return !Settings::values.resolution_factor |     return !Settings::values.resolution_factor | ||||||
|                ? VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio() |                ? VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio() | ||||||
|                : Settings::values.resolution_factor; |                : Settings::values.resolution_factor; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> | SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( | ||||||
| RasterizerCacheOpenGL::GetFramebufferSurfaces( |     bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport_rect) { | ||||||
|     const Pica::FramebufferRegs::FramebufferConfig& config) { |  | ||||||
| 
 |  | ||||||
|     const auto& regs = Pica::g_state.regs; |     const auto& regs = Pica::g_state.regs; | ||||||
|  |     const auto& config = regs.framebuffer.framebuffer; | ||||||
| 
 | 
 | ||||||
|     // update resolution_scale_factor and reset cache if changed
 |     // update resolution_scale_factor and reset cache if changed
 | ||||||
|     static u16 resolution_scale_factor = GetResolutionScaleFactor(); |     static u16 resolution_scale_factor = GetResolutionScaleFactor(); | ||||||
|     if (resolution_scale_factor != GetResolutionScaleFactor()) { |     if (resolution_scale_factor != GetResolutionScaleFactor()) { | ||||||
|         resolution_scale_factor = GetResolutionScaleFactor(); |         resolution_scale_factor = GetResolutionScaleFactor(); | ||||||
|         FlushAll(); |         FlushAll(); | ||||||
|         InvalidateRegion(0, 0xffffffff, nullptr); |         while (!surface_cache.empty()) | ||||||
|  |             UnregisterSurface(*surface_cache.begin()->second.begin()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Make sur that framebuffers don't overlap if both color and depth are being used
 |     MathUtil::Rectangle<u32> viewport_clamped{ | ||||||
|     u32 fb_area = config.GetWidth() * config.GetHeight(); |         static_cast<u32>( | ||||||
|     bool framebuffers_overlap = |             MathUtil::Clamp(viewport_rect.left, 0, static_cast<s32>(config.GetWidth()))), | ||||||
|         config.GetColorBufferPhysicalAddress() != 0 && |         static_cast<u32>( | ||||||
|         config.GetDepthBufferPhysicalAddress() != 0 && |             MathUtil::Clamp(viewport_rect.top, 0, static_cast<s32>(config.GetHeight()))), | ||||||
|         MathUtil::IntervalsIntersect( |         static_cast<u32>( | ||||||
|             config.GetColorBufferPhysicalAddress(), |             MathUtil::Clamp(viewport_rect.right, 0, static_cast<s32>(config.GetWidth()))), | ||||||
|             fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())), |         static_cast<u32>( | ||||||
|             config.GetDepthBufferPhysicalAddress(), |             MathUtil::Clamp(viewport_rect.bottom, 0, static_cast<s32>(config.GetHeight())))}; | ||||||
|             fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format)); |  | ||||||
|     bool using_color_fb = config.GetColorBufferPhysicalAddress() != 0; |  | ||||||
|     bool depth_write_enable = regs.framebuffer.output_merger.depth_write_enable && |  | ||||||
|                               regs.framebuffer.framebuffer.allow_depth_stencil_write; |  | ||||||
|     bool using_depth_fb = config.GetDepthBufferPhysicalAddress() != 0 && |  | ||||||
|                           (regs.framebuffer.output_merger.depth_test_enable || depth_write_enable || |  | ||||||
|                            !framebuffers_overlap); |  | ||||||
| 
 | 
 | ||||||
|     if (framebuffers_overlap && using_color_fb && using_depth_fb) { |     // get color and depth surfaces
 | ||||||
|  |     SurfaceParams color_params; | ||||||
|  |     color_params.is_tiled = true; | ||||||
|  |     color_params.res_scale = resolution_scale_factor; | ||||||
|  |     color_params.width = config.GetWidth(); | ||||||
|  |     color_params.height = config.GetHeight(); | ||||||
|  |     SurfaceParams depth_params = color_params; | ||||||
|  | 
 | ||||||
|  |     color_params.addr = config.GetColorBufferPhysicalAddress(); | ||||||
|  |     color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format); | ||||||
|  |     color_params.UpdateParams(); | ||||||
|  | 
 | ||||||
|  |     depth_params.addr = config.GetDepthBufferPhysicalAddress(); | ||||||
|  |     depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format); | ||||||
|  |     depth_params.UpdateParams(); | ||||||
|  | 
 | ||||||
|  |     auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped); | ||||||
|  |     auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped); | ||||||
|  | 
 | ||||||
|  |     // Make sur that framebuffers don't overlap if both color and depth are being used
 | ||||||
|  |     if (using_color_fb && using_depth_fb && | ||||||
|  |         boost::icl::length(color_vp_interval & depth_vp_interval)) { | ||||||
|         LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; " |         LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; " | ||||||
|                                     "overlapping framebuffers not supported!"); |                                     "overlapping framebuffers not supported!"); | ||||||
|         using_depth_fb = false; |         using_depth_fb = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // get color and depth surfaces
 |     MathUtil::Rectangle<u32> color_rect{}; | ||||||
|     CachedSurface color_params; |     Surface color_surface = nullptr; | ||||||
|     CachedSurface depth_params; |     if (using_color_fb) | ||||||
|     color_params.width = depth_params.width = config.GetWidth(); |         std::tie(color_surface, color_rect) = | ||||||
|     color_params.height = depth_params.height = config.GetHeight(); |             GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); | ||||||
|     color_params.is_tiled = depth_params.is_tiled = true; |  | ||||||
| 
 | 
 | ||||||
|     // Scale the resolution by the specified factor
 |     MathUtil::Rectangle<u32> depth_rect{}; | ||||||
|     color_params.res_scale_width = resolution_scale_factor; |     Surface depth_surface = nullptr; | ||||||
|     depth_params.res_scale_width = resolution_scale_factor; |     if (using_depth_fb) | ||||||
|     color_params.res_scale_height = resolution_scale_factor; |         std::tie(depth_surface, depth_rect) = | ||||||
|     depth_params.res_scale_height = resolution_scale_factor; |             GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); | ||||||
| 
 | 
 | ||||||
|     color_params.addr = config.GetColorBufferPhysicalAddress(); |     MathUtil::Rectangle<u32> fb_rect{}; | ||||||
|     color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format); |     if (color_surface != nullptr && depth_surface != nullptr) { | ||||||
| 
 |         fb_rect = color_rect; | ||||||
|     depth_params.addr = config.GetDepthBufferPhysicalAddress(); |         // Color and Depth surfaces must have the same dimensions and offsets
 | ||||||
|     depth_params.pixel_format = CachedSurface::PixelFormatFromDepthFormat(config.depth_format); |         if (color_rect.bottom != depth_rect.bottom || | ||||||
| 
 |             color_surface->height != depth_surface->height) { | ||||||
|     MathUtil::Rectangle<int> color_rect; |             color_surface = GetSurface(color_params, ScaleMatch::Exact, false); | ||||||
|     CachedSurface* color_surface = |             depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); | ||||||
|         using_color_fb ? GetSurfaceRect(color_params, true, true, color_rect) : nullptr; |             fb_rect = color_surface->GetScaledRect(); | ||||||
| 
 |  | ||||||
|     MathUtil::Rectangle<int> depth_rect; |  | ||||||
|     CachedSurface* depth_surface = |  | ||||||
|         using_depth_fb ? GetSurfaceRect(depth_params, true, true, depth_rect) : nullptr; |  | ||||||
| 
 |  | ||||||
|     // Sanity check to make sure found surfaces aren't the same
 |  | ||||||
|     if (using_depth_fb && using_color_fb && color_surface == depth_surface) { |  | ||||||
|         LOG_CRITICAL( |  | ||||||
|             Render_OpenGL, |  | ||||||
|             "Color and depth framebuffer surfaces overlap; overlapping surfaces not supported!"); |  | ||||||
|         using_depth_fb = false; |  | ||||||
|         depth_surface = nullptr; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     MathUtil::Rectangle<int> rect; |  | ||||||
| 
 |  | ||||||
|     if (color_surface != nullptr && depth_surface != nullptr && |  | ||||||
|         (depth_rect.left != color_rect.left || depth_rect.top != color_rect.top)) { |  | ||||||
|         // Can't specify separate color and depth viewport offsets in OpenGL, so re-zero both if
 |  | ||||||
|         // they don't match
 |  | ||||||
|         if (color_rect.left != 0 || color_rect.top != 0) { |  | ||||||
|             color_surface = GetSurface(color_params, true, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (depth_rect.left != 0 || depth_rect.top != 0) { |  | ||||||
|             depth_surface = GetSurface(depth_params, true, true); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (!color_surface->is_tiled) { |  | ||||||
|             rect = MathUtil::Rectangle<int>( |  | ||||||
|                 0, 0, (int)(color_params.width * color_params.res_scale_width), |  | ||||||
|                 (int)(color_params.height * color_params.res_scale_height)); |  | ||||||
|         } else { |  | ||||||
|             rect = MathUtil::Rectangle<int>( |  | ||||||
|                 0, (int)(color_params.height * color_params.res_scale_height), |  | ||||||
|                 (int)(color_params.width * color_params.res_scale_width), 0); |  | ||||||
|         } |         } | ||||||
|     } else if (color_surface != nullptr) { |     } else if (color_surface != nullptr) { | ||||||
|         rect = color_rect; |         fb_rect = color_rect; | ||||||
|     } else if (depth_surface != nullptr) { |     } else if (depth_surface != nullptr) { | ||||||
|         rect = depth_rect; |         fb_rect = depth_rect; | ||||||
|     } else { |     } | ||||||
|         rect = MathUtil::Rectangle<int>(0, 0, 0, 0); |     ASSERT(!fb_rect.left && fb_rect.right == config.GetWidth() * resolution_scale_factor); | ||||||
|  | 
 | ||||||
|  |     if (color_surface != nullptr) { | ||||||
|  |         ValidateSurface(color_surface, boost::icl::first(color_vp_interval), | ||||||
|  |                         boost::icl::length(color_vp_interval)); | ||||||
|  |     } | ||||||
|  |     if (depth_surface != nullptr) { | ||||||
|  |         ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), | ||||||
|  |                         boost::icl::length(depth_vp_interval)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return std::make_tuple(color_surface, depth_surface, rect); |     return {color_surface, depth_surface, fb_rect}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CachedSurface* RasterizerCacheOpenGL::TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config) { | SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) { | ||||||
|     auto surface_interval = |     MathUtil::Rectangle<u32> rect{}; | ||||||
|         boost::icl::interval<PAddr>::right_open(config.GetStartAddress(), config.GetEndAddress()); |  | ||||||
|     auto range = surface_cache.equal_range(surface_interval); |  | ||||||
|     for (auto it = range.first; it != range.second; ++it) { |  | ||||||
|         for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { |  | ||||||
|             int bits_per_value = 0; |  | ||||||
|             if (config.fill_24bit) { |  | ||||||
|                 bits_per_value = 24; |  | ||||||
|             } else if (config.fill_32bit) { |  | ||||||
|                 bits_per_value = 32; |  | ||||||
|             } else { |  | ||||||
|                 bits_per_value = 16; |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             CachedSurface* surface = it2->get(); |     Surface match_surface = FindMatch<MatchFlags::TexCopy | MatchFlags::Invalid>( | ||||||
|  |         surface_cache, params, ScaleMatch::Ignore); | ||||||
| 
 | 
 | ||||||
|             if (surface->addr == config.GetStartAddress() && |     if (match_surface != nullptr) { | ||||||
|                 CachedSurface::GetFormatBpp(surface->pixel_format) == bits_per_value && |         ValidateSurface(match_surface, params.addr, params.size); | ||||||
|                 (surface->width * surface->height * | 
 | ||||||
|                  CachedSurface::GetFormatBpp(surface->pixel_format) / 8) == |         SurfaceParams match_subrect = params; | ||||||
|                     (config.GetEndAddress() - config.GetStartAddress())) { |         match_subrect.width = match_surface->PixelsInBytes(params.width); | ||||||
|                 return surface; |         match_subrect.stride = match_surface->PixelsInBytes(params.stride); | ||||||
|             } | 
 | ||||||
|  |         if (match_surface->is_tiled) { | ||||||
|  |             match_subrect.width /= 8; | ||||||
|  |             match_subrect.stride /= 8; | ||||||
|  |             match_subrect.height *= 8; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         rect = match_surface->GetScaledSubRect(match_subrect); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return nullptr; |     return {match_surface, rect}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) { | ||||||
|  |     Surface new_surface = std::make_shared<CachedSurface>(); | ||||||
|  | 
 | ||||||
|  |     new_surface->addr = config.GetStartAddress(); | ||||||
|  |     new_surface->end = config.GetEndAddress(); | ||||||
|  |     new_surface->size = new_surface->end - new_surface->addr; | ||||||
|  |     new_surface->type = SurfaceType::Fill; | ||||||
|  |     new_surface->res_scale = std::numeric_limits<u16>::max(); | ||||||
|  |     std::memcpy(&new_surface->fill_data[0], &config.value_32bit, 4); | ||||||
|  |     if (config.fill_32bit) { | ||||||
|  |         new_surface->fill_size = 4; | ||||||
|  |     } else if (config.fill_24bit) { | ||||||
|  |         new_surface->fill_size = 3; | ||||||
|  |     } else { | ||||||
|  |         new_surface->fill_size = 2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RegisterSurface(new_surface); | ||||||
|  |     return new_surface; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface, | ||||||
|  |                                              const Surface& dest_surface) { | ||||||
|  |     ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end); | ||||||
|  | 
 | ||||||
|  |     BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface, | ||||||
|  |                  dest_surface->GetScaledSubRect(*src_surface)); | ||||||
|  | 
 | ||||||
|  |     dest_surface->invalid_regions -= src_surface->GetInterval(); | ||||||
|  |     dest_surface->invalid_regions += src_surface->invalid_regions; | ||||||
|  | 
 | ||||||
|  |     SurfaceRegions regions; | ||||||
|  |     for (auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) { | ||||||
|  |         if (pair.second == src_surface) { | ||||||
|  |             regions += pair.first; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     for (auto& interval : regions) { | ||||||
|  |         dirty_regions.set({interval, dest_surface}); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u32 size) { | ||||||
|  |     if (size == 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     const auto validate_interval = SurfaceInterval(addr, addr + size); | ||||||
|  | 
 | ||||||
|  |     if (surface->type == SurfaceType::Fill) { | ||||||
|  |         // Sanity check, fill surfaces will always be valid when used
 | ||||||
|  |         ASSERT(surface->IsRegionValid(validate_interval)); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto validate_regions = surface->invalid_regions.find(validate_interval); | ||||||
|  | 
 | ||||||
|  |     for (;;) { | ||||||
|  |         const auto it = validate_regions.begin(); | ||||||
|  |         if (it == surface->invalid_regions.end()) | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         const auto interval = *it & validate_interval; | ||||||
|  |         // Look for a valid surface to copy from
 | ||||||
|  |         SurfaceParams params = surface->FromInterval(interval); | ||||||
|  | 
 | ||||||
|  |         Surface copy_surface = | ||||||
|  |             FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval); | ||||||
|  |         if (copy_surface != nullptr) { | ||||||
|  |             SurfaceInterval copy_interval = params.GetCopyableInterval(copy_surface); | ||||||
|  |             CopySurface(copy_surface, surface, copy_interval); | ||||||
|  |             validate_regions.erase(interval); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Load data from 3DS memory
 | ||||||
|  |         FlushRegion(params.addr, params.size); | ||||||
|  |         surface->LoadGLBuffer(params.addr, params.end); | ||||||
|  |         surface->UploadGLTexture(surface->GetSubRect(params)); | ||||||
|  |         validate_regions.erase(interval) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue