mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	gl_rasterizer: implement mipmap
This commit is contained in:
		
							parent
							
								
									acaca4188e
								
							
						
					
					
						commit
						ca78d34933
					
				
					 6 changed files with 163 additions and 47 deletions
				
			
		|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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