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
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_if_create) {
|
||||||
|
ValidateSurface(surface, params.addr, params.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& params,
|
||||||
|
ScaleMatch match_res_scale,
|
||||||
|
bool load_if_create) {
|
||||||
|
if (params.addr == 0 || params.height * params.width == 0) {
|
||||||
|
return {nullptr, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to find encompassing surface
|
||||||
|
Surface surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(surface_cache, 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 == 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the best subrect surface if found
|
// Check for a surface we can expand before creating a new one
|
||||||
if (best_subrect_surface != nullptr) {
|
if (surface == nullptr) {
|
||||||
unsigned int bytes_per_pixel =
|
surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, params,
|
||||||
(CachedSurface::GetFormatBpp(best_subrect_surface->pixel_format) / 8);
|
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);
|
||||||
|
|
||||||
int x0, y0;
|
Surface new_surface = CreateSurface(new_params);
|
||||||
|
DuplicateSurface(surface, new_surface);
|
||||||
|
|
||||||
if (!params.is_tiled) {
|
// Delete the expanded surface, this can't be done safely yet
|
||||||
u32 begin_pixel_index = (params.addr - best_subrect_surface->addr) / bytes_per_pixel;
|
// because it may still be in use
|
||||||
x0 = begin_pixel_index % best_subrect_surface->width;
|
remove_surfaces.emplace(surface);
|
||||||
y0 = begin_pixel_index / best_subrect_surface->width;
|
|
||||||
|
|
||||||
out_rect = MathUtil::Rectangle<int>(x0, y0, x0 + params.width, y0 + params.height);
|
surface = new_surface;
|
||||||
} else {
|
RegisterSurface(new_surface);
|
||||||
u32 bytes_per_tile = 8 * 8 * bytes_per_pixel;
|
|
||||||
u32 tiles_per_row = best_subrect_surface->width / 8;
|
|
||||||
|
|
||||||
u32 begin_tile_index = (params.addr - best_subrect_surface->addr) / bytes_per_tile;
|
|
||||||
x0 = begin_tile_index % tiles_per_row * 8;
|
|
||||||
y0 = begin_tile_index / tiles_per_row * 8;
|
|
||||||
|
|
||||||
// Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory.
|
|
||||||
out_rect =
|
|
||||||
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);
|
|
||||||
out_rect.right = (int)(out_rect.right * best_subrect_surface->res_scale_width);
|
|
||||||
out_rect.top = (int)(out_rect.top * best_subrect_surface->res_scale_height);
|
|
||||||
out_rect.bottom = (int)(out_rect.bottom * best_subrect_surface->res_scale_height);
|
|
||||||
|
|
||||||
return best_subrect_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;
|
||||||
|
}
|
||||||
|
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 {color_surface, depth_surface, fb_rect};
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceRect_Tuple RasterizerCacheOpenGL::GetTexCopySurface(const SurfaceParams& params) {
|
||||||
|
MathUtil::Rectangle<u32> rect{};
|
||||||
|
|
||||||
|
Surface match_surface = FindMatch<MatchFlags::TexCopy | MatchFlags::Invalid>(
|
||||||
|
surface_cache, params, ScaleMatch::Ignore);
|
||||||
|
|
||||||
|
if (match_surface != nullptr) {
|
||||||
|
ValidateSurface(match_surface, params.addr, params.size);
|
||||||
|
|
||||||
|
SurfaceParams match_subrect = params;
|
||||||
|
match_subrect.width = match_surface->PixelsInBytes(params.width);
|
||||||
|
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 {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 {
|
} else {
|
||||||
rect = MathUtil::Rectangle<int>(0, 0, 0, 0);
|
new_surface->fill_size = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(color_surface, depth_surface, rect);
|
RegisterSurface(new_surface);
|
||||||
|
return new_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedSurface* RasterizerCacheOpenGL::TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config) {
|
void RasterizerCacheOpenGL::DuplicateSurface(const Surface& src_surface,
|
||||||
auto surface_interval =
|
const Surface& dest_surface) {
|
||||||
boost::icl::interval<PAddr>::right_open(config.GetStartAddress(), config.GetEndAddress());
|
ASSERT(dest_surface->addr <= src_surface->addr && dest_surface->end >= src_surface->end);
|
||||||
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();
|
BlitSurfaces(src_surface, src_surface->GetScaledRect(), dest_surface,
|
||||||
|
dest_surface->GetScaledSubRect(*src_surface));
|
||||||
|
|
||||||
if (surface->addr == config.GetStartAddress() &&
|
dest_surface->invalid_regions -= src_surface->GetInterval();
|
||||||
CachedSurface::GetFormatBpp(surface->pixel_format) == bits_per_value &&
|
dest_surface->invalid_regions += src_surface->invalid_regions;
|
||||||
(surface->width * surface->height *
|
|
||||||
CachedSurface::GetFormatBpp(surface->pixel_format) / 8) ==
|
SurfaceRegions regions;
|
||||||
(config.GetEndAddress() - config.GetStartAddress())) {
|
for (auto& pair : RangeFromInterval(dirty_regions, src_surface->GetInterval())) {
|
||||||
return surface;
|
if (pair.second == src_surface) {
|
||||||
|
regions += pair.first;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (auto& interval : regions) {
|
||||||
|
dirty_regions.set({interval, dest_surface});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
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));
|
||||||
|
|
Reference in New Issue