res scale fixed + cleanup

This commit is contained in:
Phantom 2017-10-09 04:24:15 +02:00
parent 926abc6ddb
commit 8969245e2e
3 changed files with 152 additions and 80 deletions

View File

@ -240,7 +240,7 @@ void RasterizerOpenGL::DrawTriangles() {
const bool using_color_fb = regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 && const bool using_color_fb = regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() != 0 &&
write_color_fb; write_color_fb;
const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 && const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 &&
(state.depth.test_enabled || write_depth_fb); (write_depth_fb || state.depth.test_enabled || (has_stencil && state.stencil.test_enabled));
// Sync and bind the framebuffer surfaces // Sync and bind the framebuffer surfaces
Surface color_surface; Surface color_surface;

View File

@ -314,8 +314,8 @@ static bool FillSurface(const Surface& surface, const u8* fill_data) {
MathUtil::Rectangle<u32> CachedSurface::GetSubRect(const SurfaceParams& sub_surface) const { MathUtil::Rectangle<u32> CachedSurface::GetSubRect(const SurfaceParams& sub_surface) const {
const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr); const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr);
const int x0 = begin_pixel_index % width; const int x0 = begin_pixel_index % stride;
const int y0 = begin_pixel_index / width; const int y0 = begin_pixel_index / stride;
if (is_tiled) if (is_tiled)
return MathUtil::Rectangle<u32>(x0, height - y0 - sub_surface.height, x0 + sub_surface.width, height - y0); // Bottom to top return MathUtil::Rectangle<u32>(x0, height - y0 - sub_surface.height, x0 + sub_surface.width, height - y0); // Bottom to top
@ -361,6 +361,9 @@ bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const {
if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) && if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) &&
dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range
if (fill_size != dest_surface.bytes_per_pixel) { if (fill_size != dest_surface.bytes_per_pixel) {
if (dest_surface.is_tiled && GetFormatBpp(dest_surface.pixel_format) * 8 % fill_size != 0)
return false;
// Check if bits repeat for our fill_size // Check if bits repeat for our fill_size
const u32 dest_bytes_per_pixel = std::max(dest_surface.bytes_per_pixel, 1u); // Take care of 4bpp formats const u32 dest_bytes_per_pixel = std::max(dest_surface.bytes_per_pixel, 1u); // Take care of 4bpp formats
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel); std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
@ -383,17 +386,34 @@ bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const {
return false; return false;
} }
bool CachedSurface::CanExpand(const SurfaceParams& expanded_surface) const {
if (pixel_format != expanded_surface.pixel_format ||
is_tiled != expanded_surface.is_tiled ||
addr > expanded_surface.end || expanded_surface.addr > end ||
stride != expanded_surface.stride)
return false;
const u32 begin_pixel_index =
PixelsInBytes(std::max(expanded_surface.addr, addr) -
std::min(expanded_surface.addr, addr));
const int x0 = begin_pixel_index % stride;
const int y0 = begin_pixel_index / stride;
return x0 == 0 && (!is_tiled || y0 % 8 == 0);
}
bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const { bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const {
// TODO: Accept "Fill" surfaces
if (pixel_format == PixelFormat::Invalid || if (pixel_format == PixelFormat::Invalid ||
addr > texcopy_params.addr || end < texcopy_params.end || addr > texcopy_params.addr || end < texcopy_params.end ||
((texcopy_params.addr - addr) * 8) % SurfaceParams::GetFormatBpp(pixel_format) != 0 || ((texcopy_params.addr - addr) * 8) % GetFormatBpp(pixel_format) != 0 ||
(texcopy_params.width * 8) % SurfaceParams::GetFormatBpp(pixel_format) != 0 || (texcopy_params.width * 8) % GetFormatBpp(pixel_format) != 0 ||
(texcopy_params.stride * 8) % SurfaceParams::GetFormatBpp(pixel_format) != 0) (texcopy_params.stride * 8) % GetFormatBpp(pixel_format) != 0)
return false; return false;
const u32 begin_pixel_index = PixelsInBytes(texcopy_params.addr - addr); const u32 begin_pixel_index = PixelsInBytes(texcopy_params.addr - addr);
const int x0 = begin_pixel_index % width; const int x0 = begin_pixel_index % stride;
const int y0 = begin_pixel_index / width; const int y0 = begin_pixel_index / stride;
if (!is_tiled) if (!is_tiled)
return (PixelsInBytes(texcopy_params.stride) == stride && return (PixelsInBytes(texcopy_params.stride) == stride &&
@ -619,7 +639,8 @@ enum MatchFlags {
Exact = 1 << 1, // Surfaces perfectly match Exact = 1 << 1, // Surfaces perfectly match
SubRect = 1 << 2, // Surface encompasses params SubRect = 1 << 2, // Surface encompasses params
Copy = 1 << 3, // Surface we can copy from Copy = 1 << 3, // Surface we can copy from
TexCopy = 1 << 4 // Surface that will match a display transfer "texture copy" parameters Expand = 1 << 4, // Surface that can expand params
TexCopy = 1 << 5 // Surface that will match a display transfer "texture copy" parameters
}; };
constexpr MatchFlags operator | (MatchFlags lhs, MatchFlags rhs) { constexpr MatchFlags operator | (MatchFlags lhs, MatchFlags rhs) {
@ -632,6 +653,7 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
Surface match_surface = nullptr; Surface match_surface = nullptr;
bool match_valid = false; bool match_valid = false;
u32 match_scale = 0; u32 match_scale = 0;
u32 match_size = 0;
for (auto& pair : RangeFromInterval(surface_cache, params.GetInterval())) { for (auto& pair : RangeFromInterval(surface_cache, params.GetInterval())) {
for (auto& surface : pair.second) { for (auto& surface : pair.second) {
@ -643,28 +665,45 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
if (!(find_flags & MatchFlags::Invalid) && !is_valid) if (!(find_flags & MatchFlags::Invalid) && !is_valid)
continue; continue;
const auto IsMatch_Helper = [&](MatchFlags check_type, auto match_fn) { auto IsMatch_Helper = [&](MatchFlags check_type, auto match_fn) {
if (!(find_flags & check_type)) if (!(find_flags & check_type) || !match_fn())
return false; return;
if (!match_fn()) if (!res_scale_matched &&
return false; match_scale_type != ScaleMatch::Ignore &&
surface->type != SurfaceType::Fill)
return;
if (match_scale_type == ScaleMatch::Ignore || res_scale_matched || surface->type == SurfaceType::Fill) { // Found a match // Found a match, update only if this is better than the previous one
if (is_valid && !match_valid) { auto UpdateMatch = [&] {
match_scale = 0; match_surface = surface;
match_valid = true; match_valid = is_valid;
} match_scale = surface->res_scale;
if (surface->res_scale > match_scale) { match_size = surface->size;
match_scale = surface->res_scale; };
match_surface = surface;
} if (surface->res_scale > match_scale) {
UpdateMatch();
return;
} else if (surface->res_scale < match_scale) {
return;
}
if (is_valid && !match_valid) {
UpdateMatch();
return;
} else if (is_valid != match_valid) {
return;
}
if (surface->size > match_size) {
UpdateMatch();
} }
return false;
}; };
IsMatch_Helper(MatchFlags::Exact, [&] { return surface->ExactMatch(params); }); IsMatch_Helper(MatchFlags::Exact, [&] { return surface->ExactMatch(params); });
IsMatch_Helper(MatchFlags::SubRect, [&] { return surface->CanSubRect(params); }); IsMatch_Helper(MatchFlags::SubRect, [&] { return surface->CanSubRect(params); });
IsMatch_Helper(MatchFlags::Copy, [&] { return surface->CanCopy(params); }); IsMatch_Helper(MatchFlags::Copy, [&] { return surface->CanCopy(params); });
IsMatch_Helper(MatchFlags::Expand, [&] { return surface->CanExpand(params); });
IsMatch_Helper(MatchFlags::TexCopy, [&] { return surface->CanTexCopy(params); }); IsMatch_Helper(MatchFlags::TexCopy, [&] { return surface->CanTexCopy(params); });
} }
} }
@ -702,72 +741,105 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
return nullptr; return nullptr;
} }
// Check for an exact or subrect match in existing surfaces
Surface surface_match = FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
if (surface_match != nullptr) {
if (load_if_create) {
ValidateSurface(surface_match, params.addr, params.size);
}
return surface_match;
}
ASSERT(params.width == params.stride); // Use GetSurfaceSubRect instead ASSERT(params.width == params.stride); // Use GetSurfaceSubRect instead
Surface new_surface = CreateSurface(params); // Check for an exact match in existing surfaces
if (load_if_create) Surface surface = FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
ValidateSurface(new_surface, params.addr, params.size);
RegisterSurface(new_surface); Surface expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
u16 target_res_scale = surface == nullptr ? params.res_scale : surface->res_scale;
if (match_res_scale != ScaleMatch::Exact &&
expandable != nullptr &&
expandable->res_scale > params.res_scale) {
target_res_scale = expandable->res_scale;
}
return new_surface; if (surface == nullptr || target_res_scale != surface->res_scale) {
SurfaceParams new_params = params;
new_params.res_scale = target_res_scale;
surface = CreateSurface(new_params);
RegisterSurface(surface);
}
if (load_if_create) {
ValidateSurface(surface, params.addr, params.size);
}
return surface;
} }
SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params, SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params,
ScaleMatch match_res_scale, ScaleMatch match_res_scale,
bool load_if_create) { bool load_if_create) {
MathUtil::Rectangle<u32> out_rect{}; Surface surface = nullptr;
MathUtil::Rectangle<u32> rect{};
if (params.addr == 0 || params.height * params.width == 0) { if (params.addr == 0 || params.height * params.width == 0) {
return std::make_tuple(nullptr, out_rect); return { surface, rect };
} }
// Attempt to find encompassing surface // Attempt to find encompassing surface
Surface subrect_match = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, match_res_scale); surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
// Return the best subrect surface if found // Check if FindMatch failed because of res scaling
if (subrect_match != nullptr) { // If that's the case create a new surface with
if (load_if_create) { // the dimensions of the lower res_scale surface
ValidateSurface(subrect_match, params.addr, params.size); // 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;
surface = CreateSurface(new_params);
RegisterSurface(surface);
} }
}
out_rect = subrect_match->GetScaledSubRect(params); // Check for a surface we can expand before creating a new one
// Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory. if (surface == nullptr) {
if (params.is_tiled) surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
std::swap(out_rect.top, out_rect.bottom); 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 / (SurfaceParams::GetFormatBpp(params.pixel_format) * params.stride / 8);
return std::make_tuple(subrect_match, out_rect); Surface new_surface = CreateSurface(new_params);
RegisterSurface(new_surface);
// TODO: Delete the expanded surface, this can't be done safely yet
// because it may still be in use
BlitSurfaces(surface, surface->GetScaledRect(), new_surface, new_surface->GetScaledSubRect(*surface));
new_surface->invalid_regions -= surface->GetInterval();
new_surface->invalid_regions += surface->invalid_regions;
surface = new_surface;
}
} }
// No subrect found - create and return a new surface // No subrect found - create and return a new surface
SurfaceParams new_params = params; if (surface == nullptr) {
new_params.width = params.stride; // Can't have gaps in a surface SurfaceParams new_params = params;
new_params.UpdateParams(); new_params.width = params.stride; // Can't have gaps in a surface
new_params.UpdateParams();
out_rect = new_params.GetScaledRect(); surface = CreateSurface(new_params);
if (new_params.is_tiled) RegisterSurface(surface);
std::swap(out_rect.top, out_rect.bottom); }
// If stride was bigger than width we need to adjust our output rect if (load_if_create) {
out_rect.right = static_cast<int>(params.width * new_params.res_scale); ValidateSurface(surface, params.addr, params.size);
}
Surface new_surface = CreateSurface(new_params); rect = surface->GetScaledSubRect(params);
if (load_if_create) // Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory.
ValidateSurface(new_surface, new_params.addr, new_params.size); if (surface->is_tiled)
std::swap(rect.top, rect.bottom);
RegisterSurface(new_surface); return { surface, rect };
return std::make_tuple(new_surface, out_rect);
} }
Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config) { Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config) {
@ -796,15 +868,14 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin
// Make sur that framebuffers don't overlap if both color and depth are being used // Make sur that framebuffers don't overlap if both color and depth are being used
u32 fb_area = config.GetWidth() * config.GetHeight(); u32 fb_area = config.GetWidth() * config.GetHeight();
bool framebuffers_overlap = config.GetColorBufferPhysicalAddress() != 0 && bool framebuffers_overlap = using_color_fb && using_depth_fb &&
config.GetDepthBufferPhysicalAddress() != 0 &&
MathUtil::IntervalsIntersect( MathUtil::IntervalsIntersect(
config.GetColorBufferPhysicalAddress(), config.GetColorBufferPhysicalAddress(),
fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())), fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())),
config.GetDepthBufferPhysicalAddress(), config.GetDepthBufferPhysicalAddress(),
fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format)); fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format));
if (framebuffers_overlap && using_color_fb && using_depth_fb) { if (framebuffers_overlap) {
LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!"); LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!");
using_depth_fb = false; using_depth_fb = false;
} }
@ -856,7 +927,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin
std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, true); std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, true);
} }
return std::make_tuple(color_surface, depth_surface, rect); return { color_surface, depth_surface, rect };
} }
Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) { Surface RasterizerCacheOpenGL::GetFillSurface(const GPU::Regs::MemoryFillConfig& config) {
@ -901,7 +972,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams&
std::swap(rect.top, rect.bottom); std::swap(rect.top, rect.bottom);
} }
return std::make_tuple(match_surface, rect); return { match_surface, rect };
} }
void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u32 size) { void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, u32 size) {
@ -1015,6 +1086,8 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface
if (size == 0) if (size == 0)
return; return;
SurfaceSet remove_surfaces;
const auto invalid_interval = SurfaceInterval::right_open(addr, addr + size); const auto invalid_interval = SurfaceInterval::right_open(addr, addr + size);
if (region_owner != nullptr) { if (region_owner != nullptr) {
@ -1056,13 +1129,12 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface
} }
if (region_owner != nullptr) if (region_owner != nullptr)
dirty_regions.set(std::make_pair(invalid_interval, region_owner)); dirty_regions.set({ invalid_interval, region_owner });
else else
dirty_regions.erase(invalid_interval); dirty_regions.erase(invalid_interval);
for (auto& remove_surface : remove_surfaces) for (auto& remove_surface : remove_surfaces)
UnregisterSurface(remove_surface); UnregisterSurface(remove_surface);
remove_surfaces.clear();
} }
Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
@ -1090,13 +1162,13 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
} }
void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
surface_cache.add(std::make_pair(surface->GetInterval(), SurfaceSet({ surface }))); surface_cache.add({ surface->GetInterval(), SurfaceSet{ surface } });
UpdatePagesCachedCount(surface->addr, surface->size, 1); UpdatePagesCachedCount(surface->addr, surface->size, 1);
} }
void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
UpdatePagesCachedCount(surface->addr, surface->size, -1); UpdatePagesCachedCount(surface->addr, surface->size, -1);
surface_cache.subtract(std::make_pair(surface->GetInterval(), SurfaceSet({ surface }))); surface_cache.subtract({ surface->GetInterval(), SurfaceSet{ surface } });
} }
void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int delta) { void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int delta) {
@ -1107,7 +1179,7 @@ void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int del
// Interval maps will erase segments if count reaches 0, so if delta is negative we have to subtract after iterating // Interval maps will erase segments if count reaches 0, so if delta is negative we have to subtract after iterating
const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end); const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end);
if (delta > 0) if (delta > 0)
cached_pages.add(std::make_pair(pages_interval, delta)); cached_pages.add({ pages_interval, delta });
for (auto& pair : RangeFromInterval(cached_pages, pages_interval)) { for (auto& pair : RangeFromInterval(cached_pages, pages_interval)) {
const auto interval = pair.first & pages_interval; const auto interval = pair.first & pages_interval;
@ -1126,5 +1198,5 @@ void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int del
} }
if (delta < 0) if (delta < 0)
cached_pages.add(std::make_pair(pages_interval, delta)); cached_pages.add({ pages_interval, delta });
} }

