rasterizer.cpp 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <cmath>
  7. #include "common/assert.h"
  8. #include "common/bit_field.h"
  9. #include "common/color.h"
  10. #include "common/common_types.h"
  11. #include "common/logging/log.h"
  12. #include "common/math_util.h"
  13. #include "common/microprofile.h"
  14. #include "common/vector_math.h"
  15. #include "core/memory.h"
  16. #include "core/hw/gpu.h"
  17. #include "video_core/debug_utils/debug_utils.h"
  18. #include "video_core/pica.h"
  19. #include "video_core/pica_state.h"
  20. #include "video_core/pica_types.h"
  21. #include "video_core/rasterizer.h"
  22. #include "video_core/utils.h"
  23. #include "video_core/shader/shader.h"
  24. namespace Pica {
  25. namespace Rasterizer {
  26. static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
  27. const auto& framebuffer = g_state.regs.framebuffer;
  28. const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
  29. // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
  30. // NOTE: The framebuffer height register contains the actual FB height minus one.
  31. y = framebuffer.height - y;
  32. const u32 coarse_y = y & ~7;
  33. u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
  34. u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer.width * bytes_per_pixel;
  35. u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
  36. switch (framebuffer.color_format) {
  37. case Regs::ColorFormat::RGBA8:
  38. Color::EncodeRGBA8(color, dst_pixel);
  39. break;
  40. case Regs::ColorFormat::RGB8:
  41. Color::EncodeRGB8(color, dst_pixel);
  42. break;
  43. case Regs::ColorFormat::RGB5A1:
  44. Color::EncodeRGB5A1(color, dst_pixel);
  45. break;
  46. case Regs::ColorFormat::RGB565:
  47. Color::EncodeRGB565(color, dst_pixel);
  48. break;
  49. case Regs::ColorFormat::RGBA4:
  50. Color::EncodeRGBA4(color, dst_pixel);
  51. break;
  52. default:
  53. LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", framebuffer.color_format.Value());
  54. UNIMPLEMENTED();
  55. }
  56. }
  57. static const Math::Vec4<u8> GetPixel(int x, int y) {
  58. const auto& framebuffer = g_state.regs.framebuffer;
  59. const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
  60. y = framebuffer.height - y;
  61. const u32 coarse_y = y & ~7;
  62. u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
  63. u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * framebuffer.width * bytes_per_pixel;
  64. u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
  65. switch (framebuffer.color_format) {
  66. case Regs::ColorFormat::RGBA8:
  67. return Color::DecodeRGBA8(src_pixel);
  68. case Regs::ColorFormat::RGB8:
  69. return Color::DecodeRGB8(src_pixel);
  70. case Regs::ColorFormat::RGB5A1:
  71. return Color::DecodeRGB5A1(src_pixel);
  72. case Regs::ColorFormat::RGB565:
  73. return Color::DecodeRGB565(src_pixel);
  74. case Regs::ColorFormat::RGBA4:
  75. return Color::DecodeRGBA4(src_pixel);
  76. default:
  77. LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", framebuffer.color_format.Value());
  78. UNIMPLEMENTED();
  79. }
  80. return {0, 0, 0, 0};
  81. }
  82. static u32 GetDepth(int x, int y) {
  83. const auto& framebuffer = g_state.regs.framebuffer;
  84. const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
  85. u8* depth_buffer = Memory::GetPhysicalPointer(addr);
  86. y = framebuffer.height - y;
  87. const u32 coarse_y = y & ~7;
  88. u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
  89. u32 stride = framebuffer.width * bytes_per_pixel;
  90. u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
  91. u8* src_pixel = depth_buffer + src_offset;
  92. switch (framebuffer.depth_format) {
  93. case Regs::DepthFormat::D16:
  94. return Color::DecodeD16(src_pixel);
  95. case Regs::DepthFormat::D24:
  96. return Color::DecodeD24(src_pixel);
  97. case Regs::DepthFormat::D24S8:
  98. return Color::DecodeD24S8(src_pixel).x;
  99. default:
  100. LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
  101. UNIMPLEMENTED();
  102. return 0;
  103. }
  104. }
  105. static u8 GetStencil(int x, int y) {
  106. const auto& framebuffer = g_state.regs.framebuffer;
  107. const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
  108. u8* depth_buffer = Memory::GetPhysicalPointer(addr);
  109. y = framebuffer.height - y;
  110. const u32 coarse_y = y & ~7;
  111. u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
  112. u32 stride = framebuffer.width * bytes_per_pixel;
  113. u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
  114. u8* src_pixel = depth_buffer + src_offset;
  115. switch (framebuffer.depth_format) {
  116. case Regs::DepthFormat::D24S8:
  117. return Color::DecodeD24S8(src_pixel).y;
  118. default:
  119. LOG_WARNING(HW_GPU, "GetStencil called for function which doesn't have a stencil component (format %u)", framebuffer.depth_format);
  120. return 0;
  121. }
  122. }
  123. static void SetDepth(int x, int y, u32 value) {
  124. const auto& framebuffer = g_state.regs.framebuffer;
  125. const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
  126. u8* depth_buffer = Memory::GetPhysicalPointer(addr);
  127. y = framebuffer.height - y;
  128. const u32 coarse_y = y & ~7;
  129. u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
  130. u32 stride = framebuffer.width * bytes_per_pixel;
  131. u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
  132. u8* dst_pixel = depth_buffer + dst_offset;
  133. switch (framebuffer.depth_format) {
  134. case Regs::DepthFormat::D16:
  135. Color::EncodeD16(value, dst_pixel);
  136. break;
  137. case Regs::DepthFormat::D24:
  138. Color::EncodeD24(value, dst_pixel);
  139. break;
  140. case Regs::DepthFormat::D24S8:
  141. Color::EncodeD24X8(value, dst_pixel);
  142. break;
  143. default:
  144. LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
  145. UNIMPLEMENTED();
  146. break;
  147. }
  148. }
  149. static void SetStencil(int x, int y, u8 value) {
  150. const auto& framebuffer = g_state.regs.framebuffer;
  151. const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
  152. u8* depth_buffer = Memory::GetPhysicalPointer(addr);
  153. y = framebuffer.height - y;
  154. const u32 coarse_y = y & ~7;
  155. u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
  156. u32 stride = framebuffer.width * bytes_per_pixel;
  157. u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
  158. u8* dst_pixel = depth_buffer + dst_offset;
  159. switch (framebuffer.depth_format) {
  160. case Pica::Regs::DepthFormat::D16:
  161. case Pica::Regs::DepthFormat::D24:
  162. // Nothing to do
  163. break;
  164. case Pica::Regs::DepthFormat::D24S8:
  165. Color::EncodeX24S8(value, dst_pixel);
  166. break;
  167. default:
  168. LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
  169. UNIMPLEMENTED();
  170. break;
  171. }
  172. }
  173. static u8 PerformStencilAction(Regs::StencilAction action, u8 old_stencil, u8 ref) {
  174. switch (action) {
  175. case Regs::StencilAction::Keep:
  176. return old_stencil;
  177. case Regs::StencilAction::Zero:
  178. return 0;
  179. case Regs::StencilAction::Replace:
  180. return ref;
  181. case Regs::StencilAction::Increment:
  182. // Saturated increment
  183. return std::min<u8>(old_stencil, 254) + 1;
  184. case Regs::StencilAction::Decrement:
  185. // Saturated decrement
  186. return std::max<u8>(old_stencil, 1) - 1;
  187. case Regs::StencilAction::Invert:
  188. return ~old_stencil;
  189. case Regs::StencilAction::IncrementWrap:
  190. return old_stencil + 1;
  191. case Regs::StencilAction::DecrementWrap:
  192. return old_stencil - 1;
  193. default:
  194. LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
  195. UNIMPLEMENTED();
  196. return 0;
  197. }
  198. }
  199. // NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values
  200. struct Fix12P4 {
  201. Fix12P4() {}
  202. Fix12P4(u16 val) : val(val) {}
  203. static u16 FracMask() { return 0xF; }
  204. static u16 IntMask() { return (u16)~0xF; }
  205. operator u16() const {
  206. return val;
  207. }
  208. bool operator < (const Fix12P4& oth) const {
  209. return (u16)*this < (u16)oth;
  210. }
  211. private:
  212. u16 val;
  213. };
  214. /**
  215. * Calculate signed area of the triangle spanned by the three argument vertices.
  216. * The sign denotes an orientation.
  217. *
  218. * @todo define orientation concretely.
  219. */
  220. static int SignedArea (const Math::Vec2<Fix12P4>& vtx1,
  221. const Math::Vec2<Fix12P4>& vtx2,
  222. const Math::Vec2<Fix12P4>& vtx3) {
  223. const auto vec1 = Math::MakeVec(vtx2 - vtx1, 0);
  224. const auto vec2 = Math::MakeVec(vtx3 - vtx1, 0);
  225. // TODO: There is a very small chance this will overflow for sizeof(int) == 4
  226. return Math::Cross(vec1, vec2).z;
  227. };
  228. MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240));
  229. /**
  230. * Helper function for ProcessTriangle with the "reversed" flag to allow for implementing
  231. * culling via recursion.
  232. */
  233. static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
  234. const Shader::OutputVertex& v1,
  235. const Shader::OutputVertex& v2,
  236. bool reversed = false)
  237. {
  238. const auto& regs = g_state.regs;
  239. MICROPROFILE_SCOPE(GPU_Rasterization);
  240. // vertex positions in rasterizer coordinates
  241. static auto FloatToFix = [](float24 flt) {
  242. // TODO: Rounding here is necessary to prevent garbage pixels at
  243. // triangle borders. Is it that the correct solution, though?
  244. return Fix12P4(static_cast<unsigned short>(round(flt.ToFloat32() * 16.0f)));
  245. };
  246. static auto ScreenToRasterizerCoordinates = [](const Math::Vec3<float24>& vec) {
  247. return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)};
  248. };
  249. Math::Vec3<Fix12P4> vtxpos[3]{ ScreenToRasterizerCoordinates(v0.screenpos),
  250. ScreenToRasterizerCoordinates(v1.screenpos),
  251. ScreenToRasterizerCoordinates(v2.screenpos) };
  252. if (regs.cull_mode == Regs::CullMode::KeepAll) {
  253. // Make sure we always end up with a triangle wound counter-clockwise
  254. if (!reversed && SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0) {
  255. ProcessTriangleInternal(v0, v2, v1, true);
  256. return;
  257. }
  258. } else {
  259. if (!reversed && regs.cull_mode == Regs::CullMode::KeepClockWise) {
  260. // Reverse vertex order and use the CCW code path.
  261. ProcessTriangleInternal(v0, v2, v1, true);
  262. return;
  263. }
  264. // Cull away triangles which are wound clockwise.
  265. if (SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0)
  266. return;
  267. }
  268. // TODO: Proper scissor rect test!
  269. u16 min_x = std::min({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
  270. u16 min_y = std::min({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
  271. u16 max_x = std::max({vtxpos[0].x, vtxpos[1].x, vtxpos[2].x});
  272. u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
  273. min_x &= Fix12P4::IntMask();
  274. min_y &= Fix12P4::IntMask();
  275. max_x = ((max_x + Fix12P4::FracMask()) & Fix12P4::IntMask());
  276. max_y = ((max_y + Fix12P4::FracMask()) & Fix12P4::IntMask());
  277. // Triangle filling rules: Pixels on the right-sided edge or on flat bottom edges are not
  278. // drawn. Pixels on any other triangle border are drawn. This is implemented with three bias
  279. // values which are added to the barycentric coordinates w0, w1 and w2, respectively.
  280. // NOTE: These are the PSP filling rules. Not sure if the 3DS uses the same ones...
  281. auto IsRightSideOrFlatBottomEdge = [](const Math::Vec2<Fix12P4>& vtx,
  282. const Math::Vec2<Fix12P4>& line1,
  283. const Math::Vec2<Fix12P4>& line2)
  284. {
  285. if (line1.y == line2.y) {
  286. // just check if vertex is above us => bottom line parallel to x-axis
  287. return vtx.y < line1.y;
  288. } else {
  289. // check if vertex is on our left => right side
  290. // TODO: Not sure how likely this is to overflow
  291. return (int)vtx.x < (int)line1.x + ((int)line2.x - (int)line1.x) * ((int)vtx.y - (int)line1.y) / ((int)line2.y - (int)line1.y);
  292. }
  293. };
  294. int bias0 = IsRightSideOrFlatBottomEdge(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) ? -1 : 0;
  295. int bias1 = IsRightSideOrFlatBottomEdge(vtxpos[1].xy(), vtxpos[2].xy(), vtxpos[0].xy()) ? -1 : 0;
  296. int bias2 = IsRightSideOrFlatBottomEdge(vtxpos[2].xy(), vtxpos[0].xy(), vtxpos[1].xy()) ? -1 : 0;
  297. auto w_inverse = Math::MakeVec(v0.pos.w, v1.pos.w, v2.pos.w);
  298. auto textures = regs.GetTextures();
  299. auto tev_stages = regs.GetTevStages();
  300. bool stencil_action_enable = g_state.regs.output_merger.stencil_test.enable && g_state.regs.framebuffer.depth_format == Regs::DepthFormat::D24S8;
  301. const auto stencil_test = g_state.regs.output_merger.stencil_test;
  302. // Enter rasterization loop, starting at the center of the topleft bounding box corner.
  303. // TODO: Not sure if looping through x first might be faster
  304. for (u16 y = min_y + 8; y < max_y; y += 0x10) {
  305. for (u16 x = min_x + 8; x < max_x; x += 0x10) {
  306. // Calculate the barycentric coordinates w0, w1 and w2
  307. int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y});
  308. int w1 = bias1 + SignedArea(vtxpos[2].xy(), vtxpos[0].xy(), {x, y});
  309. int w2 = bias2 + SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), {x, y});
  310. int wsum = w0 + w1 + w2;
  311. // If current pixel is not covered by the current primitive
  312. if (w0 < 0 || w1 < 0 || w2 < 0)
  313. continue;
  314. auto baricentric_coordinates = Math::MakeVec(float24::FromFloat32(static_cast<float>(w0)),
  315. float24::FromFloat32(static_cast<float>(w1)),
  316. float24::FromFloat32(static_cast<float>(w2)));
  317. float24 interpolated_w_inverse = float24::FromFloat32(1.0f) / Math::Dot(w_inverse, baricentric_coordinates);
  318. // Perspective correct attribute interpolation:
  319. // Attribute values cannot be calculated by simple linear interpolation since
  320. // they are not linear in screen space. For example, when interpolating a
  321. // texture coordinate across two vertices, something simple like
  322. // u = (u0*w0 + u1*w1)/(w0+w1)
  323. // will not work. However, the attribute value divided by the
  324. // clipspace w-coordinate (u/w) and and the inverse w-coordinate (1/w) are linear
  325. // in screenspace. Hence, we can linearly interpolate these two independently and
  326. // calculate the interpolated attribute by dividing the results.
  327. // I.e.
  328. // u_over_w = ((u0/v0.pos.w)*w0 + (u1/v1.pos.w)*w1)/(w0+w1)
  329. // one_over_w = (( 1/v0.pos.w)*w0 + ( 1/v1.pos.w)*w1)/(w0+w1)
  330. // u = u_over_w / one_over_w
  331. //
  332. // The generalization to three vertices is straightforward in baricentric coordinates.
  333. auto GetInterpolatedAttribute = [&](float24 attr0, float24 attr1, float24 attr2) {
  334. auto attr_over_w = Math::MakeVec(attr0, attr1, attr2);
  335. float24 interpolated_attr_over_w = Math::Dot(attr_over_w, baricentric_coordinates);
  336. return interpolated_attr_over_w * interpolated_w_inverse;
  337. };
  338. Math::Vec4<u8> primary_color{
  339. (u8)(GetInterpolatedAttribute(v0.color.r(), v1.color.r(), v2.color.r()).ToFloat32() * 255),
  340. (u8)(GetInterpolatedAttribute(v0.color.g(), v1.color.g(), v2.color.g()).ToFloat32() * 255),
  341. (u8)(GetInterpolatedAttribute(v0.color.b(), v1.color.b(), v2.color.b()).ToFloat32() * 255),
  342. (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255)
  343. };
  344. Math::Vec2<float24> uv[3];
  345. uv[0].u() = GetInterpolatedAttribute(v0.tc0.u(), v1.tc0.u(), v2.tc0.u());
  346. uv[0].v() = GetInterpolatedAttribute(v0.tc0.v(), v1.tc0.v(), v2.tc0.v());
  347. uv[1].u() = GetInterpolatedAttribute(v0.tc1.u(), v1.tc1.u(), v2.tc1.u());
  348. uv[1].v() = GetInterpolatedAttribute(v0.tc1.v(), v1.tc1.v(), v2.tc1.v());
  349. uv[2].u() = GetInterpolatedAttribute(v0.tc2.u(), v1.tc2.u(), v2.tc2.u());
  350. uv[2].v() = GetInterpolatedAttribute(v0.tc2.v(), v1.tc2.v(), v2.tc2.v());
  351. Math::Vec4<u8> texture_color[3]{};
  352. for (int i = 0; i < 3; ++i) {
  353. const auto& texture = textures[i];
  354. if (!texture.enabled)
  355. continue;
  356. DEBUG_ASSERT(0 != texture.config.address);
  357. int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32();
  358. int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32();
  359. static auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) {
  360. switch (mode) {
  361. case Regs::TextureConfig::ClampToEdge:
  362. val = std::max(val, 0);
  363. val = std::min(val, (int)size - 1);
  364. return val;
  365. case Regs::TextureConfig::ClampToBorder:
  366. return val;
  367. case Regs::TextureConfig::Repeat:
  368. return (int)((unsigned)val % size);
  369. case Regs::TextureConfig::MirroredRepeat:
  370. {
  371. unsigned int coord = ((unsigned)val % (2 * size));
  372. if (coord >= size)
  373. coord = 2 * size - 1 - coord;
  374. return (int)coord;
  375. }
  376. default:
  377. LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
  378. UNIMPLEMENTED();
  379. return 0;
  380. }
  381. };
  382. if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder && (s < 0 || s >= texture.config.width))
  383. || (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder && (t < 0 || t >= texture.config.height))) {
  384. auto border_color = texture.config.border_color;
  385. texture_color[i] = { border_color.r, border_color.g, border_color.b, border_color.a };
  386. } else {
  387. // Textures are laid out from bottom to top, hence we invert the t coordinate.
  388. // NOTE: This may not be the right place for the inversion.
  389. // TODO: Check if this applies to ETC textures, too.
  390. s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width);
  391. t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height);
  392. u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
  393. auto info = DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
  394. // TODO: Apply the min and mag filters to the texture
  395. texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info);
  396. #if PICA_DUMP_TEXTURES
  397. DebugUtils::DumpTexture(texture.config, texture_data);
  398. #endif
  399. }
  400. }
  401. // Texture environment - consists of 6 stages of color and alpha combining.
  402. //
  403. // Color combiners take three input color values from some source (e.g. interpolated
  404. // vertex color, texture color, previous stage, etc), perform some very simple
  405. // operations on each of them (e.g. inversion) and then calculate the output color
  406. // with some basic arithmetic. Alpha combiners can be configured separately but work
  407. // analogously.
  408. Math::Vec4<u8> combiner_output;
  409. Math::Vec4<u8> combiner_buffer = {0, 0, 0, 0};
  410. Math::Vec4<u8> next_combiner_buffer = {
  411. regs.tev_combiner_buffer_color.r, regs.tev_combiner_buffer_color.g,
  412. regs.tev_combiner_buffer_color.b, regs.tev_combiner_buffer_color.a
  413. };
  414. for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
  415. const auto& tev_stage = tev_stages[tev_stage_index];
  416. using Source = Regs::TevStageConfig::Source;
  417. using ColorModifier = Regs::TevStageConfig::ColorModifier;
  418. using AlphaModifier = Regs::TevStageConfig::AlphaModifier;
  419. using Operation = Regs::TevStageConfig::Operation;
  420. auto GetSource = [&](Source source) -> Math::Vec4<u8> {
  421. switch (source) {
  422. case Source::PrimaryColor:
  423. // HACK: Until we implement fragment lighting, use primary_color
  424. case Source::PrimaryFragmentColor:
  425. return primary_color;
  426. // HACK: Until we implement fragment lighting, use zero
  427. case Source::SecondaryFragmentColor:
  428. return {0, 0, 0, 0};
  429. case Source::Texture0:
  430. return texture_color[0];
  431. case Source::Texture1:
  432. return texture_color[1];
  433. case Source::Texture2:
  434. return texture_color[2];
  435. case Source::PreviousBuffer:
  436. return combiner_buffer;
  437. case Source::Constant:
  438. return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b, tev_stage.const_a};
  439. case Source::Previous:
  440. return combiner_output;
  441. default:
  442. LOG_ERROR(HW_GPU, "Unknown color combiner source %d", (int)source);
  443. UNIMPLEMENTED();
  444. return {0, 0, 0, 0};
  445. }
  446. };
  447. static auto GetColorModifier = [](ColorModifier factor, const Math::Vec4<u8>& values) -> Math::Vec3<u8> {
  448. switch (factor) {
  449. case ColorModifier::SourceColor:
  450. return values.rgb();
  451. case ColorModifier::OneMinusSourceColor:
  452. return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
  453. case ColorModifier::SourceAlpha:
  454. return values.aaa();
  455. case ColorModifier::OneMinusSourceAlpha:
  456. return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
  457. case ColorModifier::SourceRed:
  458. return values.rrr();
  459. case ColorModifier::OneMinusSourceRed:
  460. return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
  461. case ColorModifier::SourceGreen:
  462. return values.ggg();
  463. case ColorModifier::OneMinusSourceGreen:
  464. return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
  465. case ColorModifier::SourceBlue:
  466. return values.bbb();
  467. case ColorModifier::OneMinusSourceBlue:
  468. return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
  469. }
  470. };
  471. static auto GetAlphaModifier = [](AlphaModifier factor, const Math::Vec4<u8>& values) -> u8 {
  472. switch (factor) {
  473. case AlphaModifier::SourceAlpha:
  474. return values.a();
  475. case AlphaModifier::OneMinusSourceAlpha:
  476. return 255 - values.a();
  477. case AlphaModifier::SourceRed:
  478. return values.r();
  479. case AlphaModifier::OneMinusSourceRed:
  480. return 255 - values.r();
  481. case AlphaModifier::SourceGreen:
  482. return values.g();
  483. case AlphaModifier::OneMinusSourceGreen:
  484. return 255 - values.g();
  485. case AlphaModifier::SourceBlue:
  486. return values.b();
  487. case AlphaModifier::OneMinusSourceBlue:
  488. return 255 - values.b();
  489. }
  490. };
  491. static auto ColorCombine = [](Operation op, const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> {
  492. switch (op) {
  493. case Operation::Replace:
  494. return input[0];
  495. case Operation::Modulate:
  496. return ((input[0] * input[1]) / 255).Cast<u8>();
  497. case Operation::Add:
  498. {
  499. auto result = input[0] + input[1];
  500. result.r() = std::min(255, result.r());
  501. result.g() = std::min(255, result.g());
  502. result.b() = std::min(255, result.b());
  503. return result.Cast<u8>();
  504. }
  505. case Operation::AddSigned:
  506. {
  507. // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
  508. auto result = input[0].Cast<int>() + input[1].Cast<int>() - Math::MakeVec<int>(128, 128, 128);
  509. result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
  510. result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
  511. result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
  512. return result.Cast<u8>();
  513. }
  514. case Operation::Lerp:
  515. return ((input[0] * input[2] + input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) / 255).Cast<u8>();
  516. case Operation::Subtract:
  517. {
  518. auto result = input[0].Cast<int>() - input[1].Cast<int>();
  519. result.r() = std::max(0, result.r());
  520. result.g() = std::max(0, result.g());
  521. result.b() = std::max(0, result.b());
  522. return result.Cast<u8>();
  523. }
  524. case Operation::MultiplyThenAdd:
  525. {
  526. auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
  527. result.r() = std::min(255, result.r());
  528. result.g() = std::min(255, result.g());
  529. result.b() = std::min(255, result.b());
  530. return result.Cast<u8>();
  531. }
  532. case Operation::AddThenMultiply:
  533. {
  534. auto result = input[0] + input[1];
  535. result.r() = std::min(255, result.r());
  536. result.g() = std::min(255, result.g());
  537. result.b() = std::min(255, result.b());
  538. result = (result * input[2].Cast<int>()) / 255;
  539. return result.Cast<u8>();
  540. }
  541. case Operation::Dot3_RGB:
  542. {
  543. // Not fully accurate.
  544. // Worst case scenario seems to yield a +/-3 error
  545. // Some HW results indicate that the per-component computation can't have a higher precision than 1/256,
  546. // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb( (0x80,g0,b0),(0x80,g1,b1) ) give different results
  547. int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
  548. ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
  549. ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
  550. result = std::max(0, std::min(255, result));
  551. return { (u8)result, (u8)result, (u8)result };
  552. }
  553. default:
  554. LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
  555. UNIMPLEMENTED();
  556. return {0, 0, 0};
  557. }
  558. };
  559. static auto AlphaCombine = [](Operation op, const std::array<u8,3>& input) -> u8 {
  560. switch (op) {
  561. case Operation::Replace:
  562. return input[0];
  563. case Operation::Modulate:
  564. return input[0] * input[1] / 255;
  565. case Operation::Add:
  566. return std::min(255, input[0] + input[1]);
  567. case Operation::AddSigned:
  568. {
  569. // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
  570. auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
  571. return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
  572. }
  573. case Operation::Lerp:
  574. return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
  575. case Operation::Subtract:
  576. return std::max(0, (int)input[0] - (int)input[1]);
  577. case Operation::MultiplyThenAdd:
  578. return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
  579. case Operation::AddThenMultiply:
  580. return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
  581. default:
  582. LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
  583. UNIMPLEMENTED();
  584. return 0;
  585. }
  586. };
  587. // color combiner
  588. // NOTE: Not sure if the alpha combiner might use the color output of the previous
  589. // stage as input. Hence, we currently don't directly write the result to
  590. // combiner_output.rgb(), but instead store it in a temporary variable until
  591. // alpha combining has been done.
  592. Math::Vec3<u8> color_result[3] = {
  593. GetColorModifier(tev_stage.color_modifier1, GetSource(tev_stage.color_source1)),
  594. GetColorModifier(tev_stage.color_modifier2, GetSource(tev_stage.color_source2)),
  595. GetColorModifier(tev_stage.color_modifier3, GetSource(tev_stage.color_source3))
  596. };
  597. auto color_output = ColorCombine(tev_stage.color_op, color_result);
  598. // alpha combiner
  599. std::array<u8,3> alpha_result = {{
  600. GetAlphaModifier(tev_stage.alpha_modifier1, GetSource(tev_stage.alpha_source1)),
  601. GetAlphaModifier(tev_stage.alpha_modifier2, GetSource(tev_stage.alpha_source2)),
  602. GetAlphaModifier(tev_stage.alpha_modifier3, GetSource(tev_stage.alpha_source3))
  603. }};
  604. auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result);
  605. combiner_output[0] = std::min((unsigned)255, color_output.r() * tev_stage.GetColorMultiplier());
  606. combiner_output[1] = std::min((unsigned)255, color_output.g() * tev_stage.GetColorMultiplier());
  607. combiner_output[2] = std::min((unsigned)255, color_output.b() * tev_stage.GetColorMultiplier());
  608. combiner_output[3] = std::min((unsigned)255, alpha_output * tev_stage.GetAlphaMultiplier());
  609. combiner_buffer = next_combiner_buffer;
  610. if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index)) {
  611. next_combiner_buffer.r() = combiner_output.r();
  612. next_combiner_buffer.g() = combiner_output.g();
  613. next_combiner_buffer.b() = combiner_output.b();
  614. }
  615. if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) {
  616. next_combiner_buffer.a() = combiner_output.a();
  617. }
  618. }
  619. const auto& output_merger = regs.output_merger;
  620. // TODO: Does alpha testing happen before or after stencil?
  621. if (output_merger.alpha_test.enable) {
  622. bool pass = false;
  623. switch (output_merger.alpha_test.func) {
  624. case Regs::CompareFunc::Never:
  625. pass = false;
  626. break;
  627. case Regs::CompareFunc::Always:
  628. pass = true;
  629. break;
  630. case Regs::CompareFunc::Equal:
  631. pass = combiner_output.a() == output_merger.alpha_test.ref;
  632. break;
  633. case Regs::CompareFunc::NotEqual:
  634. pass = combiner_output.a() != output_merger.alpha_test.ref;
  635. break;
  636. case Regs::CompareFunc::LessThan:
  637. pass = combiner_output.a() < output_merger.alpha_test.ref;
  638. break;
  639. case Regs::CompareFunc::LessThanOrEqual:
  640. pass = combiner_output.a() <= output_merger.alpha_test.ref;
  641. break;
  642. case Regs::CompareFunc::GreaterThan:
  643. pass = combiner_output.a() > output_merger.alpha_test.ref;
  644. break;
  645. case Regs::CompareFunc::GreaterThanOrEqual:
  646. pass = combiner_output.a() >= output_merger.alpha_test.ref;
  647. break;
  648. }
  649. if (!pass)
  650. continue;
  651. }
  652. u8 old_stencil = 0;
  653. auto UpdateStencil = [stencil_test, x, y, &old_stencil](Pica::Regs::StencilAction action) {
  654. u8 new_stencil = PerformStencilAction(action, old_stencil, stencil_test.reference_value);
  655. if (g_state.regs.framebuffer.allow_depth_stencil_write != 0)
  656. SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) | (old_stencil & ~stencil_test.write_mask));
  657. };
  658. if (stencil_action_enable) {
  659. old_stencil = GetStencil(x >> 4, y >> 4);
  660. u8 dest = old_stencil & stencil_test.input_mask;
  661. u8 ref = stencil_test.reference_value & stencil_test.input_mask;
  662. bool pass = false;
  663. switch (stencil_test.func) {
  664. case Regs::CompareFunc::Never:
  665. pass = false;
  666. break;
  667. case Regs::CompareFunc::Always:
  668. pass = true;
  669. break;
  670. case Regs::CompareFunc::Equal:
  671. pass = (ref == dest);
  672. break;
  673. case Regs::CompareFunc::NotEqual:
  674. pass = (ref != dest);
  675. break;
  676. case Regs::CompareFunc::LessThan:
  677. pass = (ref < dest);
  678. break;
  679. case Regs::CompareFunc::LessThanOrEqual:
  680. pass = (ref <= dest);
  681. break;
  682. case Regs::CompareFunc::GreaterThan:
  683. pass = (ref > dest);
  684. break;
  685. case Regs::CompareFunc::GreaterThanOrEqual:
  686. pass = (ref >= dest);
  687. break;
  688. }
  689. if (!pass) {
  690. UpdateStencil(stencil_test.action_stencil_fail);
  691. continue;
  692. }
  693. }
  694. unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
  695. u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 +
  696. v1.screenpos[2].ToFloat32() * w1 +
  697. v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum);
  698. if (output_merger.depth_test_enable) {
  699. u32 ref_z = GetDepth(x >> 4, y >> 4);
  700. bool pass = false;
  701. switch (output_merger.depth_test_func) {
  702. case Regs::CompareFunc::Never:
  703. pass = false;
  704. break;
  705. case Regs::CompareFunc::Always:
  706. pass = true;
  707. break;
  708. case Regs::CompareFunc::Equal:
  709. pass = z == ref_z;
  710. break;
  711. case Regs::CompareFunc::NotEqual:
  712. pass = z != ref_z;
  713. break;
  714. case Regs::CompareFunc::LessThan:
  715. pass = z < ref_z;
  716. break;
  717. case Regs::CompareFunc::LessThanOrEqual:
  718. pass = z <= ref_z;
  719. break;
  720. case Regs::CompareFunc::GreaterThan:
  721. pass = z > ref_z;
  722. break;
  723. case Regs::CompareFunc::GreaterThanOrEqual:
  724. pass = z >= ref_z;
  725. break;
  726. }
  727. if (!pass) {
  728. if (stencil_action_enable)
  729. UpdateStencil(stencil_test.action_depth_fail);
  730. continue;
  731. }
  732. }
  733. if (regs.framebuffer.allow_depth_stencil_write != 0 && output_merger.depth_write_enable)
  734. SetDepth(x >> 4, y >> 4, z);
  735. // The stencil depth_pass action is executed even if depth testing is disabled
  736. if (stencil_action_enable)
  737. UpdateStencil(stencil_test.action_depth_pass);
  738. auto dest = GetPixel(x >> 4, y >> 4);
  739. Math::Vec4<u8> blend_output = combiner_output;
  740. if (output_merger.alphablend_enable) {
  741. auto params = output_merger.alpha_blending;
  742. auto LookupFactor = [&](unsigned channel, Regs::BlendFactor factor) -> u8 {
  743. DEBUG_ASSERT(channel < 4);
  744. const Math::Vec4<u8> blend_const = {
  745. static_cast<u8>(output_merger.blend_const.r),
  746. static_cast<u8>(output_merger.blend_const.g),
  747. static_cast<u8>(output_merger.blend_const.b),
  748. static_cast<u8>(output_merger.blend_const.a)
  749. };
  750. switch (factor) {
  751. case Regs::BlendFactor::Zero:
  752. return 0;
  753. case Regs::BlendFactor::One:
  754. return 255;
  755. case Regs::BlendFactor::SourceColor:
  756. return combiner_output[channel];
  757. case Regs::BlendFactor::OneMinusSourceColor:
  758. return 255 - combiner_output[channel];
  759. case Regs::BlendFactor::DestColor:
  760. return dest[channel];
  761. case Regs::BlendFactor::OneMinusDestColor:
  762. return 255 - dest[channel];
  763. case Regs::BlendFactor::SourceAlpha:
  764. return combiner_output.a();
  765. case Regs::BlendFactor::OneMinusSourceAlpha:
  766. return 255 - combiner_output.a();
  767. case Regs::BlendFactor::DestAlpha:
  768. return dest.a();
  769. case Regs::BlendFactor::OneMinusDestAlpha:
  770. return 255 - dest.a();
  771. case Regs::BlendFactor::ConstantColor:
  772. return blend_const[channel];
  773. case Regs::BlendFactor::OneMinusConstantColor:
  774. return 255 - blend_const[channel];
  775. case Regs::BlendFactor::ConstantAlpha:
  776. return blend_const.a();
  777. case Regs::BlendFactor::OneMinusConstantAlpha:
  778. return 255 - blend_const.a();
  779. case Regs::BlendFactor::SourceAlphaSaturate:
  780. // Returns 1.0 for the alpha channel
  781. if (channel == 3)
  782. return 255;
  783. return std::min(combiner_output.a(), static_cast<u8>(255 - dest.a()));
  784. default:
  785. LOG_CRITICAL(HW_GPU, "Unknown blend factor %x", factor);
  786. UNIMPLEMENTED();
  787. break;
  788. }
  789. return combiner_output[channel];
  790. };
  791. static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
  792. const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
  793. Regs::BlendEquation equation) {
  794. Math::Vec4<int> result;
  795. auto src_result = (src * srcfactor).Cast<int>();
  796. auto dst_result = (dest * destfactor).Cast<int>();
  797. switch (equation) {
  798. case Regs::BlendEquation::Add:
  799. result = (src_result + dst_result) / 255;
  800. break;
  801. case Regs::BlendEquation::Subtract:
  802. result = (src_result - dst_result) / 255;
  803. break;
  804. case Regs::BlendEquation::ReverseSubtract:
  805. result = (dst_result - src_result) / 255;
  806. break;
  807. // TODO: How do these two actually work?
  808. // OpenGL doesn't include the blend factors in the min/max computations,
  809. // but is this what the 3DS actually does?
  810. case Regs::BlendEquation::Min:
  811. result.r() = std::min(src.r(), dest.r());
  812. result.g() = std::min(src.g(), dest.g());
  813. result.b() = std::min(src.b(), dest.b());
  814. result.a() = std::min(src.a(), dest.a());
  815. break;
  816. case Regs::BlendEquation::Max:
  817. result.r() = std::max(src.r(), dest.r());
  818. result.g() = std::max(src.g(), dest.g());
  819. result.b() = std::max(src.b(), dest.b());
  820. result.a() = std::max(src.a(), dest.a());
  821. break;
  822. default:
  823. LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
  824. UNIMPLEMENTED();
  825. }
  826. return Math::Vec4<u8>(MathUtil::Clamp(result.r(), 0, 255),
  827. MathUtil::Clamp(result.g(), 0, 255),
  828. MathUtil::Clamp(result.b(), 0, 255),
  829. MathUtil::Clamp(result.a(), 0, 255));
  830. };
  831. auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb),
  832. LookupFactor(1, params.factor_source_rgb),
  833. LookupFactor(2, params.factor_source_rgb),
  834. LookupFactor(3, params.factor_source_a));
  835. auto dstfactor = Math::MakeVec(LookupFactor(0, params.factor_dest_rgb),
  836. LookupFactor(1, params.factor_dest_rgb),
  837. LookupFactor(2, params.factor_dest_rgb),
  838. LookupFactor(3, params.factor_dest_a));
  839. blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb);
  840. blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a();
  841. } else {
  842. static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 {
  843. switch (op) {
  844. case Regs::LogicOp::Clear:
  845. return 0;
  846. case Regs::LogicOp::And:
  847. return src & dest;
  848. case Regs::LogicOp::AndReverse:
  849. return src & ~dest;
  850. case Regs::LogicOp::Copy:
  851. return src;
  852. case Regs::LogicOp::Set:
  853. return 255;
  854. case Regs::LogicOp::CopyInverted:
  855. return ~src;
  856. case Regs::LogicOp::NoOp:
  857. return dest;
  858. case Regs::LogicOp::Invert:
  859. return ~dest;
  860. case Regs::LogicOp::Nand:
  861. return ~(src & dest);
  862. case Regs::LogicOp::Or:
  863. return src | dest;
  864. case Regs::LogicOp::Nor:
  865. return ~(src | dest);
  866. case Regs::LogicOp::Xor:
  867. return src ^ dest;
  868. case Regs::LogicOp::Equiv:
  869. return ~(src ^ dest);
  870. case Regs::LogicOp::AndInverted:
  871. return ~src & dest;
  872. case Regs::LogicOp::OrReverse:
  873. return src | ~dest;
  874. case Regs::LogicOp::OrInverted:
  875. return ~src | dest;
  876. }
  877. };
  878. blend_output = Math::MakeVec(
  879. LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op),
  880. LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op),
  881. LogicOp(combiner_output.b(), dest.b(), output_merger.logic_op),
  882. LogicOp(combiner_output.a(), dest.a(), output_merger.logic_op));
  883. }
  884. const Math::Vec4<u8> result = {
  885. output_merger.red_enable ? blend_output.r() : dest.r(),
  886. output_merger.green_enable ? blend_output.g() : dest.g(),
  887. output_merger.blue_enable ? blend_output.b() : dest.b(),
  888. output_merger.alpha_enable ? blend_output.a() : dest.a()
  889. };
  890. if (regs.framebuffer.allow_color_write != 0)
  891. DrawPixel(x >> 4, y >> 4, result);
  892. }
  893. }
  894. }
  895. void ProcessTriangle(const Shader::OutputVertex& v0,
  896. const Shader::OutputVertex& v1,
  897. const Shader::OutputVertex& v2) {
  898. ProcessTriangleInternal(v0, v1, v2);
  899. }
  900. } // namespace Rasterizer
  901. } // namespace Pica