void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBase** entry, u32 new_width, u32 new_height) { if ((*entry)->config.width == new_width && (*entry)->config.height == new_height) { return; } u32 max = g_renderer->GetMaxTextureSize(); if (max < new_width || max < new_height) { ERROR_LOG(VIDEO, "Texture too big, width = %d, height = %d", new_width, new_height); return; } TextureCacheBase::TCacheEntryConfig newconfig; newconfig.width = new_width; newconfig.height = new_height; newconfig.layers = (*entry)->config.layers; newconfig.rendertarget = true; TCacheEntryBase* newentry = AllocateTexture(newconfig); if (newentry) { newentry->SetGeneralParameters((*entry)->addr, (*entry)->size_in_bytes, (*entry)->format); newentry->SetDimensions((*entry)->native_width, (*entry)->native_height, 1); newentry->SetHashes((*entry)->base_hash, (*entry)->hash); newentry->frameCount = frameCount; newentry->is_efb_copy = (*entry)->is_efb_copy; MathUtil::Rectangle<int> srcrect, dstrect; srcrect.left = 0; srcrect.top = 0; srcrect.right = (*entry)->config.width; srcrect.bottom = (*entry)->config.height; dstrect.left = 0; dstrect.top = 0; dstrect.right = new_width; dstrect.bottom = new_height; newentry->CopyRectangleFromTexture(*entry, srcrect, dstrect); // Keep track of the pointer for textures_by_hash if ((*entry)->textures_by_hash_iter != textures_by_hash.end()) { newentry->textures_by_hash_iter = textures_by_hash.emplace((*entry)->hash, newentry); } InvalidateTexture(GetTexCacheIter(*entry)); *entry = newentry; textures_by_address.emplace((*entry)->addr, *entry); } else { ERROR_LOG(VIDEO, "Scaling failed"); } }
TextureCache::TCacheEntryBase* TextureCache::DoPartialTextureUpdates(TexCache::iterator iter_t) { TCacheEntryBase* entry_to_update = iter_t->second; const bool isPaletteTexture = (entry_to_update->format == GX_TF_C4 || entry_to_update->format == GX_TF_C8 || entry_to_update->format == GX_TF_C14X2 || entry_to_update->format >= 0x10000); // Efb copies and paletted textures are excluded from these updates, until there's an example where a game would // benefit from this. Both would require more work to be done. // TODO: Implement upscaling support for normal textures, and then remove the efb to ram and the scaled efb restrictions if (entry_to_update->IsEfbCopy() || isPaletteTexture) return entry_to_update; u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format); u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format); u32 block_size = block_width * block_height * TexDecoder_GetTexelSizeInNibbles(entry_to_update->format) / 2; u32 numBlocksX = (entry_to_update->native_width + block_width - 1) / block_width; TexCache::iterator iter = textures_by_address.lower_bound(entry_to_update->addr); TexCache::iterator iterend = textures_by_address.upper_bound(entry_to_update->addr + entry_to_update->size_in_bytes); bool entry_need_scaling = true; while (iter != iterend) { TCacheEntryBase* entry = iter->second; if (entry != entry_to_update && entry->IsEfbCopy() && entry_to_update->addr <= entry->addr && entry->addr + entry->size_in_bytes <= entry_to_update->addr + entry_to_update->size_in_bytes && entry->frameCount == FRAMECOUNT_INVALID && entry->memory_stride == numBlocksX * block_size) { u32 block_offset = (entry->addr - entry_to_update->addr) / block_size; u32 block_x = block_offset % numBlocksX; u32 block_y = block_offset / numBlocksX; u32 x = block_x * block_width; u32 y = block_y * block_height; MathUtil::Rectangle<int> srcrect, dstrect; srcrect.left = 0; srcrect.top = 0; dstrect.left = 0; dstrect.top = 0; if (entry_need_scaling) { entry_need_scaling = false; u32 w = entry_to_update->native_width * entry->config.width / entry->native_width; u32 h = entry_to_update->native_height * entry->config.height / entry->native_height; u32 max = g_renderer->GetMaxTextureSize(); if (max < w || max < h) { iter++; continue; } if (entry_to_update->config.width != w || entry_to_update->config.height != h) { TextureCache::TCacheEntryConfig newconfig; newconfig.width = w; newconfig.height = h; newconfig.rendertarget = true; TCacheEntryBase* newentry = AllocateTexture(newconfig); newentry->SetGeneralParameters(entry_to_update->addr, entry_to_update->size_in_bytes, entry_to_update->format); newentry->SetDimensions(entry_to_update->native_width, entry_to_update->native_height, 1); newentry->SetHashes(entry_to_update->base_hash, entry_to_update->hash); newentry->frameCount = frameCount; newentry->is_efb_copy = false; srcrect.right = entry_to_update->config.width; srcrect.bottom = entry_to_update->config.height; dstrect.right = w; dstrect.bottom = h; newentry->CopyRectangleFromTexture(entry_to_update, srcrect, dstrect); entry_to_update = newentry; u64 key = iter_t->first; iter_t = FreeTexture(iter_t); textures_by_address.emplace(key, entry_to_update); } } srcrect.right = entry->config.width; srcrect.bottom = entry->config.height; dstrect.left = x * entry_to_update->config.width / entry_to_update->native_width; dstrect.top = y * entry_to_update->config.height / entry_to_update->native_height; dstrect.right = (x + entry->native_width) * entry_to_update->config.width / entry_to_update->native_width; dstrect.bottom = (y + entry->native_height) * entry_to_update->config.height / entry_to_update->native_height; entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect); // Mark the texture update as used, so it isn't applied more than once entry->frameCount = frameCount; } ++iter; } return entry_to_update; }