|
|
|
@ -65,8 +65,8 @@ struct FormatTuple { |
|
|
|
return params; |
|
|
|
} |
|
|
|
|
|
|
|
/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer( |
|
|
|
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config, Tegra::GPUVAddr zeta_address, |
|
|
|
/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, |
|
|
|
Tegra::GPUVAddr zeta_address, |
|
|
|
Tegra::DepthFormat format) { |
|
|
|
|
|
|
|
SurfaceParams params{}; |
|
|
|
@ -77,9 +77,9 @@ struct FormatTuple { |
|
|
|
params.component_type = ComponentTypeFromDepthFormat(format); |
|
|
|
params.type = GetFormatType(params.pixel_format); |
|
|
|
params.size_in_bytes = params.SizeInBytes(); |
|
|
|
params.width = config.width; |
|
|
|
params.height = config.height; |
|
|
|
params.unaligned_height = config.height; |
|
|
|
params.width = zeta_width; |
|
|
|
params.height = zeta_height; |
|
|
|
params.unaligned_height = zeta_height; |
|
|
|
params.size_in_bytes = params.SizeInBytes(); |
|
|
|
return params; |
|
|
|
} |
|
|
|
@ -254,6 +254,60 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup |
|
|
|
cur_state.Apply(); |
|
|
|
} |
|
|
|
|
|
|
|
static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, |
|
|
|
const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, |
|
|
|
GLuint read_fb_handle, GLuint draw_fb_handle) { |
|
|
|
OpenGLState prev_state{OpenGLState::GetCurState()}; |
|
|
|
SCOPE_EXIT({ prev_state.Apply(); }); |
|
|
|
|
|
|
|
OpenGLState state; |
|
|
|
state.draw.read_framebuffer = read_fb_handle; |
|
|
|
state.draw.draw_framebuffer = draw_fb_handle; |
|
|
|
state.Apply(); |
|
|
|
|
|
|
|
u32 buffers{}; |
|
|
|
|
|
|
|
if (type == SurfaceType::ColorTexture) { |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex, |
|
|
|
0); |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |
|
|
|
0); |
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, |
|
|
|
0); |
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |
|
|
|
0); |
|
|
|
|
|
|
|
buffers = GL_COLOR_BUFFER_BIT; |
|
|
|
} else if (type == SurfaceType::Depth) { |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0); |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0); |
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |
|
|
|
|
|
|
|
buffers = GL_DEPTH_BUFFER_BIT; |
|
|
|
} else if (type == SurfaceType::DepthStencil) { |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
|
|
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
|
|
|
src_tex, 0); |
|
|
|
|
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); |
|
|
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
|
|
|
dst_tex, 0); |
|
|
|
|
|
|
|
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; |
|
|
|
} |
|
|
|
|
|
|
|
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left, |
|
|
|
dst_rect.bottom, dst_rect.right, dst_rect.top, buffers, |
|
|
|
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { |
|
|
|
texture.Create(); |
|
|
|
const auto& rect{params.GetRect()}; |
|
|
|
@ -519,8 +573,8 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( |
|
|
|
} |
|
|
|
|
|
|
|
if (using_depth_fb) { |
|
|
|
depth_params = |
|
|
|
SurfaceParams::CreateForDepthBuffer(regs.rt[0], regs.zeta.Address(), regs.zeta.format); |
|
|
|
depth_params = SurfaceParams::CreateForDepthBuffer(regs.zeta_width, regs.zeta_height, |
|
|
|
regs.zeta.Address(), regs.zeta.format); |
|
|
|
} |
|
|
|
|
|
|
|
MathUtil::Rectangle<u32> color_rect{}; |
|
|
|
@ -565,17 +619,9 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) { |
|
|
|
surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerCacheOpenGL::MarkSurfaceAsDirty(const Surface& surface) { |
|
|
|
if (Settings::values.use_accurate_framebuffers) { |
|
|
|
// If enabled, always flush dirty surfaces
|
|
|
|
void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { |
|
|
|
surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); |
|
|
|
surface->FlushGLBuffer(); |
|
|
|
} else { |
|
|
|
// Otherwise, don't mark surfaces that we write to as cached, because the resulting loads
|
|
|
|
// and flushes are very slow and do not seem to improve accuracy
|
|
|
|
const auto& params{surface->GetSurfaceParams()}; |
|
|
|
Memory::RasterizerMarkRegionCached(params.addr, params.size_in_bytes, false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) { |
|
|
|
@ -588,25 +634,53 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) { |
|
|
|
if (gpu.memory_manager->GpuToCpuAddress(params.addr) == boost::none) |
|
|
|
return {}; |
|
|
|
|
|
|
|
// Check for an exact match in existing surfaces
|
|
|
|
const auto& surface_key{SurfaceKey::Create(params)}; |
|
|
|
const auto& search{surface_cache.find(surface_key)}; |
|
|
|
// Look up surface in the cache based on address
|
|
|
|
const auto& search{surface_cache.find(params.addr)}; |
|
|
|
Surface surface; |
|
|
|
if (search != surface_cache.end()) { |
|
|
|
surface = search->second; |
|
|
|
if (Settings::values.use_accurate_framebuffers) { |
|
|
|
// Reload the surface from Switch memory
|
|
|
|
LoadSurface(surface); |
|
|
|
} |
|
|
|
// If use_accurate_framebuffers is enabled, always load from memory
|
|
|
|
FlushSurface(surface); |
|
|
|
UnregisterSurface(surface); |
|
|
|
} else if (surface->GetSurfaceParams() != params) { |
|
|
|
// If surface parameters changed, recreate the surface from the old one
|
|
|
|
return RecreateSurface(surface, params); |
|
|
|
} else { |
|
|
|
// Use the cached surface as-is
|
|
|
|
return surface; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// No surface found - create a new one
|
|
|
|
surface = std::make_shared<CachedSurface>(params); |
|
|
|
RegisterSurface(surface); |
|
|
|
LoadSurface(surface); |
|
|
|
} |
|
|
|
|
|
|
|
return surface; |
|
|
|
} |
|
|
|
|
|
|
|
Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, |
|
|
|
const SurfaceParams& new_params) { |
|
|
|
// Verify surface is compatible for blitting
|
|
|
|
const auto& params{surface->GetSurfaceParams()}; |
|
|
|
ASSERT(params.type == new_params.type); |
|
|
|
ASSERT(params.pixel_format == new_params.pixel_format); |
|
|
|
ASSERT(params.component_type == new_params.component_type); |
|
|
|
|
|
|
|
// Create a new surface with the new parameters, and blit the previous surface to it
|
|
|
|
Surface new_surface{std::make_shared<CachedSurface>(new_params)}; |
|
|
|
BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, |
|
|
|
new_surface->GetSurfaceParams().GetRect(), params.type, read_framebuffer.handle, |
|
|
|
draw_framebuffer.handle); |
|
|
|
|
|
|
|
// Update cache accordingly
|
|
|
|
UnregisterSurface(surface); |
|
|
|
RegisterSurface(new_surface); |
|
|
|
|
|
|
|
return new_surface; |
|
|
|
} |
|
|
|
|
|
|
|
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const { |
|
|
|
// Tries to find the GPU address of a framebuffer based on the CPU address. This is because
|
|
|
|
// final output framebuffers are specified by CPU address, but internally our GPU cache uses
|
|
|
|
@ -652,22 +726,20 @@ void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size) |
|
|
|
|
|
|
|
void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) { |
|
|
|
const auto& params{surface->GetSurfaceParams()}; |
|
|
|
const auto& surface_key{SurfaceKey::Create(params)}; |
|
|
|
const auto& search{surface_cache.find(surface_key)}; |
|
|
|
const auto& search{surface_cache.find(params.addr)}; |
|
|
|
|
|
|
|
if (search != surface_cache.end()) { |
|
|
|
// Registered already
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
surface_cache[surface_key] = surface; |
|
|
|
surface_cache[params.addr] = surface; |
|
|
|
UpdatePagesCachedCount(params.addr, params.size_in_bytes, 1); |
|
|
|
} |
|
|
|
|
|
|
|
void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) { |
|
|
|
const auto& params{surface->GetSurfaceParams()}; |
|
|
|
const auto& surface_key{SurfaceKey::Create(params)}; |
|
|
|
const auto& search{surface_cache.find(surface_key)}; |
|
|
|
const auto& search{surface_cache.find(params.addr)}; |
|
|
|
|
|
|
|
if (search == surface_cache.end()) { |
|
|
|
// Unregistered already
|
|
|
|
|