mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-03 23:28:48 +00:00 
			
		
		
		
	gl_rasterizer: implement mipmap
This commit is contained in:
		
							parent
							
								
									acaca4188e
								
							
						
					
					
						commit
						ca78d34933
					
				
					 6 changed files with 163 additions and 47 deletions
				
			
		| 
						 | 
				
			
			@ -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() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<u32> 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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -356,6 +356,11 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface
 | 
			
		|||
 | 
			
		||||
    OGLTexture texture;
 | 
			
		||||
 | 
			
		||||
    /// max mipmap level that has been attached to the texture
 | 
			
		||||
    u32 max_level = 0;
 | 
			
		||||
    /// level_watchers[i] watches the (i+1)-th level mipmap source surface
 | 
			
		||||
    std::array<std::shared_ptr<SurfaceWatcher>, 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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,33 +29,40 @@ using GLivec4 = std::array<GLint, 4>;
 | 
			
		|||
 | 
			
		||||
namespace PicaToGL {
 | 
			
		||||
 | 
			
		||||
inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) {
 | 
			
		||||
    static constexpr std::array<GLenum, 2> filter_mode_table{{
 | 
			
		||||
        GL_NEAREST, // TextureFilter::Nearest
 | 
			
		||||
        GL_LINEAR,  // TextureFilter::Linear
 | 
			
		||||
    }};
 | 
			
		||||
 | 
			
		||||
    const auto index = static_cast<std::size_t>(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<u32>(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<u32>(min),
 | 
			
		||||
                 static_cast<u32>(mip));
 | 
			
		||||
    UNIMPLEMENTED();
 | 
			
		||||
    return GL_LINEAR_MIPMAP_LINEAR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue