123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- // Copyright 2009 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "VideoBackends/Software/TextureSampler.h"
- #include <algorithm>
- #include <cmath>
- #include <span>
- #include "Common/CommonTypes.h"
- #include "Common/MsgHandler.h"
- #include "Common/SpanUtils.h"
- #include "Core/HW/Memmap.h"
- #include "Core/System.h"
- #include "VideoCommon/BPMemory.h"
- #include "VideoCommon/TextureDecoder.h"
- #define ALLOW_MIPMAP 1
- namespace TextureSampler
- {
- static inline void WrapCoord(int* coordp, WrapMode wrap_mode, int image_size)
- {
- int coord = *coordp;
- switch (wrap_mode)
- {
- case WrapMode::Clamp:
- coord = std::clamp(coord, 0, image_size - 1);
- break;
- case WrapMode::Repeat:
- // Per YAGCD's info on TX_SETMODE1_I0 (et al.), mirror "requires the texture size to be a power
- // of two. (wrapping is implemented by a logical AND (SIZE-1))". So though this doesn't wrap
- // nicely for non-power-of-2 sizes, that's how hardware does it.
- coord = coord & (image_size - 1);
- break;
- case WrapMode::Mirror:
- {
- // YAGCD doesn't mention this, but this seems to be the check used to implement mirroring.
- // With power-of-2 sizes, this correctly checks if it's an even-numbered repeat or an
- // odd-numbered one, and thus can decide whether to reflect. It fails in unusual ways
- // with non-power-of-2 sizes, but seems to match what happens on actual hardware.
- if ((coord & image_size) != 0)
- coord = ~coord;
- coord = coord & (image_size - 1);
- break;
- }
- default:
- // Hardware testing indicates that wrap_mode set to 3 behaves the same as clamp.
- PanicAlertFmt("Invalid wrap mode: {}", wrap_mode);
- coord = std::clamp(coord, 0, image_size - 1);
- break;
- }
- *coordp = coord;
- }
- static inline void SetTexel(const u8* inTexel, u32* outTexel, u32 fract)
- {
- outTexel[0] = inTexel[0] * fract;
- outTexel[1] = inTexel[1] * fract;
- outTexel[2] = inTexel[2] * fract;
- outTexel[3] = inTexel[3] * fract;
- }
- static inline void AddTexel(const u8* inTexel, u32* outTexel, u32 fract)
- {
- outTexel[0] += inTexel[0] * fract;
- outTexel[1] += inTexel[1] * fract;
- outTexel[2] += inTexel[2] * fract;
- outTexel[3] += inTexel[3] * fract;
- }
- void Sample(s32 s, s32 t, s32 lod, bool linear, u8 texmap, u8* sample)
- {
- int baseMip = 0;
- bool mipLinear = false;
- #if (ALLOW_MIPMAP)
- auto texUnit = bpmem.tex.GetUnit(texmap);
- const TexMode0& tm0 = texUnit.texMode0;
- const s32 lodFract = lod & 0xf;
- if (lod > 0 && tm0.mipmap_filter != MipMode::None)
- {
- // use mipmap
- baseMip = lod >> 4;
- mipLinear = (lodFract && tm0.mipmap_filter == MipMode::Linear);
- // if using nearest mip filter and lodFract >= 0.5 round up to next mip
- if (tm0.mipmap_filter == MipMode::Point && lodFract >= 8)
- baseMip++;
- }
- if (mipLinear)
- {
- u8 sampledTex[4];
- u32 texel[4];
- SampleMip(s, t, baseMip, linear, texmap, sampledTex);
- SetTexel(sampledTex, texel, (16 - lodFract));
- SampleMip(s, t, baseMip + 1, linear, texmap, sampledTex);
- AddTexel(sampledTex, texel, lodFract);
- sample[0] = (u8)(texel[0] >> 4);
- sample[1] = (u8)(texel[1] >> 4);
- sample[2] = (u8)(texel[2] >> 4);
- sample[3] = (u8)(texel[3] >> 4);
- }
- else
- #endif
- {
- SampleMip(s, t, baseMip, linear, texmap, sample);
- }
- }
- void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
- {
- auto texUnit = bpmem.tex.GetUnit(texmap);
- const TexMode0& tm0 = texUnit.texMode0;
- const TexImage0& ti0 = texUnit.texImage0;
- const TexTLUT& texTlut = texUnit.texTlut;
- const TextureFormat texfmt = ti0.format;
- const TLUTFormat tlutfmt = texTlut.tlut_format;
- std::span<const u8> image_src;
- std::span<const u8> image_src_odd;
- if (texUnit.texImage1.cache_manually_managed)
- {
- image_src = TexDecoder_GetTmemSpan(texUnit.texImage1.tmem_even * TMEM_LINE_SIZE);
- if (texfmt == TextureFormat::RGBA8)
- image_src_odd = TexDecoder_GetTmemSpan(texUnit.texImage2.tmem_odd * TMEM_LINE_SIZE);
- }
- else
- {
- auto& system = Core::System::GetInstance();
- auto& memory = system.GetMemory();
- const u32 imageBase = texUnit.texImage3.image_base << 5;
- image_src = memory.GetSpanForAddress(imageBase);
- }
- int image_width_minus_1 = ti0.width;
- int image_height_minus_1 = ti0.height;
- const int tlutAddress = texTlut.tmem_offset << 9;
- const std::span<const u8> tlut = TexDecoder_GetTmemSpan(tlutAddress);
- // reduce sample location and texture size to mip level
- // move texture pointer to mip location
- if (mip)
- {
- int mipWidth = image_width_minus_1 + 1;
- int mipHeight = image_height_minus_1 + 1;
- const int fmtWidth = TexDecoder_GetBlockWidthInTexels(texfmt);
- const int fmtHeight = TexDecoder_GetBlockHeightInTexels(texfmt);
- const int fmtDepth = TexDecoder_GetTexelSizeInNibbles(texfmt);
- image_width_minus_1 >>= mip;
- image_height_minus_1 >>= mip;
- s >>= mip;
- t >>= mip;
- while (mip)
- {
- mipWidth = std::max(mipWidth, fmtWidth);
- mipHeight = std::max(mipHeight, fmtHeight);
- const u32 size = (mipWidth * mipHeight * fmtDepth) >> 1;
- image_src = Common::SafeSubspan(image_src, size);
- mipWidth >>= 1;
- mipHeight >>= 1;
- mip--;
- }
- }
- if (linear)
- {
- // offset linear sampling
- s -= 64;
- t -= 64;
- // integer part of sample location
- int imageS = s >> 7;
- int imageT = t >> 7;
- // linear sampling
- int imageSPlus1 = imageS + 1;
- const int fractS = s & 0x7f;
- int imageTPlus1 = imageT + 1;
- const int fractT = t & 0x7f;
- u8 sampledTex[4];
- u32 texel[4];
- WrapCoord(&imageS, tm0.wrap_s, image_width_minus_1 + 1);
- WrapCoord(&imageT, tm0.wrap_t, image_height_minus_1 + 1);
- WrapCoord(&imageSPlus1, tm0.wrap_s, image_width_minus_1 + 1);
- WrapCoord(&imageTPlus1, tm0.wrap_t, image_height_minus_1 + 1);
- if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
- {
- TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageT, image_width_minus_1, texfmt,
- tlut, tlutfmt);
- SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
- TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageT, image_width_minus_1,
- texfmt, tlut, tlutfmt);
- AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
- TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageTPlus1, image_width_minus_1,
- texfmt, tlut, tlutfmt);
- AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
- TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageTPlus1, image_width_minus_1,
- texfmt, tlut, tlutfmt);
- AddTexel(sampledTex, texel, (fractS) * (fractT));
- }
- else
- {
- TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageT,
- image_width_minus_1);
- SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
- TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1, imageT,
- image_width_minus_1);
- AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
- TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageTPlus1,
- image_width_minus_1);
- AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
- TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1,
- imageTPlus1, image_width_minus_1);
- AddTexel(sampledTex, texel, (fractS) * (fractT));
- }
- sample[0] = (u8)(texel[0] >> 14);
- sample[1] = (u8)(texel[1] >> 14);
- sample[2] = (u8)(texel[2] >> 14);
- sample[3] = (u8)(texel[3] >> 14);
- }
- else
- {
- // integer part of sample location
- int imageS = s >> 7;
- int imageT = t >> 7;
- // nearest neighbor sampling
- WrapCoord(&imageS, tm0.wrap_s, image_width_minus_1 + 1);
- WrapCoord(&imageT, tm0.wrap_t, image_height_minus_1 + 1);
- if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
- {
- TexDecoder_DecodeTexel(sample, image_src, imageS, imageT, image_width_minus_1, texfmt, tlut,
- tlutfmt);
- }
- else
- {
- TexDecoder_DecodeTexelRGBA8FromTmem(sample, image_src, image_src_odd, imageS, imageT,
- image_width_minus_1);
- }
- }
- }
- } // namespace TextureSampler
|