Implement Texture Cache V2

merge-requests/60/head
Fernando Sahmkow 2019-05-07 10:57:16 +07:00 committed by ReinUsesLisp
parent 3d471e732d
commit bc930754cc
6 changed files with 575 additions and 388 deletions

@ -83,10 +83,10 @@ struct FramebufferCacheKey {
bool stencil_enable = false; bool stencil_enable = false;
std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{}; std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{};
std::array<CachedSurfaceView*, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{}; std::array<View, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{};
u32 colors_count = 0; u32 colors_count = 0;
CachedSurfaceView* zeta = nullptr; View zeta = nullptr;
auto Tie() const { auto Tie() const {
return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count, return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count,
@ -115,6 +115,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
RasterizerOpenGL::~RasterizerOpenGL() {} RasterizerOpenGL::~RasterizerOpenGL() {}
void RasterizerOpenGL::InitMemoryMananger(Tegra::MemoryManager& memory_manager) {
texture_cache.InitMemoryMananger(memory_manager);
}
void RasterizerOpenGL::CheckExtensions() { void RasterizerOpenGL::CheckExtensions() {
if (!GLAD_GL_ARB_texture_filter_anisotropic && !GLAD_GL_EXT_texture_filter_anisotropic) { if (!GLAD_GL_ARB_texture_filter_anisotropic && !GLAD_GL_EXT_texture_filter_anisotropic) {
LOG_WARNING( LOG_WARNING(
@ -474,9 +478,11 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
} }
current_framebuffer_config_state = fb_config_state; current_framebuffer_config_state = fb_config_state;
CachedSurfaceView* depth_surface{}; View depth_surface{};
if (using_depth_fb) { if (using_depth_fb) {
depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents); depth_surface = texture_cache.GetDepthBufferSurface(preserve_contents);
} else {
texture_cache.SetEmptyDepthBuffer();
} }
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0); UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
@ -489,38 +495,41 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
if (using_color_fb) { if (using_color_fb) {
if (single_color_target) { if (single_color_target) {
// Used when just a single color attachment is enabled, e.g. for clearing a color buffer // Used when just a single color attachment is enabled, e.g. for clearing a color buffer
CachedSurfaceView* color_surface{ View color_surface{
texture_cache.GetColorBufferSurface(*single_color_target, preserve_contents)}; texture_cache.GetColorBufferSurface(*single_color_target, preserve_contents)};
if (color_surface) { if (color_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even if // Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it. // the shader doesn't actually write to it.
color_surface->MarkAsModified(true); texture_cache.MarkColorBufferInUse(*single_color_target);
// Workaround for and issue in nvidia drivers // Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |= state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
color_surface->GetSurfaceParams().GetSrgbConversion();
} }
fbkey.is_single_buffer = true; fbkey.is_single_buffer = true;
fbkey.color_attachments[0] = fbkey.color_attachments[0] =
GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target); GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target);
fbkey.colors[0] = color_surface; fbkey.colors[0] = color_surface;
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
if (index != *single_color_target) {
texture_cache.SetEmptyColorBuffer(index);
}
}
} else { } else {
// Multiple color attachments are enabled // Multiple color attachments are enabled
for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
CachedSurfaceView* color_surface{ View color_surface{texture_cache.GetColorBufferSurface(index, preserve_contents)};
texture_cache.GetColorBufferSurface(index, preserve_contents)};
if (color_surface) { if (color_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even // Assume that a surface will be written to if it is used as a framebuffer, even
// if the shader doesn't actually write to it. // if the shader doesn't actually write to it.
color_surface->MarkAsModified(true); texture_cache.MarkColorBufferInUse(index);
// Enable sRGB only for supported formats // Enable sRGB only for supported formats
// Workaround for and issue in nvidia drivers // Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |= state.framebuffer_srgb.enabled |=
color_surface->GetSurfaceParams().GetSrgbConversion(); color_surface->GetSurfaceParams().srgb_conversion;
} }
fbkey.color_attachments[index] = fbkey.color_attachments[index] =
@ -538,11 +547,11 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
if (depth_surface) { if (depth_surface) {
// Assume that a surface will be written to if it is used as a framebuffer, even if // Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it. // the shader doesn't actually write to it.
depth_surface->MarkAsModified(true); texture_cache.MarkDepthBufferInUse();
fbkey.zeta = depth_surface; fbkey.zeta = depth_surface;
fbkey.stencil_enable = regs.stencil_enable && depth_surface->GetSurfaceParams().GetType() == fbkey.stencil_enable = regs.stencil_enable &&
SurfaceType::DepthStencil; depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
} }
SetupCachedFramebuffer(fbkey, current_state); SetupCachedFramebuffer(fbkey, current_state);
@ -728,11 +737,27 @@ void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) {
buffer_cache.InvalidateRegion(addr, size); buffer_cache.InvalidateRegion(addr, size);
} }
void RasterizerOpenGL::InvalidateRegionEx(GPUVAddr gpu_addr, CacheAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
if (!addr || !size) {
return;
}
texture_cache.InvalidateRegionEx(gpu_addr, size);
shader_cache.InvalidateRegion(addr, size);
global_cache.InvalidateRegion(addr, size);
buffer_cache.InvalidateRegion(addr, size);
}
void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
FlushRegion(addr, size); FlushRegion(addr, size);
InvalidateRegion(addr, size); InvalidateRegion(addr, size);
} }
void RasterizerOpenGL::FlushAndInvalidateRegionEx(GPUVAddr gpu_addr, CacheAddr addr, u64 size) {
FlushRegion(addr, size);
InvalidateRegionEx(gpu_addr, addr, size);
}
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src, bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
const Tegra::Engines::Fermi2D::Regs::Surface& dst, const Tegra::Engines::Fermi2D::Regs::Surface& dst,
const Common::Rectangle<u32>& src_rect, const Common::Rectangle<u32>& src_rect,
@ -740,7 +765,7 @@ bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs
MICROPROFILE_SCOPE(OpenGL_Blits); MICROPROFILE_SCOPE(OpenGL_Blits);
const auto src_surface{texture_cache.GetFermiSurface(src)}; const auto src_surface{texture_cache.GetFermiSurface(src)};
const auto dst_surface{texture_cache.GetFermiSurface(dst)}; const auto dst_surface{texture_cache.GetFermiSurface(dst)};
blitter.Blit(src_surface, dst_surface, src_rect, dst_rect); // blitter.Blit(src_surface, dst_surface, src_rect, dst_rect);
return true; return true;
} }
@ -762,10 +787,10 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
const auto& params{surface->GetSurfaceParams()}; const auto& params{surface->GetSurfaceParams()};
const auto& pixel_format{ const auto& pixel_format{
VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)}; VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
ASSERT_MSG(params.GetWidth() == config.width, "Framebuffer width is different"); ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
ASSERT_MSG(params.GetHeight() == config.height, "Framebuffer height is different"); ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
if (params.GetPixelFormat() != pixel_format) { if (params.pixel_format != pixel_format) {
LOG_WARNING(Render_OpenGL, "Framebuffer pixel_format is different"); LOG_WARNING(Render_OpenGL, "Framebuffer pixel_format is different");
} }
@ -860,10 +885,10 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
state.texture_units[current_bindpoint].sampler = sampler_cache.GetSampler(texture.tsc); state.texture_units[current_bindpoint].sampler = sampler_cache.GetSampler(texture.tsc);
if (const auto surface{texture_cache.GetTextureSurface(texture)}; surface) { if (const auto view{texture_cache.GetTextureSurface(texture, entry)}; view) {
state.texture_units[current_bindpoint].texture = surface->GetTexture( view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
entry.GetType(), entry.IsArray(), texture.tic.x_source, texture.tic.y_source, texture.tic.w_source);
texture.tic.z_source, texture.tic.w_source); state.texture_units[current_bindpoint].texture = view->GetTexture();
} else { } else {
// Can occur when texture addr is null or its memory is unmapped/invalid // Can occur when texture addr is null or its memory is unmapped/invalid
state.texture_units[current_bindpoint].texture = 0; state.texture_units[current_bindpoint].texture = 0;

@ -4,7 +4,9 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/microprofile.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h"
#include "video_core/morton.h" #include "video_core/morton.h"
#include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_texture_cache.h" #include "video_core/renderer_opengl/gl_texture_cache.h"
@ -22,6 +24,9 @@ using VideoCore::Surface::ComponentType;
using VideoCore::Surface::PixelFormat; using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::SurfaceTarget; using VideoCore::Surface::SurfaceTarget;
MICROPROFILE_DEFINE(OpenGL_Texture_Upload, "OpenGL", "Texture Upload", MP_RGB(128, 192, 128));
MICROPROFILE_DEFINE(OpenGL_Texture_Download, "OpenGL", "Texture Download", MP_RGB(128, 192, 128));
namespace { namespace {
struct FormatTuple { struct FormatTuple {
@ -129,8 +134,8 @@ const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType compon
return format; return format;
} }
GLenum GetTextureTarget(const SurfaceParams& params) { GLenum GetTextureTarget(const SurfaceTarget& target) {
switch (params.GetTarget()) { switch (target) {
case SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
return GL_TEXTURE_1D; return GL_TEXTURE_1D;
case SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
@ -175,8 +180,8 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.GetNumLevels() - 1); glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1);
if (params.GetNumLevels() == 1) { if (params.num_levels == 1) {
glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f);
} }
} }
@ -185,21 +190,20 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
OGLTexture texture; OGLTexture texture;
texture.Create(target); texture.Create(target);
switch (params.GetTarget()) { switch (params.target) {
case SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glTextureStorage1D(texture.handle, params.GetNumLevels(), internal_format, glTextureStorage1D(texture.handle, params.num_levels, internal_format, params.width);
params.GetWidth());
break; break;
case SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
glTextureStorage2D(texture.handle, params.GetNumLevels(), internal_format, glTextureStorage2D(texture.handle, params.num_levels, internal_format, params.width,
params.GetWidth(), params.GetHeight()); params.height);
break; break;
case SurfaceTarget::Texture3D: case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubeArray: case SurfaceTarget::TextureCubeArray:
glTextureStorage3D(texture.handle, params.GetNumLevels(), internal_format, glTextureStorage3D(texture.handle, params.num_levels, internal_format, params.width,
params.GetWidth(), params.GetHeight(), params.GetDepth()); params.height, params.depth);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
@ -212,54 +216,72 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
} // Anonymous namespace } // Anonymous namespace
CachedSurface::CachedSurface(TextureCacheOpenGL& texture_cache, const SurfaceParams& params) CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params)
: VideoCommon::SurfaceBase<TextureCacheOpenGL, CachedSurfaceView>{texture_cache, params} { : VideoCommon::SurfaceBase<View>(gpu_addr, params) {
const auto& tuple{GetFormatTuple(params.GetPixelFormat(), params.GetComponentType())}; const auto& tuple{GetFormatTuple(params.pixel_format, params.component_type)};
internal_format = tuple.internal_format; internal_format = tuple.internal_format;
format = tuple.format; format = tuple.format;
type = tuple.type; type = tuple.type;
is_compressed = tuple.compressed; is_compressed = tuple.compressed;
target = GetTextureTarget(params); target = GetTextureTarget(params.target);
texture = CreateTexture(params, target, internal_format); texture = CreateTexture(params, target, internal_format);
DecorateSurfaceName();
ViewParams main{};
main.num_levels = params.num_levels;
main.base_level = 0;
main.base_layer = 0;
main.num_layers = params.is_layered ? params.depth : 1;
main.target = params.target;
main_view = CreateView(main);
main_view->DecorateViewName(gpu_addr, params.TargetName() + "V:" + std::to_string(view_count++));
} }
CachedSurface::~CachedSurface() = default; CachedSurface::~CachedSurface() {
views.clear();
main_view = nullptr;
}
void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
LOG_CRITICAL(Render_OpenGL, "Flushing");
MICROPROFILE_SCOPE(OpenGL_Texture_Download);
void CachedSurface::DownloadTexture() {
// TODO(Rodrigo): Optimize alignment // TODO(Rodrigo): Optimize alignment
glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_ALIGNMENT, 1);
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
for (u32 level = 0; level < params.GetNumLevels(); ++level) { for (u32 level = 0; level < params.num_levels; ++level) {
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level);
if (is_compressed) { if (is_compressed) {
glGetCompressedTextureImage(texture.handle, level, glGetCompressedTextureImage(texture.handle, level,
static_cast<GLsizei>(params.GetHostMipmapSize(level)), static_cast<GLsizei>(params.GetHostMipmapSize(level)),
GetStagingBufferLevelData(level)); staging_buffer.data() + mip_offset);
} else { } else {
glGetTextureImage(texture.handle, level, format, type, glGetTextureImage(texture.handle, level, format, type,
static_cast<GLsizei>(params.GetHostMipmapSize(level)), static_cast<GLsizei>(params.GetHostMipmapSize(level)),
GetStagingBufferLevelData(level)); staging_buffer.data() + mip_offset);
} }
} }
} }
void CachedSurface::UploadTexture() { void CachedSurface::UploadTexture(std::vector<u8>& staging_buffer) {
MICROPROFILE_SCOPE(OpenGL_Texture_Upload);
SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); }); SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); });
for (u32 level = 0; level < params.GetNumLevels(); ++level) { for (u32 level = 0; level < params.num_levels; ++level) {
UploadTextureMipmap(level); UploadTextureMipmap(level, staging_buffer);
} }
} }
void CachedSurface::UploadTextureMipmap(u32 level) { void CachedSurface::UploadTextureMipmap(u32 level, std::vector<u8>& staging_buffer) {
// TODO(Rodrigo): Optimize alignment // TODO(Rodrigo): Optimize alignment
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
u8* buffer{GetStagingBufferLevelData(level)}; const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level);
u8* buffer{staging_buffer.data() + mip_offset};
if (is_compressed) { if (is_compressed) {
const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))}; const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))};
switch (params.GetTarget()) { switch (params.target) {
case SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glCompressedTextureSubImage2D(texture.handle, level, 0, 0, glCompressedTextureSubImage2D(texture.handle, level, 0, 0,
static_cast<GLsizei>(params.GetMipWidth(level)), static_cast<GLsizei>(params.GetMipWidth(level)),
@ -277,7 +299,7 @@ void CachedSurface::UploadTextureMipmap(u32 level) {
break; break;
case SurfaceTarget::TextureCubemap: { case SurfaceTarget::TextureCubemap: {
const std::size_t layer_size{params.GetHostLayerSize(level)}; const std::size_t layer_size{params.GetHostLayerSize(level)};
for (std::size_t face = 0; face < params.GetDepth(); ++face) { for (std::size_t face = 0; face < params.depth; ++face) {
glCompressedTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face), glCompressedTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
static_cast<GLsizei>(params.GetMipWidth(level)), static_cast<GLsizei>(params.GetMipWidth(level)),
static_cast<GLsizei>(params.GetMipHeight(level)), 1, static_cast<GLsizei>(params.GetMipHeight(level)), 1,
@ -291,7 +313,7 @@ void CachedSurface::UploadTextureMipmap(u32 level) {
UNREACHABLE(); UNREACHABLE();
} }
} else { } else {
switch (params.GetTarget()) { switch (params.target) {
case SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type, glTextureSubImage1D(texture.handle, level, 0, params.GetMipWidth(level), format, type,
buffer); buffer);
@ -310,7 +332,7 @@ void CachedSurface::UploadTextureMipmap(u32 level) {
static_cast<GLsizei>(params.GetMipDepth(level)), format, type, buffer); static_cast<GLsizei>(params.GetMipDepth(level)), format, type, buffer);
break; break;
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
for (std::size_t face = 0; face < params.GetDepth(); ++face) { for (std::size_t face = 0; face < params.depth; ++face) {
glTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face), glTextureSubImage3D(texture.handle, level, 0, 0, static_cast<GLint>(face),
params.GetMipWidth(level), params.GetMipHeight(level), 1, params.GetMipWidth(level), params.GetMipHeight(level), 1,
format, type, buffer); format, type, buffer);
@ -324,61 +346,57 @@ void CachedSurface::UploadTextureMipmap(u32 level) {
} }
void CachedSurface::DecorateSurfaceName() { void CachedSurface::DecorateSurfaceName() {
LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), LabelGLObject(GL_TEXTURE, texture.handle, GetGpuAddr(), params.TargetName());
params.GetTarget() == SurfaceTarget::Texture3D ? "3D" : "");
} }
std::unique_ptr<CachedSurfaceView> CachedSurface::CreateView(const ViewKey& view_key) { void CachedSurfaceView::DecorateViewName(GPUVAddr gpu_addr, std::string prefix) {
return std::make_unique<CachedSurfaceView>(*this, view_key); LabelGLObject(GL_TEXTURE, texture_view.texture.handle, gpu_addr, prefix);
} }
CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, ViewKey key) View CachedSurface::CreateView(const ViewParams& view_key) {
: surface{surface}, key{key}, params{surface.GetSurfaceParams()} {} auto view = std::make_shared<CachedSurfaceView>(*this, view_key);
views[view_key] = view;
view->DecorateViewName(gpu_addr, params.TargetName() + "V:" + std::to_string(view_count++));
return view;
}
CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& params)
: VideoCommon::ViewBase(params), surface{surface} {
target = GetTextureTarget(params.target);
texture_view = CreateTextureView();
}
CachedSurfaceView::~CachedSurfaceView() = default; CachedSurfaceView::~CachedSurfaceView() = default;
void CachedSurfaceView::Attach(GLenum attachment) const { void CachedSurfaceView::Attach(GLenum attachment) const {
ASSERT(key.num_layers == 1 && key.num_levels == 1); ASSERT(params.num_layers == 1 && params.num_levels == 1);
switch (params.GetTarget()) { switch (params.target) {
case SurfaceTarget::Texture1D: case SurfaceTarget::Texture1D:
glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(), glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, target,
surface.GetTexture(), key.base_level); surface.GetTexture(), params.base_level);
break; break;
case SurfaceTarget::Texture2D: case SurfaceTarget::Texture2D:
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTarget(), glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target,
surface.GetTexture(), key.base_level); surface.GetTexture(), params.base_level);
break; break;
case SurfaceTarget::Texture1DArray: case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray: case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap: case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray: case SurfaceTarget::TextureCubeArray:
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, surface.GetTexture(), glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, target,
key.base_level, key.base_layer); params.base_level, params.base_layer);
break; break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
} }
GLuint CachedSurfaceView::GetTexture(Tegra::Shader::TextureType texture_type, bool is_array, void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_source,
SwizzleSource x_source, SwizzleSource y_source,
SwizzleSource z_source, SwizzleSource w_source) { SwizzleSource z_source, SwizzleSource w_source) {
const auto [texture_view, target] = GetTextureView(texture_type, is_array); u32 swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
if (texture_view.get().texture.handle == 0) { if (swizzle == texture_view.swizzle)
texture_view.get() = std::move(CreateTextureView(target));
}
ApplySwizzle(texture_view, x_source, y_source, z_source, w_source);
return texture_view.get().texture.handle;
}
void CachedSurfaceView::ApplySwizzle(TextureView& texture_view, SwizzleSource x_source,
SwizzleSource y_source, SwizzleSource z_source,
SwizzleSource w_source) {
const std::array<SwizzleSource, 4> swizzle = {x_source, y_source, z_source, w_source};
if (swizzle == texture_view.swizzle) {
return; return;
}
const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source),
GetSwizzleSource(z_source), GetSwizzleSource(z_source),
GetSwizzleSource(w_source)}; GetSwizzleSource(w_source)};
@ -386,146 +404,48 @@ void CachedSurfaceView::ApplySwizzle(TextureView& texture_view, SwizzleSource x_
texture_view.swizzle = swizzle; texture_view.swizzle = swizzle;
} }
CachedSurfaceView::TextureView CachedSurfaceView::CreateTextureView(GLenum target) const { CachedSurfaceView::TextureView CachedSurfaceView::CreateTextureView() const {
const auto& owner_params = surface.GetSurfaceParams();
TextureView texture_view; TextureView texture_view;
glGenTextures(1, &texture_view.texture.handle); texture_view.texture.Create();
const GLuint handle{texture_view.texture.handle}; const GLuint handle{texture_view.texture.handle};
const FormatTuple& tuple{GetFormatTuple(params.GetPixelFormat(), params.GetComponentType())}; const FormatTuple& tuple{
GetFormatTuple(owner_params.pixel_format, owner_params.component_type)};
glTextureView(handle, target, surface.texture.handle, tuple.internal_format, key.base_level, glTextureView(handle, target, surface.texture.handle, tuple.internal_format, params.base_level,
key.num_levels, key.base_layer, key.num_layers); params.num_levels, params.base_layer, params.num_layers);
ApplyTextureDefaults(params, handle);
ApplyTextureDefaults(owner_params, handle);
u32 swizzle =
EncodeSwizzle(SwizzleSource::R, SwizzleSource::G, SwizzleSource::B, SwizzleSource::A);
texture_view.swizzle = swizzle;
return texture_view; return texture_view;
} }
std::pair<std::reference_wrapper<CachedSurfaceView::TextureView>, GLenum>
CachedSurfaceView::GetTextureView(Tegra::Shader::TextureType texture_type, bool is_array) {
using Pair = std::pair<std::reference_wrapper<TextureView>, GLenum>;
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D:
return is_array ? Pair{texture_view_1d_array, GL_TEXTURE_1D_ARRAY}
: Pair{texture_view_1d, GL_TEXTURE_1D};
case Tegra::Shader::TextureType::Texture2D:
return is_array ? Pair{texture_view_2d_array, GL_TEXTURE_2D_ARRAY}
: Pair{texture_view_2d, GL_TEXTURE_2D};
case Tegra::Shader::TextureType::Texture3D:
ASSERT(!is_array);
return {texture_view_3d, GL_TEXTURE_3D};
case Tegra::Shader::TextureType::TextureCube:
return is_array ? Pair{texture_view_cube_array, GL_TEXTURE_CUBE_MAP_ARRAY}
: Pair{texture_view_cube, GL_TEXTURE_CUBE_MAP};
}
UNREACHABLE();
}
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system, TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
VideoCore::RasterizerInterface& rasterizer) VideoCore::RasterizerInterface& rasterizer)
: TextureCacheBase{system, rasterizer} {} : TextureCacheBase{system, rasterizer} {}
TextureCacheOpenGL::~TextureCacheOpenGL() = default; TextureCacheOpenGL::~TextureCacheOpenGL() = default;
CachedSurfaceView* TextureCacheOpenGL::TryFastGetSurfaceView(GPUVAddr gpu_addr, VAddr cpu_addr, Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
u8* host_ptr, return std::make_shared<CachedSurface>(gpu_addr, params);
const SurfaceParams& new_params,
bool preserve_contents,
const std::vector<Surface>& overlaps) {
if (overlaps.size() > 1) {
return TryCopyAsViews(gpu_addr, cpu_addr, host_ptr, new_params, overlaps);
}
const auto& old_surface{overlaps[0]};
const auto& old_params{old_surface->GetSurfaceParams()};
if (old_params.GetTarget() == new_params.GetTarget() &&
old_params.GetDepth() == new_params.GetDepth() && old_params.GetDepth() == 1 &&
old_params.GetNumLevels() == new_params.GetNumLevels() &&
old_params.GetPixelFormat() == new_params.GetPixelFormat()) {
return SurfaceCopy(gpu_addr, cpu_addr, host_ptr, new_params, old_surface, old_params);
}
return nullptr;
} }
CachedSurfaceView* TextureCacheOpenGL::SurfaceCopy(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, void TextureCacheOpenGL::ImageCopy(Surface src_surface, Surface dst_surface,
const SurfaceParams& new_params, const VideoCommon::CopyParams& copy_params) {
const Surface& old_surface, const auto src_handle = src_surface->GetTexture();
const SurfaceParams& old_params) { const auto src_target = src_surface->GetTarget();
const auto new_surface{GetUncachedSurface(new_params)}; const auto dst_handle = dst_surface->GetTexture();
Register(new_surface, gpu_addr, cpu_addr, host_ptr); const auto dst_target = dst_surface->GetTarget();
glCopyImageSubData(src_handle, src_target, copy_params.source_level, copy_params.source_x,
const u32 min_width{ copy_params.source_y, copy_params.source_z, dst_handle, dst_target,
std::max(old_params.GetDefaultBlockWidth(), new_params.GetDefaultBlockWidth())}; copy_params.dest_level, copy_params.dest_x, copy_params.dest_y,
const u32 min_height{ copy_params.dest_z, copy_params.width, copy_params.height,
std::max(old_params.GetDefaultBlockHeight(), new_params.GetDefaultBlockHeight())}; copy_params.depth);
for (u32 level = 0; level < old_params.GetNumLevels(); ++level) {
const u32 width{std::min(old_params.GetMipWidth(level), new_params.GetMipWidth(level))};
const u32 height{std::min(old_params.GetMipHeight(level), new_params.GetMipHeight(level))};
if (width < min_width || height < min_height) {
// Avoid copies that are too small to be handled in OpenGL
break;
}
glCopyImageSubData(old_surface->GetTexture(), old_surface->GetTarget(), level, 0, 0, 0,
new_surface->GetTexture(), new_surface->GetTarget(), level, 0, 0, 0,
width, height, 1);
}
new_surface->MarkAsModified(true);
// TODO(Rodrigo): Add an entry to directly get the superview
return new_surface->GetView(gpu_addr, new_params);
}
CachedSurfaceView* TextureCacheOpenGL::TryCopyAsViews(GPUVAddr gpu_addr, VAddr cpu_addr,
u8* host_ptr, const SurfaceParams& new_params,
const std::vector<Surface>& overlaps) {
if (new_params.GetTarget() == SurfaceTarget::Texture1D ||
new_params.GetTarget() == SurfaceTarget::Texture1DArray ||
new_params.GetTarget() == SurfaceTarget::Texture3D) {
// Non-2D textures are not handled at the moment in this fast path.
return nullptr;
}
const auto new_surface{GetUncachedSurface(new_params)};
// TODO(Rodrigo): Move this down
Register(new_surface, gpu_addr, cpu_addr, host_ptr);
// TODO(Rodrigo): Find a way to avoid heap allocations here.
std::vector<CachedSurfaceView*> views;
views.reserve(overlaps.size());
for (const auto& overlap : overlaps) {
const auto view{
new_surface->TryGetView(overlap->GetGpuAddr(), overlap->GetSurfaceParams())};
if (!view) {
// TODO(Rodrigo): Remove this
Unregister(new_surface);
return nullptr;
}
views.push_back(view);
}
// TODO(Rodrigo): It's possible that these method leaves some unloaded textures if the data has
// been uploaded to guest memory but not used as a surface previously.
for (std::size_t i = 0; i < overlaps.size(); ++i) {
const auto& overlap{overlaps[i]};
const auto& view{views[i]};
for (u32 overlap_level = 0; overlap_level < view->GetNumLevels(); ++overlap_level) {
const u32 super_level{view->GetBaseLevel() + overlap_level};
glCopyImageSubData(overlap->GetTexture(), overlap->GetTarget(), overlap_level, 0, 0, 0,
new_surface->GetTexture(), new_surface->GetTarget(), super_level, 0,
0, view->GetBaseLayer(), view->GetWidth(), view->GetHeight(),
view->GetNumLayers());
}
}
new_surface->MarkAsModified(true);
// TODO(Rodrigo): Add an entry to directly get the superview
return new_surface->GetView(gpu_addr, new_params);
}
Surface TextureCacheOpenGL::CreateSurface(const SurfaceParams& params) {
return std::make_unique<CachedSurface>(*this, params);
} }
} // namespace OpenGL } // namespace OpenGL