View File

@ -89,7 +89,7 @@ struct SurfaceParams {
}; };
static unsigned int GetFormatBpp(SurfaceParams::PixelFormat format) { static unsigned int GetFormatBpp(SurfaceParams::PixelFormat format) {
static const std::array<unsigned int, 18> bpp_table = { static constexpr std::array<unsigned int, 18> bpp_table = {
32, // RGBA8 32, // RGBA8
24, // RGB8 24, // RGB8
16, // RGB5A1 16, // RGB5A1
@ -110,8 +110,8 @@ struct SurfaceParams {
32, // D24S8 32, // D24S8
}; };
ASSERT((unsigned int)format < ARRAY_SIZE(bpp_table)); ASSERT(static_cast<size_t>(format) < bpp_table.size());
return bpp_table[(unsigned int)format]; return bpp_table[static_cast<size_t>(format)];
} }
static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
@ -236,6 +236,7 @@ struct CachedSurface : SurfaceParams {
bool ExactMatch(const SurfaceParams& other_surface) const; bool ExactMatch(const SurfaceParams& other_surface) const;
bool CanSubRect(const SurfaceParams& sub_surface) const; bool CanSubRect(const SurfaceParams& sub_surface) const;
bool CanCopy(const SurfaceParams& dest_surface) const; bool CanCopy(const SurfaceParams& dest_surface) const;
bool CanExpand(const SurfaceParams& expanded_surface) const;
bool CanTexCopy(const SurfaceParams& texcopy_params) const; bool CanTexCopy(const SurfaceParams& texcopy_params) const;
MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const; MathUtil::Rectangle<u32> GetSubRect(const SurfaceParams& sub_surface) const;
@ -330,5 +331,4 @@ private:
SurfaceCache surface_cache; SurfaceCache surface_cache;
SurfaceMap dirty_regions; SurfaceMap dirty_regions;
PageMap cached_pages; PageMap cached_pages;
SurfaceSet remove_surfaces;
}; };