From ca78d3493347285e67457f6fb9a064e70530d332 Mon Sep 17 00:00:00 2001 From: wwylele Date: Mon, 2 Jul 2018 12:55:04 +0300 Subject: [PATCH 1/7] gl_rasterizer: implement mipmap --- src/video_core/regs_texturing.h | 7 +- .../renderer_opengl/gl_rasterizer.cpp | 32 ++++- .../renderer_opengl/gl_rasterizer.h | 4 + .../renderer_opengl/gl_rasterizer_cache.cpp | 109 +++++++++++++++--- .../renderer_opengl/gl_rasterizer_cache.h | 7 +- src/video_core/renderer_opengl/pica_to_gl.h | 51 ++++---- 6 files changed, 163 insertions(+), 47 deletions(-) diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h index d07aa28b7..3954e13b4 100644 --- a/src/video_core/regs_texturing.h +++ b/src/video_core/regs_texturing.h @@ -59,11 +59,16 @@ struct TexturingRegs { BitField<2, 1, TextureFilter> min_filter; BitField<8, 3, WrapMode> wrap_t; BitField<12, 3, WrapMode> wrap_s; + BitField<24, 1, TextureFilter> mip_filter; /// @note Only valid for texture 0 according to 3DBrew. BitField<28, 3, TextureType> type; }; - INSERT_PADDING_WORDS(0x1); + union { + BitField<0, 13, s32> bias; // fixed1.4.8 + BitField<16, 4, u32> max_level; + BitField<24, 4, u32> min_level; + } lod; BitField<0, 28, u32> address; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 923f2efd3..475d2a88b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1555,13 +1555,16 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con void RasterizerOpenGL::SamplerInfo::Create() { sampler.Create(); - mag_filter = min_filter = TextureConfig::Linear; + mag_filter = min_filter = mip_filter = TextureConfig::Linear; wrap_s = wrap_t = TextureConfig::Repeat; border_color = 0; + lod_min = lod_max = 0; + lod_bias = 0; - // default is GL_LINEAR_MIPMAP_LINEAR - glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // default is 1000 and -1000 // Other attributes have correct defaults + glSamplerParameterf(sampler.handle, GL_TEXTURE_MAX_LOD, lod_max); + glSamplerParameterf(sampler.handle, GL_TEXTURE_MIN_LOD, lod_min); } void RasterizerOpenGL::SamplerInfo::SyncWithConfig( @@ -1571,11 +1574,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( if (mag_filter != config.mag_filter) { mag_filter = config.mag_filter; - glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter)); + glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureMagFilterMode(mag_filter)); } - if (min_filter != config.min_filter) { + if (min_filter != config.min_filter || mip_filter != config.mip_filter) { min_filter = config.min_filter; - glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter)); + mip_filter = config.mip_filter; + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, + PicaToGL::TextureMinFilterMode(min_filter, mip_filter)); } if (wrap_s != config.wrap_s) { @@ -1594,6 +1599,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data()); } } + + if (lod_min != config.lod.min_level) { + lod_min = config.lod.min_level; + glSamplerParameterf(s, GL_TEXTURE_MIN_LOD, lod_min); + } + + if (lod_max != config.lod.max_level) { + lod_max = config.lod.max_level; + glSamplerParameterf(s, GL_TEXTURE_MAX_LOD, lod_max); + } + + if (lod_bias != config.lod.bias) { + lod_bias = config.lod.bias; + glSamplerParameterf(s, GL_TEXTURE_LOD_BIAS, lod_bias / 256.0f); + } } void RasterizerOpenGL::SetShader() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 730364819..f890c1049 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -69,9 +69,13 @@ private: private: TextureConfig::TextureFilter mag_filter; TextureConfig::TextureFilter min_filter; + TextureConfig::TextureFilter mip_filter; TextureConfig::WrapMode wrap_s; TextureConfig::WrapMode wrap_t; u32 border_color; + u32 lod_min; + u32 lod_max; + s32 lod_bias; }; /// Structure that the hardware rendered vertices are composed of diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 44dd2e78f..5f7ceecaa 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1326,10 +1326,11 @@ Surface RasterizerCacheOpenGL::GetTextureSurface( const Pica::TexturingRegs::FullTextureConfig& config) { Pica::Texture::TextureInfo info = Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); - return GetTextureSurface(info); + return GetTextureSurface(info, config.config.lod.max_level); } -Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info) { +Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info, + u32 max_level) { SurfaceParams params; params.addr = info.physical_address; params.width = info.width; @@ -1338,23 +1339,97 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); params.UpdateParams(); - if (info.width % 8 != 0 || info.height % 8 != 0) { - Surface src_surface; - Common::Rectangle rect; - std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); - - params.res_scale = src_surface->res_scale; - Surface tmp_surface = CreateSurface(params); - BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, - tmp_surface->GetScaledRect(), - SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle, - draw_framebuffer.handle); - - remove_surfaces.emplace(tmp_surface); - return tmp_surface; + u32 min_width = info.width >> max_level; + u32 min_height = info.height >> max_level; + if (min_width % 8 != 0 || min_height % 8 != 0) { + LOG_CRITICAL(Render_OpenGL, "Texture size ({}x{}) is not multiple of 8", min_width, + min_height); + return nullptr; + } + if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) { + LOG_CRITICAL(Render_OpenGL, + "Texture size ({}x{}) does not support required mipmap level ({})", + params.width, params.height, max_level); + return nullptr; } - return GetSurface(params, ScaleMatch::Ignore, true); + auto surface = GetSurface(params, ScaleMatch::Ignore, true); + + // Update mipmap if necessary + if (max_level != 0) { + if (max_level >= 8) { + // since PICA only supports texture size between 8 and 1024, there are at most eight + // possible mipmap levels including the base. + LOG_CRITICAL(Render_OpenGL, "Unsupported mipmap level {}", max_level); + return nullptr; + } + OpenGLState prev_state = OpenGLState::GetCurState(); + OpenGLState state; + SCOPE_EXIT({ prev_state.Apply(); }); + auto format_tuple = GetFormatTuple(params.pixel_format); + + // Allocate more mipmap level if necessary + if (surface->max_level < max_level) { + state.texture_units[0].texture_2d = surface->texture.handle; + state.Apply(); + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, max_level); + u32 width = surface->width * surface->res_scale; + u32 height = surface->height * surface->res_scale; + for (u32 level = surface->max_level + 1; level <= max_level; ++level) { + glTexImage2D(GL_TEXTURE_2D, level, format_tuple.internal_format, width >> level, + height >> level, 0, format_tuple.format, format_tuple.type, nullptr); + } + surface->max_level = max_level; + } + + // Blit mipmaps that have been invalidated + state.draw.read_framebuffer = read_framebuffer.handle; + state.draw.draw_framebuffer = draw_framebuffer.handle; + state.ResetTexture(surface->texture.handle); + SurfaceParams params = *surface; + for (u32 level = 1; level <= max_level; ++level) { + // In PICA all mipmap levels are stored next to each other + params.addr += params.width * params.height * params.GetFormatBpp() / 8; + params.width /= 2; + params.height /= 2; + params.stride = 0; // reset stride and let UpdateParams re-initialize it + params.UpdateParams(); + auto& watcher = surface->level_watchers[level - 1]; + if (!watcher || !watcher->Get()) { + auto level_surface = GetSurface(params, ScaleMatch::Ignore, true); + if (level_surface) { + watcher = level_surface->CreateWatcher(); + } else { + watcher = nullptr; + } + } + + if (watcher && !watcher->IsValid()) { + auto level_surface = watcher->Get(); + state.ResetTexture(level_surface->texture.handle); + state.Apply(); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + level_surface->texture.handle, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + surface->texture.handle, level); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + + auto src_rect = level_surface->GetScaledRect(); + auto dst_rect = params.GetScaledRect(); + 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, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + watcher->Validate(); + } + } + } + + return surface; } const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCubeConfig& config) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 555e9078b..f3dbdd8dd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -356,6 +356,11 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this, 7> level_watchers; + static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type return format == PixelFormat::Invalid @@ -434,7 +439,7 @@ public: /// Get a surface based on the texture configuration Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); - Surface GetTextureSurface(const Pica::Texture::TextureInfo& info); + Surface GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level = 0); /// Get a texture cube based on the texture configuration const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config); diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index 0bdc63d4a..c7cda0ba9 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -29,33 +29,40 @@ using GLivec4 = std::array; namespace PicaToGL { -inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) { - static constexpr std::array filter_mode_table{{ - GL_NEAREST, // TextureFilter::Nearest - GL_LINEAR, // TextureFilter::Linear - }}; - - const auto index = static_cast(mode); - - // Range check table for input - if (index >= filter_mode_table.size()) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index); - UNREACHABLE(); +using TextureFilter = Pica::TexturingRegs::TextureConfig::TextureFilter; +inline GLenum TextureMagFilterMode(TextureFilter mode) { + if (mode == TextureFilter::Linear) { return GL_LINEAR; } - - GLenum gl_mode = filter_mode_table[index]; - - // Check for dummy values indicating an unknown mode - if (gl_mode == 0) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index); - UNIMPLEMENTED(); - - return GL_LINEAR; + if (mode == TextureFilter::Nearest) { + return GL_NEAREST; } + LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", static_cast(mode)); + UNIMPLEMENTED(); + return GL_LINEAR; +} - return gl_mode; +inline GLenum TextureMinFilterMode(TextureFilter min, TextureFilter mip) { + if (min == TextureFilter::Linear) { + if (mip == TextureFilter::Linear) { + return GL_LINEAR_MIPMAP_LINEAR; + } + if (mip == TextureFilter::Nearest) { + return GL_LINEAR_MIPMAP_NEAREST; + } + } else if (min == TextureFilter::Nearest) { + if (mip == TextureFilter::Linear) { + return GL_NEAREST_MIPMAP_LINEAR; + } + if (mip == TextureFilter::Nearest) { + return GL_NEAREST_MIPMAP_NEAREST; + } + } + LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {} and {}", static_cast(min), + static_cast(mip)); + UNIMPLEMENTED(); + return GL_LINEAR_MIPMAP_LINEAR; } inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { From fa141c799b47adaae8e34196cdf8e3550513ac4b Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 6 Jul 2018 00:59:43 +0300 Subject: [PATCH 2/7] gl_shader_gen: use accurate LOD formula for texture 2D --- .../renderer_opengl/gl_shader_gen.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 3ea883038..1e3b9f9c8 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -317,8 +317,9 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un // Only unit 0 respects the texturing type switch (state.texture0_type) { case TexturingRegs::TextureConfig::Texture2D: - return "texture(tex0, texcoord0)"; + return "textureLod(tex0, texcoord0, getLod(texcoord0 * textureSize(tex0, 0)))"; case TexturingRegs::TextureConfig::Projection2D: + // TODO (wwylele): find the exact LOD formula for projection texture return "textureProj(tex0, vec3(texcoord0, texcoord0_w))"; case TexturingRegs::TextureConfig::TextureCube: return "texture(tex_cube, vec3(texcoord0, texcoord0_w))"; @@ -335,12 +336,12 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un return "texture(tex0, texcoord0)"; } case 1: - return "texture(tex1, texcoord1)"; + return "textureLod(tex1, texcoord1, getLod(texcoord1 * textureSize(tex1, 0)))"; case 2: if (state.texture2_use_coord1) - return "texture(tex2, texcoord1)"; + return "textureLod(tex2, texcoord1, getLod(texcoord1 * textureSize(tex2, 0)))"; else - return "texture(tex2, texcoord2)"; + return "textureLod(tex2, texcoord2, getLod(texcoord2 * textureSize(tex2, 0)))"; case 3: if (state.proctex.enable) { return "ProcTex()"; @@ -1333,6 +1334,15 @@ vec4 byteround(vec4 x) { return round(x * 255.0) * (1.0 / 255.0); } +// PICA's LOD formula for 2D textures. +// This LOD formula is the same as the LOD lower limit defined in OpenGL. +// f(x, y) >= max{m_u, m_v, m_w} +// (See OpenGL 4.6 spec, 8.14.1 - Scale Factor and Level-of-Detail) +float getLod(vec2 coord) { + vec2 d = max(abs(dFdx(coord)), abs(dFdy(coord))); + return log2(max(d.x, d.y)); +} + #if ALLOW_SHADOW uvec2 DecodeShadow(uint pixel) { From d7196b5573c625ca24b75e617f15d76f0f62c94e Mon Sep 17 00:00:00 2001 From: wwylele Date: Mon, 9 Jul 2018 21:19:14 +0300 Subject: [PATCH 3/7] gl_rasterizer_cache: invalidate watchers on (partial) surface invalidation --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 5f7ceecaa..932a8ad9d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1807,6 +1807,7 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface const auto interval = cached_surface->GetInterval() & invalid_interval; cached_surface->invalid_regions.insert(interval); + cached_surface->InvalidateAllWatcher(); // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures if (cached_surface->type == SurfaceType::Fill && From 777af04f4ae1e08f544d50b6f45f40f25c9f61b3 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 22 Jul 2018 17:31:02 +0300 Subject: [PATCH 4/7] gl_rasterizer: ignore mipmap setting for cubemap before we implements it --- src/video_core/renderer_opengl/gl_rasterizer.cpp | 15 +++++++++++++++ src/video_core/renderer_opengl/gl_rasterizer.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 475d2a88b..8730d1fe9 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -1583,6 +1583,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( PicaToGL::TextureMinFilterMode(min_filter, mip_filter)); } + // TODO(wwylele): remove this block once mipmap for cube is implemented + bool new_supress_mipmap_for_cube = + config.type == Pica::TexturingRegs::TextureConfig::TextureCube; + if (supress_mipmap_for_cube != new_supress_mipmap_for_cube) { + supress_mipmap_for_cube = new_supress_mipmap_for_cube; + if (new_supress_mipmap_for_cube) { + // HACK: use mag filter converter for min filter because they are the same anyway + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, + PicaToGL::TextureMagFilterMode(min_filter)); + } else { + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, + PicaToGL::TextureMinFilterMode(min_filter, mip_filter)); + } + } + if (wrap_s != config.wrap_s) { wrap_s = config.wrap_s; glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s)); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index f890c1049..c225d62b7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -76,6 +76,9 @@ private: u32 lod_min; u32 lod_max; s32 lod_bias; + + // TODO(wwylele): remove this once mipmap for cube is implemented + bool supress_mipmap_for_cube = false; }; /// Structure that the hardware rendered vertices are composed of From ebdef4fd6949a6e1e070575aa0ef73ac8164a076 Mon Sep 17 00:00:00 2001 From: wwylele Date: Tue, 24 Jul 2018 23:18:36 +0300 Subject: [PATCH 5/7] gl_rasterizer_cache: unlink watchers if surface is moved to remove_surfaces but is not immediately removed --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 1 + src/video_core/renderer_opengl/gl_rasterizer_cache.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 932a8ad9d..66fbf2322 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1300,6 +1300,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& // Delete the expanded surface, this can't be done safely yet // because it may still be in use + surface->UnlinkAllWatcher(); // unlink watchers as if this surface is already deleted remove_surfaces.emplace(surface); surface = new_surface; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index f3dbdd8dd..962cbceb6 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -397,6 +397,16 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_thisvalid = false; + locked->surface.reset(); + } + } + watchers.clear(); + } + private: std::list> watchers; }; From 88a011ec8efba5634a827988272dcb67bb639cfa Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Sun, 10 Mar 2019 11:02:56 -0400 Subject: [PATCH 6/7] GetTextureSurface: return on invalid physical address early Previously this check is in GetSurface (if (addr == 0)). This worked fine because GetTextureSurface directly forwarded the address value to GetSurface. However, now with mipmap support, GetTextureSurface would call GetSurface several times with different address offset, resulting some >0 but still invalid address in case the input is 0. We should error out early on invalid address instead of sending it furthor down which would cause invalid memory access --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 66fbf2322..13e318e04 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1332,6 +1332,10 @@ Surface RasterizerCacheOpenGL::GetTextureSurface( Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInfo& info, u32 max_level) { + if (info.physical_address == 0) { + return nullptr; + } + SurfaceParams params; params.addr = info.physical_address; params.width = info.width; From e3b6bf93bc506181afee8810f7897b0d4aadbf52 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Mon, 15 Apr 2019 09:07:36 -0400 Subject: [PATCH 7/7] gl_rasterizer_cache: validate surface in mipmap/cubemap if the children is not validated yet --- src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 13e318e04..292c50f7c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1412,6 +1412,9 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf if (watcher && !watcher->IsValid()) { auto level_surface = watcher->Get(); + if (!level_surface->invalid_regions.empty()) { + ValidateSurface(level_surface, level_surface->addr, level_surface->size); + } state.ResetTexture(level_surface->texture.handle); state.Apply(); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, @@ -1504,6 +1507,9 @@ const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCube for (const Face& face : faces) { if (face.watcher && !face.watcher->IsValid()) { auto surface = face.watcher->Get(); + if (!surface->invalid_regions.empty()) { + ValidateSurface(surface, surface->addr, surface->size); + } state.ResetTexture(surface->texture.handle); state.Apply(); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,