@ -19,24 +19,25 @@
namespace OpenGL { namespace OpenGL {
using VideoCommon::SurfaceParams; using VideoCommon::SurfaceParams;
using VideoCommon::ViewKey; using VideoCommon::ViewParams;
class CachedSurfaceView; class CachedSurfaceView;
class CachedSurface; class CachedSurface;
class TextureCacheOpenGL; class TextureCacheOpenGL;
using Surface = std::shared_ptr<CachedSurface>; using Surface = std::shared_ptr<CachedSurface>;
using TextureCacheBase = VideoCommon::TextureCache<CachedSurface, CachedSurfaceView>; using View = std::shared_ptr<CachedSurfaceView>;
using TextureCacheBase = VideoCommon::TextureCache<Surface, View>;
class CachedSurface final : public VideoCommon::SurfaceBase<TextureCacheOpenGL, CachedSurfaceView> { class CachedSurface final : public VideoCommon::SurfaceBase<View> {
friend CachedSurfaceView; friend CachedSurfaceView;
public: public:
explicit CachedSurface(TextureCacheOpenGL& texture_cache, const SurfaceParams& params); explicit CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params);
~CachedSurface(); ~CachedSurface();
void UploadTexture(); void UploadTexture(std::vector<u8>& staging_buffer) override;
void DownloadTexture(); void DownloadTexture(std::vector<u8>& staging_buffer) override;
GLenum GetTarget() const { GLenum GetTarget() const {
return target; return target;
@ -49,99 +50,79 @@ public:
protected: protected:
void DecorateSurfaceName(); void DecorateSurfaceName();
std::unique_ptr<CachedSurfaceView> CreateView(const ViewKey& view_key); View CreateView(const ViewParams& view_key) override;
private: private:
void UploadTextureMipmap(u32 level); void UploadTextureMipmap(u32 level, std::vector<u8>& staging_buffer);
GLenum internal_format{}; GLenum internal_format{};
GLenum format{}; GLenum format{};
GLenum type{}; GLenum type{};
bool is_compressed{}; bool is_compressed{};
GLenum target{}; GLenum target{};
u32 view_count{};
OGLTexture texture; OGLTexture texture;
}; };
class CachedSurfaceView final { class CachedSurfaceView final : public VideoCommon::ViewBase {
public: public:
explicit CachedSurfaceView(CachedSurface& surface, ViewKey key); explicit CachedSurfaceView(CachedSurface& surface, const ViewParams& params);
~CachedSurfaceView(); ~CachedSurfaceView();
/// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
void Attach(GLenum attachment) const; void Attach(GLenum attachment) const;
GLuint GetTexture(Tegra::Shader::TextureType texture_type, bool is_array, GLuint GetTexture() {
Tegra::Texture::SwizzleSource x_source, return texture_view.texture.handle;
Tegra::Texture::SwizzleSource y_source,
Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source);
void MarkAsModified(bool is_modified) {
surface.MarkAsModified(is_modified);
} }
const SurfaceParams& GetSurfaceParams() const { const SurfaceParams& GetSurfaceParams() const {
return params; return surface.GetSurfaceParams();
} }
u32 GetWidth() const { u32 GetWidth() const {
return params.GetMipWidth(GetBaseLevel()); const auto owner_params = GetSurfaceParams();
return owner_params.GetMipWidth(params.base_level);
} }
u32 GetHeight() const { u32 GetHeight() const {
return params.GetMipHeight(GetBaseLevel()); const auto owner_params = GetSurfaceParams();
return owner_params.GetMipHeight(params.base_level);
} }
u32 GetDepth() const { u32 GetDepth() const {
return params.GetMipDepth(GetBaseLevel()); const auto owner_params = GetSurfaceParams();
return owner_params.GetMipDepth(params.base_level);
} }
u32 GetBaseLayer() const { void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
return key.base_layer;
}
u32 GetNumLayers() const {
return key.num_layers;
}
u32 GetBaseLevel() const {
return key.base_level;
}
u32 GetNumLevels() const {
return key.num_levels;
}
private:
struct TextureView {
OGLTexture texture;
std::array<Tegra::Texture::SwizzleSource, 4> swizzle{
Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G,
Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A};
};
void ApplySwizzle(TextureView& texture_view, Tegra::Texture::SwizzleSource x_source,
Tegra::Texture::SwizzleSource y_source, Tegra::Texture::SwizzleSource y_source,
Tegra::Texture::SwizzleSource z_source, Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source); Tegra::Texture::SwizzleSource w_source);
TextureView CreateTextureView(GLenum target) const; void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
std::pair<std::reference_wrapper<TextureView>, GLenum> GetTextureView( private:
Tegra::Shader::TextureType texture_type, bool is_array); struct TextureView {
OGLTextureView texture;
u32 swizzle;
};
u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
Tegra::Texture::SwizzleSource y_source,
Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source) const {
return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
(static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
}
TextureView CreateTextureView() const;
CachedSurface& surface; CachedSurface& surface;
const ViewKey key; GLenum target{};
const SurfaceParams params;
TextureView texture_view_1d; TextureView texture_view;
TextureView texture_view_1d_array;
TextureView texture_view_2d;
TextureView texture_view_2d_array;
TextureView texture_view_3d;
TextureView texture_view_cube;
TextureView texture_view_cube_array;
}; };
class TextureCacheOpenGL final : public TextureCacheBase { class TextureCacheOpenGL final : public TextureCacheBase {
@ -150,21 +131,9 @@ public:
~TextureCacheOpenGL(); ~TextureCacheOpenGL();
protected: protected:
CachedSurfaceView* TryFastGetSurfaceView(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, Surface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) override;
const SurfaceParams& new_params, void ImageCopy(Surface src_surface, Surface dst_surface,
bool preserve_contents, const VideoCommon::CopyParams& copy_params) override;
const std::vector<Surface>& overlaps);
Surface CreateSurface(const SurfaceParams& params);
private:
CachedSurfaceView* SurfaceCopy(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
const SurfaceParams& new_params, const Surface& old_surface,
const SurfaceParams& old_params);
CachedSurfaceView* TryCopyAsViews(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
const SurfaceParams& new_params,
const std::vector<Surface>& overlaps);
}; };
} // namespace OpenGL } // namespace OpenGL

@ -56,8 +56,7 @@ SurfaceBlitter::SurfaceBlitter() {
SurfaceBlitter::~SurfaceBlitter() = default; SurfaceBlitter::~SurfaceBlitter() = default;
void SurfaceBlitter::Blit(CachedSurfaceView* src, CachedSurfaceView* dst, void SurfaceBlitter::Blit(View src, View dst, const Common::Rectangle<u32>& src_rect,
const Common::Rectangle<u32>& src_rect,
const Common::Rectangle<u32>& dst_rect) const { const Common::Rectangle<u32>& dst_rect) const {
const auto& src_params{src->GetSurfaceParams()}; const auto& src_params{src->GetSurfaceParams()};
const auto& dst_params{dst->GetSurfaceParams()}; const auto& dst_params{dst->GetSurfaceParams()};
@ -72,17 +71,13 @@ void SurfaceBlitter::Blit(CachedSurfaceView* src, CachedSurfaceView* dst,
u32 buffers{}; u32 buffers{};
UNIMPLEMENTED_IF(src_params.GetTarget() != SurfaceTarget::Texture2D); UNIMPLEMENTED_IF(src_params.target != SurfaceTarget::Texture2D);
UNIMPLEMENTED_IF(dst_params.GetTarget() != SurfaceTarget::Texture2D); UNIMPLEMENTED_IF(dst_params.target != SurfaceTarget::Texture2D);
const auto GetTexture = [](CachedSurfaceView* view) { const GLuint src_texture{src->GetTexture()};
return view->GetTexture(TextureType::Texture2D, false, SwizzleSource::R, SwizzleSource::G, const GLuint dst_texture{dst->GetTexture()};
SwizzleSource::B, SwizzleSource::A);
};
const GLuint src_texture{GetTexture(src)};
const GLuint dst_texture{GetTexture(dst)};
if (src_params.GetType() == SurfaceType::ColorTexture) { if (src_params.type == SurfaceType::ColorTexture) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
src_texture, 0); src_texture, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
@ -94,7 +89,7 @@ void SurfaceBlitter::Blit(CachedSurfaceView* src, CachedSurfaceView* dst,
0); 0);
buffers = GL_COLOR_BUFFER_BIT; buffers = GL_COLOR_BUFFER_BIT;
} else if (src_params.GetType() == SurfaceType::Depth) { } else if (src_params.type == SurfaceType::Depth) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_texture, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_texture,
0); 0);
@ -106,7 +101,7 @@ void SurfaceBlitter::Blit(CachedSurfaceView* src, CachedSurfaceView* dst,
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
buffers = GL_DEPTH_BUFFER_BIT; buffers = GL_DEPTH_BUFFER_BIT;
} else if (src_params.GetType() == SurfaceType::DepthStencil) { } else if (src_params.type == SurfaceType::DepthStencil) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
src_texture, 0); src_texture, 0);
@ -148,4 +143,4 @@ void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_vie
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
} }
} // namespace OpenGL } // namespace OpenGL

@ -39,8 +39,8 @@ public:
explicit SurfaceBlitter(); explicit SurfaceBlitter();
~SurfaceBlitter(); ~SurfaceBlitter();
void Blit(CachedSurfaceView* src, CachedSurfaceView* dst, void Blit(View src, View dst, const Common::Rectangle<u32>& src_rect,
const Common::Rectangle<u32>& src_rect, const Common::Rectangle<u32>& dst_rect) const; const Common::Rectangle<u32>& dst_rect) const;
private: private:
OGLFramebuffer src_framebuffer; OGLFramebuffer src_framebuffer;
@ -49,4 +49,4 @@ private:
void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {}); void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string_view extra_info = {});
} // namespace OpenGL } // namespace OpenGL

@ -22,6 +22,7 @@
#include "video_core/memory_manager.h" #include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h" #include "video_core/rasterizer_interface.h"
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/texture_cache/copy_params.h"
#include "video_core/texture_cache/surface_base.h" #include "video_core/texture_cache/surface_base.h"
#include "video_core/texture_cache/surface_params.h" #include "video_core/texture_cache/surface_params.h"
#include "video_core/texture_cache/surface_view.h" #include "video_core/texture_cache/surface_view.h"
@ -40,32 +41,42 @@ class RasterizerInterface;
namespace VideoCommon { namespace VideoCommon {
using VideoCore::Surface::SurfaceTarget;
using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
template <typename TSurface, typename TView> template <typename TSurface, typename TView>
class TextureCache { class TextureCache {
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>; using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface>>;
using IntervalType = typename IntervalMap::interval_type; using IntervalType = typename IntervalMap::interval_type;
public: public:
void InitMemoryMananger(Tegra::MemoryManager& memory_manager) {
this->memory_manager = &memory_manager;
}
void InvalidateRegion(CacheAddr addr, std::size_t size) { void InvalidateRegion(CacheAddr addr, std::size_t size) {
for (const auto& surface : GetSurfacesInRegion(addr, size)) { for (const auto& surface : GetSurfacesInRegion(addr, size)) {
if (!surface->IsRegistered()) {
// Skip duplicates
continue;
}
Unregister(surface); Unregister(surface);
} }
} }
TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { void InvalidateRegionEx(GPUVAddr addr, std::size_t size) {
for (const auto& surface : GetSurfacesInRegionInner(addr, size)) {
Unregister(surface);
}
}
TView GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
const VideoCommon::Shader::Sampler& entry) {
const auto gpu_addr{config.tic.Address()}; const auto gpu_addr{config.tic.Address()};
if (!gpu_addr) { if (!gpu_addr) {
return {}; return {};
} }
const auto params{SurfaceParams::CreateForTexture(system, config)}; const auto params{SurfaceParams::CreateForTexture(system, config, entry)};
return GetSurfaceView(gpu_addr, params, true); return GetSurface(gpu_addr, params, true).second;
} }
TView* GetDepthBufferSurface(bool preserve_contents) { TView GetDepthBufferSurface(bool preserve_contents) {
const auto& regs{system.GPU().Maxwell3D().regs}; const auto& regs{system.GPU().Maxwell3D().regs};
const auto gpu_addr{regs.zeta.Address()}; const auto gpu_addr{regs.zeta.Address()};
if (!gpu_addr || !regs.zeta_enable) { if (!gpu_addr || !regs.zeta_enable) {
@ -75,36 +86,75 @@ public:
system, regs.zeta_width, regs.zeta_height, regs.zeta.format, system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
return GetSurfaceView(gpu_addr, depth_params, preserve_contents); auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents);
if (depth_buffer.target)
depth_buffer.target->MarkAsProtected(false);
if (depth_buffer.target)
depth_buffer.target->MarkAsProtected(true);
return surface_view.second;
} }
TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) { TView GetColorBufferSurface(std::size_t index, bool preserve_contents) {
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
const auto& regs{system.GPU().Maxwell3D().regs}; const auto& regs{system.GPU().Maxwell3D().regs};
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
SetEmptyColorBuffer(index);
return {}; return {};
} }
auto& memory_manager{system.GPU().MemoryManager()}; const auto& config{regs.rt[index]};
const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; const auto gpu_addr{config.Address()};
const auto gpu_addr{config.Address() +
config.base_layer * config.layer_stride * sizeof(u32)};
if (!gpu_addr) { if (!gpu_addr) {
SetEmptyColorBuffer(index);
return {}; return {};
} }
return GetSurfaceView(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
preserve_contents); preserve_contents);
if (render_targets[index].target)
render_targets[index].target->MarkAsProtected(false);
render_targets[index].target = surface_view.first;
if (render_targets[index].target)
render_targets[index].target->MarkAsProtected(true);
return surface_view.second;
} }
TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) { void MarkColorBufferInUse(std::size_t index) {
return GetSurfaceView(config.Address(), SurfaceParams::CreateForFermiCopySurface(config), if (render_targets[index].target)
true); render_targets[index].target->MarkAsModified(true, Tick());
} }
std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { void MarkDepthBufferInUse() {
if (depth_buffer.target)
depth_buffer.target->MarkAsModified(true, Tick());
}
void SetEmptyDepthBuffer() {
if (depth_buffer.target != nullptr) {
depth_buffer.target->MarkAsProtected(false);
depth_buffer.target = nullptr;
depth_buffer.view = nullptr;
}
}
void SetEmptyColorBuffer(std::size_t index) {
if (render_targets[index].target != nullptr) {
render_targets[index].target->MarkAsProtected(false);
std::memset(&render_targets[index].config, sizeof(RenderTargetConfig), 0);
render_targets[index].target = nullptr;
render_targets[index].view = nullptr;
}
}
TView GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) {
SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config);
const GPUVAddr gpu_addr = config.Address();
return GetSurface(gpu_addr, params, true).second;
}
TSurface TryFindFramebufferSurface(const u8* host_ptr) const {
const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
return it != registered_surfaces.end() ? *it->second.begin() : nullptr; return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
} }
@ -115,126 +165,334 @@ public:
protected: protected:
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: system{system}, rasterizer{rasterizer} {} : system{system}, rasterizer{rasterizer} {
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
SetEmptyColorBuffer(i);
}
SetEmptyDepthBuffer();
}
~TextureCache() = default; ~TextureCache() = default;
virtual TView* TryFastGetSurfaceView( virtual TSurface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) = 0;
GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params,
bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; virtual void ImageCopy(TSurface src_surface, TSurface dst_surface,
const CopyParams& copy_params) = 0;
void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr, void Register(TSurface surface) {
u8* host_ptr) { const GPUVAddr gpu_addr = surface->GetGpuAddr();
surface->Register(gpu_addr, cpu_addr, host_ptr); u8* host_ptr = memory_manager->GetPointer(gpu_addr);
registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); const std::size_t size = surface->GetSizeInBytes();
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); const std::optional<VAddr> cpu_addr = memory_manager->GpuToCpuAddress(gpu_addr);
if (!host_ptr || !cpu_addr) {
LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}",
gpu_addr);
return;
}
surface->SetHostPtr(host_ptr);
surface->SetCpuAddr(*cpu_addr);
registered_surfaces.add({GetInterval(host_ptr, size), {surface}});
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
RegisterInnerCache(surface);
surface->MarkAsRegistered(true);
} }
void Unregister(std::shared_ptr<TSurface> surface) { void Unregister(TSurface surface) {
registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); if (surface->IsProtected())
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); return;
surface->Unregister(); const GPUVAddr gpu_addr = surface->GetGpuAddr();
const void* host_ptr = surface->GetHostPtr();
const std::size_t size = surface->GetSizeInBytes();
const VAddr cpu_addr = surface->GetCpuAddr();
registered_surfaces.erase(GetInterval(host_ptr, size));
rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
UnregisterInnerCache(surface);
surface->MarkAsRegistered(false);
ReserveSurface(surface->GetSurfaceParams(), surface);
} }
std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) { TSurface GetUncachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
if (const auto surface = TryGetReservedSurface(params); surface) if (const auto surface = TryGetReservedSurface(params); surface) {
surface->SetGpuAddr(gpu_addr);
return surface; return surface;
}
// No reserved surface available, create a new one and reserve it // No reserved surface available, create a new one and reserve it
auto new_surface{CreateSurface(params)}; auto new_surface{CreateSurface(gpu_addr, params)};
ReserveSurface(params, new_surface);
return new_surface; return new_surface;
} }
Core::System& system; Core::System& system;
private: private:
TView* GetSurfaceView(GPUVAddr gpu_addr, const SurfaceParams& params, bool preserve_contents) { enum class RecycleStrategy : u32 {
auto& memory_manager{system.GPU().MemoryManager()}; Ignore = 0,
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; Flush = 1,
DEBUG_ASSERT(cpu_addr); BufferCopy = 3,
};
const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
const GPUVAddr gpu_addr, const bool untopological) {
// Untopological decision
if (untopological) {
return RecycleStrategy::Ignore;
}
// 3D Textures decision
if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
return RecycleStrategy::Flush;
}
for (auto s : overlaps) {
const auto& s_params = s->GetSurfaceParams();
if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) {
return RecycleStrategy::Flush;
}
}
return RecycleStrategy::Ignore;
}
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params, const GPUVAddr gpu_addr,
const u8* host_ptr, const bool preserve_contents,
const bool untopological) {
for (auto surface : overlaps) {
Unregister(surface);
}
RecycleStrategy strategy = !Settings::values.use_accurate_gpu_emulation
? PickStrategy(overlaps, params, gpu_addr, untopological)
: RecycleStrategy::Flush;
switch (strategy) {
case RecycleStrategy::Ignore: {
return InitializeSurface(gpu_addr, params, preserve_contents);
}
case RecycleStrategy::Flush: {
std::sort(overlaps.begin(), overlaps.end(),
[](const TSurface& a, const TSurface& b) -> bool {
return a->GetModificationTick() < b->GetModificationTick();
});
for (auto surface : overlaps) {
FlushSurface(surface);
}
return InitializeSurface(gpu_addr, params, preserve_contents);
}
default: {
UNIMPLEMENTED_MSG("Unimplemented Texture Cache Recycling Strategy!");
return InitializeSurface(gpu_addr, params, preserve_contents);
}
}
}
std::pair<TSurface, TView> RebuildMirage(TSurface current_surface,
const SurfaceParams& params) {
const auto gpu_addr = current_surface->GetGpuAddr();
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
std::vector<CopyParams> bricks = current_surface->BreakDown();
for (auto& brick : bricks) {
ImageCopy(current_surface, new_surface, brick);
}
Unregister(current_surface);
Register(new_surface);
return {new_surface, new_surface->GetMainView()};
}
std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
const SurfaceParams& params) {
const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
if (is_mirage) {
return RebuildMirage(current_surface, params);
}
const bool matches_target = current_surface->MatchTarget(params.target);
if (matches_target) {
return {current_surface, current_surface->GetMainView()};
}
return {current_surface, current_surface->EmplaceOverview(params)};
}
std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps,
const SurfaceParams& params,
const GPUVAddr gpu_addr,
const u8* host_ptr) {
if (!params.is_layered || params.target == SurfaceTarget::Texture3D) {
return {};
}
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
for (auto surface : overlaps) {
const SurfaceParams& src_params = surface->GetSurfaceParams();
if (src_params.is_layered || src_params.num_levels > 1) {
// We send this cases to recycle as they are more complex to handle
return {};
}
const std::size_t candidate_size = src_params.GetGuestSizeInBytes();
auto mipmap_layer = new_surface->GetLayerMipmap(surface->GetGpuAddr());
if (!mipmap_layer) {
return {};
}
const u32 layer = (*mipmap_layer).first;
const u32 mipmap = (*mipmap_layer).second;
if (new_surface->GetMipmapSize(mipmap) != candidate_size) {
return {};
}
// Now we got all the data set up
CopyParams copy_params{};
const u32 dst_width = params.GetMipWidth(mipmap);
const u32 dst_height = params.GetMipHeight(mipmap);
copy_params.width = std::min(src_params.width, dst_width);
copy_params.height = std::min(src_params.height, dst_height);
copy_params.depth = 1;
copy_params.source_level = 0;
copy_params.dest_level = mipmap;
copy_params.source_z = 0;
copy_params.dest_z = layer;
ImageCopy(surface, new_surface, copy_params);
}
for (auto surface : overlaps) {
Unregister(surface);
}
Register(new_surface);
return {{new_surface, new_surface->GetMainView()}};
}
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
bool preserve_contents) {
const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
const auto cache_addr{ToCacheAddr(host_ptr)}; const auto cache_addr{ToCacheAddr(host_ptr)};
auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; const std::size_t candidate_size = params.GetGuestSizeInBytes();
auto overlaps{GetSurfacesInRegionInner(gpu_addr, candidate_size)};
if (overlaps.empty()) { if (overlaps.empty()) {
return LoadSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); return InitializeSurface(gpu_addr, params, preserve_contents);
}
for (auto surface : overlaps) {
if (!surface->MatchesTopology(params)) {
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
true);
}
} }
if (overlaps.size() == 1) { if (overlaps.size() == 1) {
if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) { TSurface current_surface = overlaps[0];
return view; if (current_surface->MatchesStructure(params) &&
current_surface->GetGpuAddr() == gpu_addr &&
(params.target != SurfaceTarget::Texture3D ||
current_surface->MatchTarget(params.target))) {
return ManageStructuralMatch(current_surface, params);
} }
} if (current_surface->GetSizeInBytes() <= candidate_size) {
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
const auto fast_view{TryFastGetSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, false);
preserve_contents, overlaps)};
if (!fast_view) {
std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) {
return lhs->GetModificationTick() < rhs->GetModificationTick();
});
}
for (const auto& surface : overlaps) {
if (!fast_view) {
// Flush even when we don't care about the contents, to preserve memory not
// written by the new surface.
FlushSurface(surface);
} }
Unregister(surface); std::optional<TView> view = current_surface->EmplaceView(params, gpu_addr);
if (view.has_value()) {
const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
if (is_mirage) {
LOG_CRITICAL(HW_GPU, "Mirage View Unsupported");
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
false);
}
return {current_surface, *view};
}
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
} else {
std::optional<std::pair<TSurface, TView>> view =
ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
if (view.has_value()) {
return *view;
}
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
} }
if (fast_view) {
return fast_view;
}
return LoadSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
} }
TView* LoadSurfaceView(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
const SurfaceParams& params, bool preserve_contents) { bool preserve_contents) {
const auto new_surface{GetUncachedSurface(params)}; auto new_surface{GetUncachedSurface(gpu_addr, params)};
Register(new_surface, gpu_addr, cpu_addr, host_ptr); Register(new_surface);
if (preserve_contents) { if (preserve_contents) {
LoadSurface(new_surface); LoadSurface(new_surface);
} }
return new_surface->GetView(gpu_addr, params); return {new_surface, new_surface->GetMainView()};
} }
void LoadSurface(const std::shared_ptr<TSurface>& surface) { void LoadSurface(const TSurface& surface) {
surface->LoadBuffer(); staging_buffer.resize(surface->GetHostSizeInBytes());
surface->UploadTexture(); surface->LoadBuffer(*memory_manager, staging_buffer);
surface->MarkAsModified(false); surface->UploadTexture(staging_buffer);
surface->MarkAsModified(false, Tick());
} }
void FlushSurface(const std::shared_ptr<TSurface>& surface) { void FlushSurface(const TSurface& surface) {
if (!surface->IsModified()) { if (!surface->IsModified()) {
return; return;
} }
surface->DownloadTexture(); staging_buffer.resize(surface->GetHostSizeInBytes());
surface->FlushBuffer(); surface->DownloadTexture(staging_buffer);
surface->FlushBuffer(staging_buffer);
surface->MarkAsModified(false, Tick());
} }
std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr, std::vector<TSurface> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
std::size_t size) const {
if (size == 0) { if (size == 0) {
return {}; return {};
} }
const IntervalType interval{cache_addr, cache_addr + size}; const IntervalType interval{cache_addr, cache_addr + size};
std::vector<std::shared_ptr<TSurface>> surfaces; std::vector<TSurface> surfaces;
for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
surfaces.push_back(*pair.second.begin()); for (auto& s : pair.second) {
if (!s || !s->IsRegistered()) {
continue;
}
surfaces.push_back(s);
}
} }
return surfaces; return surfaces;
} }
void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) { void RegisterInnerCache(TSurface& surface) {
GPUVAddr start = surface->GetGpuAddr() >> inner_cache_page_bits;
const GPUVAddr end = (surface->GetGpuAddrEnd() - 1) >> inner_cache_page_bits;
while (start <= end) {
inner_cache[start].push_back(surface);
start++;
}
}
void UnregisterInnerCache(TSurface& surface) {
GPUVAddr start = surface->GetGpuAddr() >> inner_cache_page_bits;
const GPUVAddr end = (surface->GetGpuAddrEnd() - 1) >> inner_cache_page_bits;
while (start <= end) {
inner_cache[start].remove(surface);
start++;
}
}
std::vector<TSurface> GetSurfacesInRegionInner(const GPUVAddr gpu_addr, const std::size_t size) {
if (size == 0) {
return {};
}
const GPUVAddr gpu_addr_end = gpu_addr + size;
GPUVAddr start = gpu_addr >> inner_cache_page_bits;
const GPUVAddr end = (gpu_addr_end - 1) >> inner_cache_page_bits;
std::vector<TSurface> surfaces;
while (start <= end) {
std::list<TSurface>& list = inner_cache[start];
for (auto& s : list) {
if (!s->IsPicked() && s->Overlaps(gpu_addr, gpu_addr_end)) {
s->MarkAsPicked(true);
surfaces.push_back(s);
}
}
start++;
}
for (auto& s : surfaces) {
s->MarkAsPicked(false);
}
return surfaces;
}
void ReserveSurface(const SurfaceParams& params, TSurface surface) {
surface_reserve[params].push_back(std::move(surface)); surface_reserve[params].push_back(std::move(surface));
} }
std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) { TSurface TryGetReservedSurface(const SurfaceParams& params) {
auto search{surface_reserve.find(params)}; auto search{surface_reserve.find(params)};
if (search == surface_reserve.end()) { if (search == surface_reserve.end()) {
return {}; return {};
@ -247,21 +505,41 @@ private:
return {}; return {};
} }
IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const { IntervalType GetInterval(const void* host_ptr, const std::size_t size) const {
return IntervalType::right_open(surface->GetCacheAddr(), const CacheAddr addr = ToCacheAddr(host_ptr);
surface->GetCacheAddr() + surface->GetSizeInBytes()); return IntervalType::right_open(addr, addr + size);
} }
struct RenderInfo {
RenderTargetConfig config;
TSurface target;
TView view;
};
struct DepthBufferInfo {
TSurface target;
TView view;
};
VideoCore::RasterizerInterface& rasterizer; VideoCore::RasterizerInterface& rasterizer;
Tegra::MemoryManager* memory_manager;
u64 ticks{}; u64 ticks{};
IntervalMap registered_surfaces; IntervalMap registered_surfaces;
static constexpr u64 inner_cache_page_bits{20};
static constexpr u64 inner_cache_page_size{1 << inner_cache_page_bits};
std::unordered_map<GPUVAddr, std::list<TSurface>> inner_cache;
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
/// previously been used. This is to prevent surfaces from being constantly created and /// previously been used. This is to prevent surfaces from being constantly created and
/// destroyed when used with different surface parameters. /// destroyed when used with different surface parameters.
std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve; std::unordered_map<SurfaceParams, std::list<TSurface>> surface_reserve;
std::array<RenderInfo, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> render_targets;
DepthBufferInfo depth_buffer;
std::vector<u8> staging_buffer;
}; };
} // namespace VideoCommon } // namespace VideoCommon