diff --git a/src/Textures.cpp b/src/Textures.cpp index 0685deea..f3a025e4 100644 --- a/src/Textures.cpp +++ b/src/Textures.cpp @@ -1077,34 +1077,65 @@ void TextureCache::_getTextureDestData(CachedTexture& tmptex, } } +template +void doubleTexture(T* pTex, u32 width, u32 height) +{ + std::vector vData(width * height); + memcpy(vData.data(), pTex, width * height * sizeof(T)); + u32 srcIdx = 0; + u32 dstIdx = 0; + for (u32 y = 0; y < height; ++y) { + const u32 srcIdxCur = srcIdx; + for (u32 x = 0; x < width; ++x) { + pTex[dstIdx++] = vData[srcIdx]; + pTex[dstIdx++] = vData[srcIdx++]; + } + srcIdx = srcIdxCur; + for (u32 x = 0; x < width; ++x) { + pTex[dstIdx++] = vData[srcIdx]; + pTex[dstIdx++] = vData[srcIdx++]; + } + } +} + void TextureCache::_load(u32 _tile, CachedTexture *_pTexture) { u64 ricecrc = 0; if (_loadHiresTexture(_tile, _pTexture, ricecrc)) return; - u16 line; - GetTexelFunc GetTexel; - InternalColorFormatParam glInternalFormat; - DatatypeParam glType; - u32 sizeShift; + s32 mipLevel = 0; + bool force32bitFormat = false; + _pTexture->max_level = 0; - const TextureLoadParameters & loadParams = - ImageFormat::get().tlp[gDP.otherMode.textureLUT][_pTexture->size][_pTexture->format]; - if (loadParams.autoFormat == internalcolorFormat::RGBA8) { - sizeShift = 2; - _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift; - GetTexel = loadParams.Get32; - glInternalFormat = loadParams.glInternalFormat32; - glType = loadParams.glType32; - } else { - sizeShift = 1; - _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift; - GetTexel = loadParams.Get16; - glInternalFormat = loadParams.glInternalFormat16; - glType = loadParams.glType16; + if (config.generalEmulation.enableLOD != 0 && gSP.texture.level > 1) { + if (_tile == 0) { + _pTexture->max_level = 0; + } else { + _pTexture->max_level = static_cast(gSP.texture.level - 1); + const u16 dim = std::max(_pTexture->width, _pTexture->height); + while (dim < static_cast(1 << _pTexture->max_level)) + --_pTexture->max_level; + + auto texFormat = gDP.tiles[gSP.texture.tile + 1].format; + auto texSize = gDP.tiles[gSP.texture.tile + 1].size; + u32 tileMipLevel = gSP.texture.tile + 2; + while (!force32bitFormat && (tileMipLevel < gSP.texture.tile + gSP.texture.level)) { + gDPTile const& mipTile = gDP.tiles[tileMipLevel++]; + force32bitFormat = texFormat != mipTile.format || texSize != mipTile.size; + } + } } + u32 sizeShift = 1; + { + const TextureLoadParameters & loadParams = + ImageFormat::get().tlp[gDP.otherMode.textureLUT][_pTexture->size][_pTexture->format]; + if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) + sizeShift = 2; + } + _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift; + // RAII holder for texture data class TexData { @@ -1127,29 +1158,54 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture) u32 *pData = NULL; } texData(_pTexture->textureBytes); + GetTexelFunc GetTexel; + InternalColorFormatParam glInternalFormat; + DatatypeParam glType; - s32 mipLevel = 0; - _pTexture->max_level = 0; - - if (config.generalEmulation.enableLOD != 0 && gSP.texture.level > 1) { - if (_tile == 0) { - _pTexture->max_level = 0; - } else { - _pTexture->max_level = static_cast(gSP.texture.level - 1); - const u16 dim = std::max(_pTexture->width, _pTexture->height); - while (dim < static_cast(1 << _pTexture->max_level)) - --_pTexture->max_level; + auto getLoadParams = [&](u16 _format, u16 _size) + { + const TextureLoadParameters & loadParams = + ImageFormat::get().tlp[gDP.otherMode.textureLUT][_size][_format]; + if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) { + GetTexel = loadParams.Get32; + glInternalFormat = loadParams.glInternalFormat32; + glType = loadParams.glType32; } - } + else { + GetTexel = loadParams.Get16; + glInternalFormat = loadParams.glInternalFormat16; + glType = loadParams.glType16; + } + }; - ObjectHandle name; - CachedTexture tmptex(name); - memcpy(&tmptex, _pTexture, sizeof(CachedTexture)); - - line = tmptex.line; + CachedTexture tmptex = *_pTexture; + u16 line = tmptex.line; while (true) { - _getTextureDestData(tmptex, texData.get(), glInternalFormat, GetTexel, &line); + getLoadParams(tmptex.format, tmptex.size); + { + const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1; + gDPTile & mipTile = gDP.tiles[tileMipLevel]; + if (tmptex.max_level > 1 && + tmptex.width == (mipTile.lrs - mipTile.uls + 1) * 2 && + tmptex.height == (mipTile.lrt - mipTile.ult + 1) * 2) + { + // Special case for Southern Swamp grass texture, Zelda MM. See #2315 + const u16 texWidth = tmptex.width; + const u16 texHeight = tmptex.height; + tmptex.width = mipTile.lrs - mipTile.uls + 1; + tmptex.height = mipTile.lrt - mipTile.ult + 1; + _getTextureDestData(tmptex, texData.get(), glInternalFormat, GetTexel, &line); + if (sizeShift == 2) + doubleTexture(texData.get(), tmptex.width, tmptex.height); + else + doubleTexture((u16*)texData.get(), tmptex.width, tmptex.height); + tmptex.width = texWidth; + tmptex.height = texHeight; + } else { + _getTextureDestData(tmptex, texData.get(), glInternalFormat, GetTexel, &line); + } + } if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) { _loadDepthTexture(_pTexture, (u16*)texData.get()); @@ -1239,6 +1295,8 @@ void TextureCache::_load(u32 _tile, CachedTexture *_pTexture) tmptex.palette = mipTile.palette; tmptex.maskS = mipTile.masks; tmptex.maskT = mipTile.maskt; + tmptex.format = mipTile.format; + tmptex.size = mipTile.size; TileSizes sizes; _calcTileSizes(tileMipLevel, sizes, nullptr); tmptex.clampWidth = sizes.clampWidth; @@ -1529,6 +1587,7 @@ void TextureCache::update(u32 _t) TileSizes sizes; _calcTileSizes(_t, sizes, gDP.loadTile); TextureParams params; + const u32 texLevel = _t == 0 ? 0U : gSP.texture.level; params.flags = pTile->masks | (pTile->maskt << 4) | (pTile->mirrors << 8) | @@ -1537,7 +1596,8 @@ void TextureCache::update(u32 _t) (pTile->clampt << 11) | (pTile->size << 12) | (pTile->format << 14) | - (gDP.otherMode.textureLUT << 17); + (gDP.otherMode.textureLUT << 17) | + (texLevel << 19); params.width = sizes.width; params.height = sizes.height;