BPFunctions.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright 2009 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include "Common/CommonTypes.h"
  5. #include "Core/ConfigManager.h"
  6. #include "Core/HW/Memmap.h"
  7. #include "VideoCommon/BPFunctions.h"
  8. #include "VideoCommon/RenderBase.h"
  9. #include "VideoCommon/TextureCacheBase.h"
  10. #include "VideoCommon/VertexManagerBase.h"
  11. #include "VideoCommon/VertexShaderManager.h"
  12. #include "VideoCommon/VideoConfig.h"
  13. namespace BPFunctions
  14. {
  15. // ----------------------------------------------
  16. // State translation lookup tables
  17. // Reference: Yet Another GameCube Documentation
  18. // ----------------------------------------------
  19. void FlushPipeline()
  20. {
  21. VertexManager::Flush();
  22. }
  23. void SetGenerationMode()
  24. {
  25. g_renderer->SetGenerationMode();
  26. }
  27. void SetScissor()
  28. {
  29. /* NOTE: the minimum value here for the scissor rect and offset is -342.
  30. * GX internally adds on an offset of 342 to both the offset and scissor
  31. * coords to ensure that the register was always unsigned.
  32. *
  33. * The code that was here before tried to "undo" this offset, but
  34. * since we always take the difference, the +342 added to both
  35. * sides cancels out. */
  36. /* The scissor offset is always even, so to save space, the scissor offset
  37. * register is scaled down by 2. So, if somebody calls
  38. * GX_SetScissorBoxOffset(20, 20); the registers will be set to 10, 10. */
  39. const int xoff = bpmem.scissorOffset.x * 2;
  40. const int yoff = bpmem.scissorOffset.y * 2;
  41. EFBRectangle rc (bpmem.scissorTL.x - xoff, bpmem.scissorTL.y - yoff,
  42. bpmem.scissorBR.x - xoff + 1, bpmem.scissorBR.y - yoff + 1);
  43. if (rc.left < 0) rc.left = 0;
  44. if (rc.top < 0) rc.top = 0;
  45. if (rc.right > EFB_WIDTH) rc.right = EFB_WIDTH;
  46. if (rc.bottom > EFB_HEIGHT) rc.bottom = EFB_HEIGHT;
  47. if (rc.left > rc.right) rc.right = rc.left;
  48. if (rc.top > rc.bottom) rc.bottom = rc.top;
  49. g_renderer->SetScissorRect(rc);
  50. }
  51. void SetDepthMode()
  52. {
  53. g_renderer->SetDepthMode();
  54. }
  55. void SetBlendMode()
  56. {
  57. g_renderer->SetBlendMode(false);
  58. }
  59. void SetDitherMode()
  60. {
  61. g_renderer->SetDitherMode();
  62. }
  63. void SetLogicOpMode()
  64. {
  65. g_renderer->SetLogicOpMode();
  66. }
  67. void SetColorMask()
  68. {
  69. g_renderer->SetColorMask();
  70. }
  71. void CopyEFB(u32 dstAddr, const EFBRectangle& srcRect,
  72. unsigned int dstFormat, PEControl::PixelFormat srcFormat,
  73. bool isIntensity, bool scaleByHalf)
  74. {
  75. // bpmem.zcontrol.pixel_format to PEControl::Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
  76. TextureCache::CopyRenderTargetToTexture(dstAddr, dstFormat, srcFormat,
  77. srcRect, isIntensity, scaleByHalf);
  78. }
  79. /* Explanation of the magic behind ClearScreen:
  80. There's numerous possible formats for the pixel data in the EFB.
  81. However, in the HW accelerated backends we're always using RGBA8
  82. for the EFB format, which causes some problems:
  83. - We're using an alpha channel although the game doesn't
  84. - If the actual EFB format is RGBA6_Z24 or R5G6B5_Z16, we are using more bits per channel than the native HW
  85. To properly emulate the above points, we're doing the following:
  86. (1)
  87. - disable alpha channel writing of any kind of rendering if the actual EFB format doesn't use an alpha channel
  88. - NOTE: Always make sure that the EFB has been cleared to an alpha value of 0xFF in this case!
  89. - Same for color channels, these need to be cleared to 0x00 though.
  90. (2)
  91. - convert the RGBA8 color to RGBA6/RGB8/RGB565 and convert it to RGBA8 again
  92. - convert the Z24 depth value to Z16 and back to Z24
  93. */
  94. void ClearScreen(const EFBRectangle &rc)
  95. {
  96. bool colorEnable = (bpmem.blendmode.colorupdate != 0);
  97. bool alphaEnable = (bpmem.blendmode.alphaupdate != 0);
  98. bool zEnable = (bpmem.zmode.updateenable != 0);
  99. auto pixel_format = bpmem.zcontrol.pixel_format;
  100. // (1): Disable unused color channels
  101. if (pixel_format == PEControl::RGB8_Z24 ||
  102. pixel_format == PEControl::RGB565_Z16 ||
  103. pixel_format == PEControl::Z24)
  104. {
  105. alphaEnable = false;
  106. }
  107. if (colorEnable || alphaEnable || zEnable)
  108. {
  109. u32 color = (bpmem.clearcolorAR << 16) | bpmem.clearcolorGB;
  110. u32 z = bpmem.clearZValue;
  111. // (2) drop additional accuracy
  112. if (pixel_format == PEControl::RGBA6_Z24)
  113. {
  114. color = RGBA8ToRGBA6ToRGBA8(color);
  115. }
  116. else if (pixel_format == PEControl::RGB565_Z16)
  117. {
  118. color = RGBA8ToRGB565ToRGBA8(color);
  119. z = Z24ToZ16ToZ24(z);
  120. }
  121. g_renderer->ClearScreen(rc, colorEnable, alphaEnable, zEnable, color, z);
  122. }
  123. }
  124. void OnPixelFormatChange()
  125. {
  126. int convtype = -1;
  127. // TODO : Check for Z compression format change
  128. // When using 16bit Z, the game may enable a special compression format which we need to handle
  129. // If we don't, Z values will be completely screwed up, currently only Star Wars:RS2 uses that.
  130. /*
  131. * When changing the EFB format, the pixel data won't get converted to the new format but stays the same.
  132. * Since we are always using an RGBA8 buffer though, this causes issues in some games.
  133. * Thus, we reinterpret the old EFB data with the new format here.
  134. */
  135. if (!g_ActiveConfig.bEFBEmulateFormatChanges)
  136. return;
  137. auto old_format = Renderer::GetPrevPixelFormat();
  138. auto new_format = bpmem.zcontrol.pixel_format;
  139. // no need to reinterpret pixel data in these cases
  140. if (new_format == old_format || old_format == PEControl::INVALID_FMT)
  141. goto skip;
  142. // Check for pixel format changes
  143. switch (old_format)
  144. {
  145. case PEControl::RGB8_Z24:
  146. case PEControl::Z24:
  147. // Z24 and RGB8_Z24 are treated equal, so just return in this case
  148. if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
  149. goto skip;
  150. if (new_format == PEControl::RGBA6_Z24)
  151. convtype = 0;
  152. else if (new_format == PEControl::RGB565_Z16)
  153. convtype = 1;
  154. break;
  155. case PEControl::RGBA6_Z24:
  156. if (new_format == PEControl::RGB8_Z24 ||
  157. new_format == PEControl::Z24)
  158. convtype = 2;
  159. else if (new_format == PEControl::RGB565_Z16)
  160. convtype = 3;
  161. break;
  162. case PEControl::RGB565_Z16:
  163. if (new_format == PEControl::RGB8_Z24 ||
  164. new_format == PEControl::Z24)
  165. convtype = 4;
  166. else if (new_format == PEControl::RGBA6_Z24)
  167. convtype = 5;
  168. break;
  169. default:
  170. break;
  171. }
  172. if (convtype == -1)
  173. {
  174. ERROR_LOG(VIDEO, "Unhandled EFB format change: %d to %d\n", static_cast<int>(old_format), static_cast<int>(new_format));
  175. goto skip;
  176. }
  177. g_renderer->ReinterpretPixelData(convtype);
  178. skip:
  179. DEBUG_LOG(VIDEO, "pixelfmt: pixel=%d, zc=%d", static_cast<int>(new_format), static_cast<int>(bpmem.zcontrol.zformat));
  180. Renderer::StorePixelFormat(new_format);
  181. }
  182. void SetInterlacingMode(const BPCmd &bp)
  183. {
  184. // TODO
  185. switch (bp.address)
  186. {
  187. case BPMEM_FIELDMODE:
  188. {
  189. // SDK always sets bpmem.lineptwidth.lineaspect via BPMEM_LINEPTWIDTH
  190. // just before this cmd
  191. const char *action[] = { "don't adjust", "adjust" };
  192. DEBUG_LOG(VIDEO, "BPMEM_FIELDMODE texLOD:%s lineaspect:%s",
  193. action[bpmem.fieldmode.texLOD],
  194. action[bpmem.lineptwidth.lineaspect]);
  195. }
  196. break;
  197. case BPMEM_FIELDMASK:
  198. {
  199. // Determines if fields will be written to EFB (always computed)
  200. const char *action[] = { "skip", "write" };
  201. DEBUG_LOG(VIDEO, "BPMEM_FIELDMASK even:%s odd:%s",
  202. action[bpmem.fieldmask.even], action[bpmem.fieldmask.odd]);
  203. }
  204. break;
  205. default:
  206. ERROR_LOG(VIDEO, "SetInterlacingMode default");
  207. break;
  208. }
  209. }
  210. };