TextureSampler.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright 2009 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoBackends/Software/TextureSampler.h"
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <span>
  7. #include "Common/CommonTypes.h"
  8. #include "Common/MsgHandler.h"
  9. #include "Common/SpanUtils.h"
  10. #include "Core/HW/Memmap.h"
  11. #include "Core/System.h"
  12. #include "VideoCommon/BPMemory.h"
  13. #include "VideoCommon/TextureDecoder.h"
  14. #define ALLOW_MIPMAP 1
  15. namespace TextureSampler
  16. {
  17. static inline void WrapCoord(int* coordp, WrapMode wrap_mode, int image_size)
  18. {
  19. int coord = *coordp;
  20. switch (wrap_mode)
  21. {
  22. case WrapMode::Clamp:
  23. coord = std::clamp(coord, 0, image_size - 1);
  24. break;
  25. case WrapMode::Repeat:
  26. // Per YAGCD's info on TX_SETMODE1_I0 (et al.), mirror "requires the texture size to be a power
  27. // of two. (wrapping is implemented by a logical AND (SIZE-1))". So though this doesn't wrap
  28. // nicely for non-power-of-2 sizes, that's how hardware does it.
  29. coord = coord & (image_size - 1);
  30. break;
  31. case WrapMode::Mirror:
  32. {
  33. // YAGCD doesn't mention this, but this seems to be the check used to implement mirroring.
  34. // With power-of-2 sizes, this correctly checks if it's an even-numbered repeat or an
  35. // odd-numbered one, and thus can decide whether to reflect. It fails in unusual ways
  36. // with non-power-of-2 sizes, but seems to match what happens on actual hardware.
  37. if ((coord & image_size) != 0)
  38. coord = ~coord;
  39. coord = coord & (image_size - 1);
  40. break;
  41. }
  42. default:
  43. // Hardware testing indicates that wrap_mode set to 3 behaves the same as clamp.
  44. PanicAlertFmt("Invalid wrap mode: {}", wrap_mode);
  45. coord = std::clamp(coord, 0, image_size - 1);
  46. break;
  47. }
  48. *coordp = coord;
  49. }
  50. static inline void SetTexel(const u8* inTexel, u32* outTexel, u32 fract)
  51. {
  52. outTexel[0] = inTexel[0] * fract;
  53. outTexel[1] = inTexel[1] * fract;
  54. outTexel[2] = inTexel[2] * fract;
  55. outTexel[3] = inTexel[3] * fract;
  56. }
  57. static inline void AddTexel(const u8* inTexel, u32* outTexel, u32 fract)
  58. {
  59. outTexel[0] += inTexel[0] * fract;
  60. outTexel[1] += inTexel[1] * fract;
  61. outTexel[2] += inTexel[2] * fract;
  62. outTexel[3] += inTexel[3] * fract;
  63. }
  64. void Sample(s32 s, s32 t, s32 lod, bool linear, u8 texmap, u8* sample)
  65. {
  66. int baseMip = 0;
  67. bool mipLinear = false;
  68. #if (ALLOW_MIPMAP)
  69. auto texUnit = bpmem.tex.GetUnit(texmap);
  70. const TexMode0& tm0 = texUnit.texMode0;
  71. const s32 lodFract = lod & 0xf;
  72. if (lod > 0 && tm0.mipmap_filter != MipMode::None)
  73. {
  74. // use mipmap
  75. baseMip = lod >> 4;
  76. mipLinear = (lodFract && tm0.mipmap_filter == MipMode::Linear);
  77. // if using nearest mip filter and lodFract >= 0.5 round up to next mip
  78. if (tm0.mipmap_filter == MipMode::Point && lodFract >= 8)
  79. baseMip++;
  80. }
  81. if (mipLinear)
  82. {
  83. u8 sampledTex[4];
  84. u32 texel[4];
  85. SampleMip(s, t, baseMip, linear, texmap, sampledTex);
  86. SetTexel(sampledTex, texel, (16 - lodFract));
  87. SampleMip(s, t, baseMip + 1, linear, texmap, sampledTex);
  88. AddTexel(sampledTex, texel, lodFract);
  89. sample[0] = (u8)(texel[0] >> 4);
  90. sample[1] = (u8)(texel[1] >> 4);
  91. sample[2] = (u8)(texel[2] >> 4);
  92. sample[3] = (u8)(texel[3] >> 4);
  93. }
  94. else
  95. #endif
  96. {
  97. SampleMip(s, t, baseMip, linear, texmap, sample);
  98. }
  99. }
  100. void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
  101. {
  102. auto texUnit = bpmem.tex.GetUnit(texmap);
  103. const TexMode0& tm0 = texUnit.texMode0;
  104. const TexImage0& ti0 = texUnit.texImage0;
  105. const TexTLUT& texTlut = texUnit.texTlut;
  106. const TextureFormat texfmt = ti0.format;
  107. const TLUTFormat tlutfmt = texTlut.tlut_format;
  108. std::span<const u8> image_src;
  109. std::span<const u8> image_src_odd;
  110. if (texUnit.texImage1.cache_manually_managed)
  111. {
  112. image_src = TexDecoder_GetTmemSpan(texUnit.texImage1.tmem_even * TMEM_LINE_SIZE);
  113. if (texfmt == TextureFormat::RGBA8)
  114. image_src_odd = TexDecoder_GetTmemSpan(texUnit.texImage2.tmem_odd * TMEM_LINE_SIZE);
  115. }
  116. else
  117. {
  118. auto& system = Core::System::GetInstance();
  119. auto& memory = system.GetMemory();
  120. const u32 imageBase = texUnit.texImage3.image_base << 5;
  121. image_src = memory.GetSpanForAddress(imageBase);
  122. }
  123. int image_width_minus_1 = ti0.width;
  124. int image_height_minus_1 = ti0.height;
  125. const int tlutAddress = texTlut.tmem_offset << 9;
  126. const std::span<const u8> tlut = TexDecoder_GetTmemSpan(tlutAddress);
  127. // reduce sample location and texture size to mip level
  128. // move texture pointer to mip location
  129. if (mip)
  130. {
  131. int mipWidth = image_width_minus_1 + 1;
  132. int mipHeight = image_height_minus_1 + 1;
  133. const int fmtWidth = TexDecoder_GetBlockWidthInTexels(texfmt);
  134. const int fmtHeight = TexDecoder_GetBlockHeightInTexels(texfmt);
  135. const int fmtDepth = TexDecoder_GetTexelSizeInNibbles(texfmt);
  136. image_width_minus_1 >>= mip;
  137. image_height_minus_1 >>= mip;
  138. s >>= mip;
  139. t >>= mip;
  140. while (mip)
  141. {
  142. mipWidth = std::max(mipWidth, fmtWidth);
  143. mipHeight = std::max(mipHeight, fmtHeight);
  144. const u32 size = (mipWidth * mipHeight * fmtDepth) >> 1;
  145. image_src = Common::SafeSubspan(image_src, size);
  146. mipWidth >>= 1;
  147. mipHeight >>= 1;
  148. mip--;
  149. }
  150. }
  151. if (linear)
  152. {
  153. // offset linear sampling
  154. s -= 64;
  155. t -= 64;
  156. // integer part of sample location
  157. int imageS = s >> 7;
  158. int imageT = t >> 7;
  159. // linear sampling
  160. int imageSPlus1 = imageS + 1;
  161. const int fractS = s & 0x7f;
  162. int imageTPlus1 = imageT + 1;
  163. const int fractT = t & 0x7f;
  164. u8 sampledTex[4];
  165. u32 texel[4];
  166. WrapCoord(&imageS, tm0.wrap_s, image_width_minus_1 + 1);
  167. WrapCoord(&imageT, tm0.wrap_t, image_height_minus_1 + 1);
  168. WrapCoord(&imageSPlus1, tm0.wrap_s, image_width_minus_1 + 1);
  169. WrapCoord(&imageTPlus1, tm0.wrap_t, image_height_minus_1 + 1);
  170. if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
  171. {
  172. TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageT, image_width_minus_1, texfmt,
  173. tlut, tlutfmt);
  174. SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
  175. TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageT, image_width_minus_1,
  176. texfmt, tlut, tlutfmt);
  177. AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
  178. TexDecoder_DecodeTexel(sampledTex, image_src, imageS, imageTPlus1, image_width_minus_1,
  179. texfmt, tlut, tlutfmt);
  180. AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
  181. TexDecoder_DecodeTexel(sampledTex, image_src, imageSPlus1, imageTPlus1, image_width_minus_1,
  182. texfmt, tlut, tlutfmt);
  183. AddTexel(sampledTex, texel, (fractS) * (fractT));
  184. }
  185. else
  186. {
  187. TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageT,
  188. image_width_minus_1);
  189. SetTexel(sampledTex, texel, (128 - fractS) * (128 - fractT));
  190. TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1, imageT,
  191. image_width_minus_1);
  192. AddTexel(sampledTex, texel, (fractS) * (128 - fractT));
  193. TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageS, imageTPlus1,
  194. image_width_minus_1);
  195. AddTexel(sampledTex, texel, (128 - fractS) * (fractT));
  196. TexDecoder_DecodeTexelRGBA8FromTmem(sampledTex, image_src, image_src_odd, imageSPlus1,
  197. imageTPlus1, image_width_minus_1);
  198. AddTexel(sampledTex, texel, (fractS) * (fractT));
  199. }
  200. sample[0] = (u8)(texel[0] >> 14);
  201. sample[1] = (u8)(texel[1] >> 14);
  202. sample[2] = (u8)(texel[2] >> 14);
  203. sample[3] = (u8)(texel[3] >> 14);
  204. }
  205. else
  206. {
  207. // integer part of sample location
  208. int imageS = s >> 7;
  209. int imageT = t >> 7;
  210. // nearest neighbor sampling
  211. WrapCoord(&imageS, tm0.wrap_s, image_width_minus_1 + 1);
  212. WrapCoord(&imageT, tm0.wrap_t, image_height_minus_1 + 1);
  213. if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1.cache_manually_managed))
  214. {
  215. TexDecoder_DecodeTexel(sample, image_src, imageS, imageT, image_width_minus_1, texfmt, tlut,
  216. tlutfmt);
  217. }
  218. else
  219. {
  220. TexDecoder_DecodeTexelRGBA8FromTmem(sample, image_src, image_src_odd, imageS, imageT,
  221. image_width_minus_1);
  222. }
  223. }
  224. }
  225. } // namespace TextureSampler