From 8761918a468a3f821160277a42fc92c534a76540 Mon Sep 17 00:00:00 2001 From: mkwong98 Date: Sun, 1 Nov 2020 20:29:12 +0800 Subject: [PATCH] Fix Bug when rendering overlapping HD sprites with different background priority (SourMesen/Mesen issue 609) Fix Bug when rendering overlapping HD sprites with different background priority (SourMesen/Mesen issue 609) --- Core/HdNesPack.cpp | 116 +++++++++++++++++++++++++++++++++++++++++---- Core/HdNesPack.h | 6 ++- 2 files changed, 111 insertions(+), 11 deletions(-) diff --git a/Core/HdNesPack.cpp b/Core/HdNesPack.cpp index 03e6c0e2..f42d4fce 100644 --- a/Core/HdNesPack.cpp +++ b/Core/HdNesPack.cpp @@ -13,6 +13,7 @@ HdNesPack::HdNesPack(shared_ptr hdData, EmulationSettings* settings) { _hdData = hdData; _settings = settings; + spriteMask.resize(hdData->Scale * hdData->Scale); } HdNesPack::~HdNesPack() @@ -50,6 +51,29 @@ void HdNesPack::DrawColor(uint32_t color, uint32_t *outputBuffer, uint32_t scale } } +void HdNesPack::DrawSpriteColor(uint32_t color, uint32_t* outputBuffer, uint32_t scale, uint32_t screenWidth, int spriteID) +{ + if (scale == 1 && spriteID <= spriteMask[0]) { + *outputBuffer = color; + spriteMask[0] = spriteID; + } + else { + + for (uint32_t y = 0; y < scale; y++) { + for (uint32_t x = 0; x < scale; x++) { + vector::iterator it = spriteMask.begin(); + if (spriteID <= *it) { + *it = spriteID; + * outputBuffer = color; + } + it++; + outputBuffer++; + } + outputBuffer += screenWidth - scale; + } + } +} + void HdNesPack::DrawCustomBackground(HdBackgroundInfo& bgInfo, uint32_t *outputBuffer, uint32_t x, uint32_t y, uint32_t scale, uint32_t screenWidth) { int brightness = bgInfo.Brightness; @@ -140,6 +164,80 @@ void HdNesPack::DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo } } +void HdNesPack::DrawSpriteTile(HdPpuTileInfo& tileInfo, HdPackTileInfo& hdPackTileInfo, uint32_t* outputBuffer, uint32_t screenWidth, int spriteID) +{ + if (hdPackTileInfo.IsFullyTransparent) { + return; + } + + uint32_t scale = GetScale(); + uint32_t* bitmapData = hdPackTileInfo.HdTileData.data(); + uint32_t tileWidth = 8 * scale; + uint8_t tileOffsetX = tileInfo.HorizontalMirroring ? 7 - tileInfo.OffsetX : tileInfo.OffsetX; + uint32_t bitmapOffset = (tileInfo.OffsetY * scale) * tileWidth + tileOffsetX * scale; + int32_t bitmapSmallInc = 1; + int32_t bitmapLargeInc = tileWidth - scale; + if (tileInfo.HorizontalMirroring) { + bitmapOffset += scale - 1; + bitmapSmallInc = -1; + bitmapLargeInc = tileWidth + scale; + } + if (tileInfo.VerticalMirroring) { + bitmapOffset += tileWidth * (scale - 1); + bitmapLargeInc = (tileInfo.HorizontalMirroring ? (int32_t)scale : -(int32_t)scale) - (int32_t)tileWidth; + } + + uint32_t rgbValue; + vector::iterator it = spriteMask.begin(); + if (hdPackTileInfo.HasTransparentPixels || hdPackTileInfo.Brightness != 255) { + for (uint32_t y = 0; y < scale; y++) { + for (uint32_t x = 0; x < scale; x++) { + if (spriteID <= *it) { + + + if (hdPackTileInfo.Brightness == 255) { + rgbValue = *(bitmapData + bitmapOffset); + } + else { + rgbValue = AdjustBrightness((uint8_t*)(bitmapData + bitmapOffset), hdPackTileInfo.Brightness); + } + + if (!hdPackTileInfo.HasTransparentPixels || (bitmapData[bitmapOffset] & 0xFF000000) == 0xFF000000) { + *outputBuffer = rgbValue; + *it = spriteID; + } + else { + if (bitmapData[bitmapOffset] & 0xFF000000) { + BlendColors((uint8_t*)outputBuffer, (uint8_t*)& rgbValue); + } + } + } + it++; + outputBuffer++; + bitmapOffset += bitmapSmallInc; + } + bitmapOffset += bitmapLargeInc; + outputBuffer += screenWidth - scale; + } + } + else { + for (uint32_t y = 0; y < scale; y++) { + for (uint32_t x = 0; x < scale; x++) { + if (spriteID <= *it) { + *it = spriteID; + *outputBuffer = *(bitmapData + bitmapOffset); + } + it++; + outputBuffer++; + bitmapOffset += bitmapSmallInc; + } + bitmapOffset += bitmapLargeInc; + outputBuffer += screenWidth - scale; + } + } +} + + uint32_t HdNesPack::GetScale() { return _hdData->Scale; @@ -284,7 +382,9 @@ void HdNesPack::GetPixels(uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uin hdPackTileInfo = GetCachedMatchingTile(x, y, &pixelInfo.Tile); } - int lowestBgSprite = 999; + //init bg sprite depth mask + for (int i = 0; i < spriteMask.size(); i++) spriteMask[i] = 999; + DrawColor(_palette[pixelInfo.Tile.PpuBackgroundColor], outputBuffer, _hdData->Scale, screenWidth); @@ -295,15 +395,11 @@ void HdNesPack::GetPixels(uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uin if(hasSprite) { for(int k = pixelInfo.SpriteCount - 1; k >= 0; k--) { if(pixelInfo.Sprite[k].BackgroundPriority) { - if(pixelInfo.Sprite[k].SpriteColorIndex != 0) { - lowestBgSprite = k; - } - hdPackSpriteInfo = GetMatchingTile(x, y, &pixelInfo.Sprite[k]); if(hdPackSpriteInfo) { - DrawTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth); + DrawSpriteTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth, k); } else if(pixelInfo.Sprite[k].SpriteColorIndex != 0) { - DrawColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, _hdData->Scale, screenWidth); + DrawSpriteColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, _hdData->Scale, screenWidth, k); } } } @@ -328,12 +424,12 @@ void HdNesPack::GetPixels(uint32_t x, uint32_t y, HdPpuPixelInfo &pixelInfo, uin if(hasSprite) { for(int k = pixelInfo.SpriteCount - 1; k >= 0; k--) { - if(!pixelInfo.Sprite[k].BackgroundPriority && lowestBgSprite > k) { + if(!pixelInfo.Sprite[k].BackgroundPriority) { hdPackSpriteInfo = GetMatchingTile(x, y, &pixelInfo.Sprite[k]); if(hdPackSpriteInfo) { - DrawTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth); + DrawSpriteTile(pixelInfo.Sprite[k], *hdPackSpriteInfo, outputBuffer, screenWidth, k); } else if(pixelInfo.Sprite[k].SpriteColorIndex != 0) { - DrawColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, _hdData->Scale, screenWidth); + DrawSpriteColor(_palette[pixelInfo.Sprite[k].SpriteColor], outputBuffer, _hdData->Scale, screenWidth, k); } } } diff --git a/Core/HdNesPack.h b/Core/HdNesPack.h index bbedc6bd..23cced27 100644 --- a/Core/HdNesPack.h +++ b/Core/HdNesPack.h @@ -35,11 +35,15 @@ private: bool _useCachedTile = false; int32_t _scrollX = 0; + vector spriteMask; + __forceinline void BlendColors(uint8_t output[4], uint8_t input[4]); __forceinline uint32_t AdjustBrightness(uint8_t input[4], int brightness); __forceinline void DrawColor(uint32_t color, uint32_t* outputBuffer, uint32_t scale, uint32_t screenWidth); __forceinline void DrawTile(HdPpuTileInfo &tileInfo, HdPackTileInfo &hdPackTileInfo, uint32_t* outputBuffer, uint32_t screenWidth); - + __forceinline void DrawSpriteTile(HdPpuTileInfo& tileInfo, HdPackTileInfo& hdPackTileInfo, uint32_t* outputBuffer, uint32_t screenWidth, int spriteID); + __forceinline void DrawSpriteColor(uint32_t color, uint32_t* outputBuffer, uint32_t scale, uint32_t screenWidth, int spriteID); + __forceinline HdPackTileInfo* GetCachedMatchingTile(uint32_t x, uint32_t y, HdPpuTileInfo* tile); __forceinline HdPackTileInfo* GetMatchingTile(uint32_t x, uint32_t y, HdPpuTileInfo* tile, bool* disableCache = nullptr);