mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #3910 from wwylele/mipmap
gl_rasterizer: implement mipmap by forwarding PICA mipmap configuration
This commit is contained in:
		
						commit
						0ec45f694c
					
				
					 7 changed files with 217 additions and 51 deletions
				
			
		|  | @ -59,11 +59,16 @@ struct TexturingRegs { | ||||||
|             BitField<2, 1, TextureFilter> min_filter; |             BitField<2, 1, TextureFilter> min_filter; | ||||||
|             BitField<8, 3, WrapMode> wrap_t; |             BitField<8, 3, WrapMode> wrap_t; | ||||||
|             BitField<12, 3, WrapMode> wrap_s; |             BitField<12, 3, WrapMode> wrap_s; | ||||||
|  |             BitField<24, 1, TextureFilter> mip_filter; | ||||||
|             /// @note Only valid for texture 0 according to 3DBrew.
 |             /// @note Only valid for texture 0 according to 3DBrew.
 | ||||||
|             BitField<28, 3, TextureType> type; |             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; |         BitField<0, 28, u32> address; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1555,13 +1555,16 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::SamplerInfo::Create() { | void RasterizerOpenGL::SamplerInfo::Create() { | ||||||
|     sampler.Create(); |     sampler.Create(); | ||||||
|     mag_filter = min_filter = TextureConfig::Linear; |     mag_filter = min_filter = mip_filter = TextureConfig::Linear; | ||||||
|     wrap_s = wrap_t = TextureConfig::Repeat; |     wrap_s = wrap_t = TextureConfig::Repeat; | ||||||
|     border_color = 0; |     border_color = 0; | ||||||
|  |     lod_min = lod_max = 0; | ||||||
|  |     lod_bias = 0; | ||||||
| 
 | 
 | ||||||
|     // default is GL_LINEAR_MIPMAP_LINEAR
 |     // default is 1000 and -1000
 | ||||||
|     glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |  | ||||||
|     // Other attributes have correct defaults
 |     // 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( | void RasterizerOpenGL::SamplerInfo::SyncWithConfig( | ||||||
|  | @ -1571,11 +1574,28 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( | ||||||
| 
 | 
 | ||||||
|     if (mag_filter != config.mag_filter) { |     if (mag_filter != config.mag_filter) { | ||||||
|         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; |         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)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // 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) { |     if (wrap_s != config.wrap_s) { | ||||||
|  | @ -1594,6 +1614,21 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig( | ||||||
|             glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data()); |             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() { | void RasterizerOpenGL::SetShader() { | ||||||
|  |  | ||||||
|  | @ -69,9 +69,16 @@ private: | ||||||
|     private: |     private: | ||||||
|         TextureConfig::TextureFilter mag_filter; |         TextureConfig::TextureFilter mag_filter; | ||||||
|         TextureConfig::TextureFilter min_filter; |         TextureConfig::TextureFilter min_filter; | ||||||
|  |         TextureConfig::TextureFilter mip_filter; | ||||||
|         TextureConfig::WrapMode wrap_s; |         TextureConfig::WrapMode wrap_s; | ||||||
|         TextureConfig::WrapMode wrap_t; |         TextureConfig::WrapMode wrap_t; | ||||||
|         u32 border_color; |         u32 border_color; | ||||||
|  |         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
 |     /// Structure that the hardware rendered vertices are composed of
 | ||||||
|  |  | ||||||
|  | @ -1332,6 +1332,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& | ||||||
| 
 | 
 | ||||||
|             // Delete the expanded surface, this can't be done safely yet
 |             // Delete the expanded surface, this can't be done safely yet
 | ||||||
|             // because it may still be in use
 |             // because it may still be in use
 | ||||||
|  |             surface->UnlinkAllWatcher(); // unlink watchers as if this surface is already deleted
 | ||||||
|             remove_surfaces.emplace(surface); |             remove_surfaces.emplace(surface); | ||||||
| 
 | 
 | ||||||
|             surface = new_surface; |             surface = new_surface; | ||||||
|  | @ -1358,10 +1359,15 @@ Surface RasterizerCacheOpenGL::GetTextureSurface( | ||||||
|     const Pica::TexturingRegs::FullTextureConfig& config) { |     const Pica::TexturingRegs::FullTextureConfig& config) { | ||||||
|     Pica::Texture::TextureInfo info = |     Pica::Texture::TextureInfo info = | ||||||
|         Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); |         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) { | ||||||
|  |     if (info.physical_address == 0) { | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     SurfaceParams params; |     SurfaceParams params; | ||||||
|     params.addr = info.physical_address; |     params.addr = info.physical_address; | ||||||
|     params.width = info.width; |     params.width = info.width; | ||||||
|  | @ -1370,23 +1376,100 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Pica::Texture::TextureInf | ||||||
|     params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); |     params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format); | ||||||
|     params.UpdateParams(); |     params.UpdateParams(); | ||||||
| 
 | 
 | ||||||
|     if (info.width % 8 != 0 || info.height % 8 != 0) { |     u32 min_width = info.width >> max_level; | ||||||
|         Surface src_surface; |     u32 min_height = info.height >> max_level; | ||||||
|         Common::Rectangle<u32> rect; |     if (min_width % 8 != 0 || min_height % 8 != 0) { | ||||||
|         std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true); |         LOG_CRITICAL(Render_OpenGL, "Texture size ({}x{}) is not multiple of 8", min_width, | ||||||
| 
 |                      min_height); | ||||||
|         params.res_scale = src_surface->res_scale; |         return nullptr; | ||||||
|         Surface tmp_surface = CreateSurface(params); |     } | ||||||
|         BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, |     if (info.width != (min_width << max_level) || info.height != (min_height << max_level)) { | ||||||
|                      tmp_surface->GetScaledRect(), |         LOG_CRITICAL(Render_OpenGL, | ||||||
|                      SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle, |                      "Texture size ({}x{}) does not support required mipmap level ({})", | ||||||
|                      draw_framebuffer.handle); |                      params.width, params.height, max_level); | ||||||
| 
 |         return nullptr; | ||||||
|         remove_surfaces.emplace(tmp_surface); |  | ||||||
|         return tmp_surface; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     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(); | ||||||
|  |                 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, | ||||||
|  |                                        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) { | const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCubeConfig& config) { | ||||||
|  | @ -1456,6 +1539,9 @@ const CachedTextureCube& RasterizerCacheOpenGL::GetTextureCube(const TextureCube | ||||||
|     for (const Face& face : faces) { |     for (const Face& face : faces) { | ||||||
|         if (face.watcher && !face.watcher->IsValid()) { |         if (face.watcher && !face.watcher->IsValid()) { | ||||||
|             auto surface = face.watcher->Get(); |             auto surface = face.watcher->Get(); | ||||||
|  |             if (!surface->invalid_regions.empty()) { | ||||||
|  |                 ValidateSurface(surface, surface->addr, surface->size); | ||||||
|  |             } | ||||||
|             state.ResetTexture(surface->texture.handle); |             state.ResetTexture(surface->texture.handle); | ||||||
|             state.Apply(); |             state.Apply(); | ||||||
|             glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |             glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, | ||||||
|  | @ -1764,6 +1850,7 @@ void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface | ||||||
| 
 | 
 | ||||||
|             const auto interval = cached_surface->GetInterval() & invalid_interval; |             const auto interval = cached_surface->GetInterval() & invalid_interval; | ||||||
|             cached_surface->invalid_regions.insert(interval); |             cached_surface->invalid_regions.insert(interval); | ||||||
|  |             cached_surface->InvalidateAllWatcher(); | ||||||
| 
 | 
 | ||||||
|             // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
 |             // Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
 | ||||||
|             if (cached_surface->type == SurfaceType::Fill && |             if (cached_surface->type == SurfaceType::Fill && | ||||||
|  |  | ||||||
|  | @ -356,6 +356,11 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface | ||||||
| 
 | 
 | ||||||
|     OGLTexture texture; |     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) { |     static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) { | ||||||
|         // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
 |         // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
 | ||||||
|         return format == PixelFormat::Invalid |         return format == PixelFormat::Invalid | ||||||
|  | @ -392,6 +397,16 @@ struct CachedSurface : SurfaceParams, std::enable_shared_from_this<CachedSurface | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void UnlinkAllWatcher() { | ||||||
|  |         for (const auto& watcher : watchers) { | ||||||
|  |             if (auto locked = watcher.lock()) { | ||||||
|  |                 locked->valid = false; | ||||||
|  |                 locked->surface.reset(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         watchers.clear(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     std::list<std::weak_ptr<SurfaceWatcher>> watchers; |     std::list<std::weak_ptr<SurfaceWatcher>> watchers; | ||||||
| }; | }; | ||||||
|  | @ -434,7 +449,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     /// Get a surface based on the texture configuration
 |     /// Get a surface based on the texture configuration
 | ||||||
|     Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); |     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
 |     /// Get a texture cube based on the texture configuration
 | ||||||
|     const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config); |     const CachedTextureCube& GetTextureCube(const TextureCubeConfig& config); | ||||||
|  |  | ||||||
|  | @ -317,8 +317,9 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un | ||||||
|         // Only unit 0 respects the texturing type
 |         // Only unit 0 respects the texturing type
 | ||||||
|         switch (state.texture0_type) { |         switch (state.texture0_type) { | ||||||
|         case TexturingRegs::TextureConfig::Texture2D: |         case TexturingRegs::TextureConfig::Texture2D: | ||||||
|             return "texture(tex0, texcoord0)"; |             return "textureLod(tex0, texcoord0, getLod(texcoord0 * textureSize(tex0, 0)))"; | ||||||
|         case TexturingRegs::TextureConfig::Projection2D: |         case TexturingRegs::TextureConfig::Projection2D: | ||||||
|  |             // TODO (wwylele): find the exact LOD formula for projection texture
 | ||||||
|             return "textureProj(tex0, vec3(texcoord0, texcoord0_w))"; |             return "textureProj(tex0, vec3(texcoord0, texcoord0_w))"; | ||||||
|         case TexturingRegs::TextureConfig::TextureCube: |         case TexturingRegs::TextureConfig::TextureCube: | ||||||
|             return "texture(tex_cube, vec3(texcoord0, texcoord0_w))"; |             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)"; |             return "texture(tex0, texcoord0)"; | ||||||
|         } |         } | ||||||
|     case 1: |     case 1: | ||||||
|         return "texture(tex1, texcoord1)"; |         return "textureLod(tex1, texcoord1, getLod(texcoord1 * textureSize(tex1, 0)))"; | ||||||
|     case 2: |     case 2: | ||||||
|         if (state.texture2_use_coord1) |         if (state.texture2_use_coord1) | ||||||
|             return "texture(tex2, texcoord1)"; |             return "textureLod(tex2, texcoord1, getLod(texcoord1 * textureSize(tex2, 0)))"; | ||||||
|         else |         else | ||||||
|             return "texture(tex2, texcoord2)"; |             return "textureLod(tex2, texcoord2, getLod(texcoord2 * textureSize(tex2, 0)))"; | ||||||
|     case 3: |     case 3: | ||||||
|         if (state.proctex.enable) { |         if (state.proctex.enable) { | ||||||
|             return "ProcTex()"; |             return "ProcTex()"; | ||||||
|  | @ -1333,6 +1334,15 @@ vec4 byteround(vec4 x) { | ||||||
|     return round(x * 255.0) * (1.0 / 255.0); |     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 | #if ALLOW_SHADOW | ||||||
| 
 | 
 | ||||||
| uvec2 DecodeShadow(uint pixel) { | uvec2 DecodeShadow(uint pixel) { | ||||||
|  |  | ||||||
|  | @ -29,33 +29,40 @@ using GLivec4 = std::array<GLint, 4>; | ||||||
| 
 | 
 | ||||||
| namespace PicaToGL { | namespace PicaToGL { | ||||||
| 
 | 
 | ||||||
| inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) { | using TextureFilter = Pica::TexturingRegs::TextureConfig::TextureFilter; | ||||||
|     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(); |  | ||||||
| 
 | 
 | ||||||
|  | inline GLenum TextureMagFilterMode(TextureFilter mode) { | ||||||
|  |     if (mode == TextureFilter::Linear) { | ||||||
|         return GL_LINEAR; |         return GL_LINEAR; | ||||||
|     } |     } | ||||||
| 
 |     if (mode == TextureFilter::Nearest) { | ||||||
|     GLenum gl_mode = filter_mode_table[index]; |         return GL_NEAREST; | ||||||
| 
 |     } | ||||||
|     // Check for dummy values indicating an unknown mode
 |     LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", static_cast<u32>(mode)); | ||||||
|     if (gl_mode == 0) { |  | ||||||
|         LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode {}", index); |  | ||||||
|     UNIMPLEMENTED(); |     UNIMPLEMENTED(); | ||||||
| 
 |  | ||||||
|     return GL_LINEAR; |     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) { | inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue