texture_cache: Correct premature texceptions

Due to our current infrastructure, it is possible for a mipmap to be set 
on as a render target before a texception of that mipmap's superset be 
set afterwards. This is problematic as we rely on texture views to set 
up texceptions and protecting render targets targets for 3D texture 
rendering.

One simple solution is to configure framebuffers after texture setup but 
this brings other problems. This solution, forces a reconfiguration of 
the framebuffers after such event happens.
merge-requests/60/head
Fernando Sahmkow 2019-05-08 17:45:59 +07:00 committed by ReinUsesLisp
parent ba677ccb5a
commit 4e2071b6d9
4 changed files with 51 additions and 14 deletions

@ -461,15 +461,15 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
} }
std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents, OpenGLState& current_state, bool must_reconfigure, bool using_color_fb, bool using_depth_fb,
std::optional<std::size_t> single_color_target) { bool preserve_contents, std::optional<std::size_t> single_color_target) {
MICROPROFILE_SCOPE(OpenGL_Framebuffer); MICROPROFILE_SCOPE(OpenGL_Framebuffer);
auto& gpu = system.GPU().Maxwell3D(); auto& gpu = system.GPU().Maxwell3D();
const auto& regs = gpu.regs; const auto& regs = gpu.regs;
const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents, const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
single_color_target}; single_color_target};
if (fb_config_state == current_framebuffer_config_state && if (!must_reconfigure && fb_config_state == current_framebuffer_config_state &&
gpu.dirty_flags.color_buffer.none() && !gpu.dirty_flags.zeta_buffer) { gpu.dirty_flags.color_buffer.none() && !gpu.dirty_flags.zeta_buffer) {
// Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
// single color targets). This is done because the guest registers may not change but the // single color targets). This is done because the guest registers may not change but the
@ -622,8 +622,9 @@ void RasterizerOpenGL::Clear() {
return; return;
} }
const auto [clear_depth, clear_stencil] = ConfigureFramebuffers( const auto [clear_depth, clear_stencil] =
clear_state, use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value()); ConfigureFramebuffers(clear_state, false, use_color, use_depth || use_stencil, false,
regs.clear_buffers.RT.Value());
if (regs.clear_flags.scissor) { if (regs.clear_flags.scissor) {
SyncScissorTest(clear_state); SyncScissorTest(clear_state);
} }
@ -705,6 +706,10 @@ void RasterizerOpenGL::DrawArrays() {
DrawParameters params = SetupDraw(); DrawParameters params = SetupDraw();
SetupShaders(params.primitive_mode); SetupShaders(params.primitive_mode);
if (texture_cache.ConsumeReconfigurationFlag()) {
ConfigureFramebuffers(state, true);
}
buffer_cache.Unmap(); buffer_cache.Unmap();
shader_program_manager->ApplyTo(state); shader_program_manager->ApplyTo(state);

@ -101,6 +101,8 @@ private:
/** /**
* Configures the color and depth framebuffer states. * Configures the color and depth framebuffer states.
* @param must_reconfigure If true, tells the framebuffer to skip the cache and reconfigure
* again. Used by the texture cache to solve texception conflicts
* @param use_color_fb If true, configure color framebuffers. * @param use_color_fb If true, configure color framebuffers.
* @param using_depth_fb If true, configure the depth/stencil framebuffer. * @param using_depth_fb If true, configure the depth/stencil framebuffer.
* @param preserve_contents If true, tries to preserve data from a previously used framebuffer. * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
@ -109,8 +111,9 @@ private:
* (requires using_depth_fb to be true) * (requires using_depth_fb to be true)
*/ */
std::pair<bool, bool> ConfigureFramebuffers( std::pair<bool, bool> ConfigureFramebuffers(
OpenGLState& current_state, bool use_color_fb = true, bool using_depth_fb = true, OpenGLState& current_state, bool must_reconfigure = false, bool use_color_fb = true,
bool preserve_contents = true, std::optional<std::size_t> single_color_target = {}); bool using_depth_fb = true, bool preserve_contents = true,
std::optional<std::size_t> single_color_target = {});
/// Configures the current constbuffers to use for the draw command. /// Configures the current constbuffers to use for the draw command.
void SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, void SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,

@ -55,6 +55,11 @@ public:
return (cache_addr < end) && (cache_addr_end > start); return (cache_addr < end) && (cache_addr_end > start);
} }
bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) {
const GPUVAddr gpu_addr_end = gpu_addr + guest_memory_size;
return (gpu_addr <= other_start && other_end <= gpu_addr_end);
}
// Use only when recycling a surface // Use only when recycling a surface
void SetGpuAddr(const GPUVAddr new_addr) { void SetGpuAddr(const GPUVAddr new_addr) {
gpu_addr = new_addr; gpu_addr = new_addr;
@ -105,6 +110,12 @@ public:
return params.target == target; return params.target == target;
} }
bool MatchesSubTexture(const SurfaceParams& rhs, const GPUVAddr other_gpu_addr) const {
return std::tie(gpu_addr, params.target, params.num_levels) ==
std::tie(other_gpu_addr, rhs.target, rhs.num_levels) &&
params.target == SurfaceTarget::Texture2D && params.num_levels == 1;
}
bool MatchesTopology(const SurfaceParams& rhs) const { bool MatchesTopology(const SurfaceParams& rhs) const {
const u32 src_bpp{params.GetBytesPerPixel()}; const u32 src_bpp{params.GetBytesPerPixel()};
const u32 dst_bpp{rhs.GetBytesPerPixel()}; const u32 dst_bpp{rhs.GetBytesPerPixel()};
@ -121,9 +132,9 @@ public:
} }
// Tiled surface // Tiled surface
if (std::tie(params.height, params.depth, params.block_width, params.block_height, if (std::tie(params.height, params.depth, params.block_width, params.block_height,
params.block_depth, params.tile_width_spacing) == params.block_depth, params.tile_width_spacing, params.num_levels) ==
std::tie(rhs.height, rhs.depth, rhs.block_width, rhs.block_height, rhs.block_depth, std::tie(rhs.height, rhs.depth, rhs.block_width, rhs.block_height, rhs.block_depth,
rhs.tile_width_spacing)) { rhs.tile_width_spacing, rhs.num_levels)) {
if (params.width == rhs.width) { if (params.width == rhs.width) {
return MatchStructureResult::FullMatch; return MatchStructureResult::FullMatch;
} }
@ -259,7 +270,7 @@ public:
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr) { std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr) {
if (view_addr < gpu_addr || params.target == SurfaceTarget::Texture3D || if (view_addr < gpu_addr || params.target == SurfaceTarget::Texture3D ||
view_params.target == SurfaceTarget::Texture3D) { params.num_levels == 1 || view_params.target == SurfaceTarget::Texture3D) {
return {}; return {};
} }
const auto layer_mipmap{GetLayerMipmap(view_addr)}; const auto layer_mipmap{GetLayerMipmap(view_addr)};

@ -120,6 +120,10 @@ public:
return {}; return {};
} }
if (regs.color_mask[index].raw == 0) {
return {};
}
auto surface_view = GetSurface(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) if (render_targets[index].target)
@ -183,6 +187,12 @@ public:
return ++ticks; return ++ticks;
} }
bool ConsumeReconfigurationFlag() {
const bool result = force_reconfiguration;
force_reconfiguration = false;
return result;
}
protected: protected:
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
: system{system}, rasterizer{rasterizer} { : system{system}, rasterizer{rasterizer} {
@ -219,9 +229,10 @@ protected:
rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
} }
void Unregister(TSurface surface) { void Unregister(TSurface surface, const bool force_unregister = false) {
if (surface->IsProtected()) if (surface->IsProtected() && !force_unregister) {
return; return;
}
const GPUVAddr gpu_addr = surface->GetGpuAddr(); const GPUVAddr gpu_addr = surface->GetGpuAddr();
const CacheAddr cache_ptr = surface->GetCacheAddr(); const CacheAddr cache_ptr = surface->GetCacheAddr();
const std::size_t size = surface->GetSizeInBytes(); const std::size_t size = surface->GetSizeInBytes();
@ -365,8 +376,10 @@ private:
std::min(src_params.height, dst_height), 1); std::min(src_params.height, dst_height), 1);
ImageCopy(surface, new_surface, copy_params); ImageCopy(surface, new_surface, copy_params);
} }
force_reconfiguration = false;
for (auto surface : overlaps) { for (auto surface : overlaps) {
Unregister(surface); force_reconfiguration |= surface->IsProtected();
Unregister(surface, true);
} }
Register(new_surface); Register(new_surface);
return {{new_surface, new_surface->GetMainView()}}; return {{new_surface, new_surface->GetMainView()}};
@ -379,6 +392,7 @@ private:
const auto cache_addr{ToCacheAddr(host_ptr)}; const auto cache_addr{ToCacheAddr(host_ptr)};
const std::size_t candidate_size = params.GetGuestSizeInBytes(); const std::size_t candidate_size = params.GetGuestSizeInBytes();
auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)}; auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
if (overlaps.empty()) { if (overlaps.empty()) {
return InitializeSurface(gpu_addr, params, preserve_contents); return InitializeSurface(gpu_addr, params, preserve_contents);
} }
@ -403,7 +417,7 @@ private:
return RebuildSurface(current_surface, params); return RebuildSurface(current_surface, params);
} }
} }
if (current_surface->GetSizeInBytes() <= candidate_size) { if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
false); false);
} }
@ -530,6 +544,10 @@ private:
u64 ticks{}; u64 ticks{};
// Sometimes Setup Textures can hit a surface that's on the render target, when this happens
// we force a reconfiguration of the frame buffer after setup.
bool force_reconfiguration;
// The internal Cache is different for the Texture Cache. It's based on buckets // The internal Cache is different for the Texture Cache. It's based on buckets
// of 1MB. This fits better for the purpose of this cache as textures are normaly // of 1MB. This fits better for the purpose of this cache as textures are normaly
// large in size. // large in size.