diff options
Diffstat (limited to 'src/video_core/rasterizer.cpp')
| -rw-r--r-- | src/video_core/rasterizer.cpp | 459 |
1 files changed, 348 insertions, 111 deletions
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 025d4e484..17f8f70ca 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include "common/common_types.h" +#include "common/math_util.h" #include "math.h" #include "pica.h" @@ -19,30 +20,60 @@ namespace Rasterizer { static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) { const PAddr addr = registers.framebuffer.GetColorBufferPhysicalAddress(); - u32* color_buffer = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(addr))); - u32 value = (color.a() << 24) | (color.r() << 16) | (color.g() << 8) | color.b(); + u8* color_buffer = Memory::GetPointer(PAddrToVAddr(addr)); - // Assuming RGBA8 format until actual framebuffer format handling is implemented - *(color_buffer + x + y * registers.framebuffer.GetWidth()) = value; + // Similarly to textures, the render framebuffer is laid out from bottom to top, too. + // NOTE: The framebuffer height register contains the actual FB height minus one. + y = (registers.framebuffer.height - y); + + switch (registers.framebuffer.color_format) { + case registers.framebuffer.RGBA8: + { + u8* pixel = color_buffer + (x + y * registers.framebuffer.GetWidth()) * 4; + pixel[3] = color.r(); + pixel[2] = color.g(); + pixel[1] = color.b(); + pixel[0] = color.a(); + break; + } + + default: + LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format); + UNIMPLEMENTED(); + } } static const Math::Vec4<u8> GetPixel(int x, int y) { const PAddr addr = registers.framebuffer.GetColorBufferPhysicalAddress(); - u32* color_buffer_u32 = reinterpret_cast<u32*>(Memory::GetPointer(PAddrToVAddr(addr))); - - u32 value = *(color_buffer_u32 + x + y * registers.framebuffer.GetWidth()); - Math::Vec4<u8> ret; - ret.a() = value >> 24; - ret.r() = (value >> 16) & 0xFF; - ret.g() = (value >> 8) & 0xFF; - ret.b() = value & 0xFF; - return ret; + u8* color_buffer = Memory::GetPointer(PAddrToVAddr(addr)); + + y = (registers.framebuffer.height - y); + + switch (registers.framebuffer.color_format) { + case registers.framebuffer.RGBA8: + { + Math::Vec4<u8> ret; + u8* pixel = color_buffer + (x + y * registers.framebuffer.GetWidth()) * 4; + ret.r() = pixel[3]; + ret.g() = pixel[2]; + ret.b() = pixel[1]; + ret.a() = pixel[0]; + return ret; + } + default: + LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format); + UNIMPLEMENTED(); + } + + return {}; } static u32 GetDepth(int x, int y) { const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress(); u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(addr))); + y = (registers.framebuffer.height - y); + // Assuming 16-bit depth buffer format until actual format handling is implemented return *(depth_buffer + x + y * registers.framebuffer.GetWidth()); } @@ -51,6 +82,8 @@ static void SetDepth(int x, int y, u16 value) { const PAddr addr = registers.framebuffer.GetDepthBufferPhysicalAddress(); u16* depth_buffer = reinterpret_cast<u16*>(Memory::GetPointer(PAddrToVAddr(addr))); + y = (registers.framebuffer.height - y); + // Assuming 16-bit depth buffer format until actual format handling is implemented *(depth_buffer + x + y * registers.framebuffer.GetWidth()) = value; } @@ -90,30 +123,43 @@ static int SignedArea (const Math::Vec2<Fix12P4>& vtx1, return Math::Cross(vec1, vec2).z; }; -void ProcessTriangle(const VertexShader::OutputVertex& v0, - const VertexShader::OutputVertex& v1, - const VertexShader::OutputVertex& v2) +/** + * Helper function for ProcessTriangle with the "reversed" flag to allow for implementing + * culling via recursion. + */ +static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, + const VertexShader::OutputVertex& v1, + const VertexShader::OutputVertex& v2, + bool reversed = false) { // vertex positions in rasterizer coordinates - auto FloatToFix = [](float24 flt) { - return Fix12P4(static_cast<unsigned short>(flt.ToFloat32() * 16.0f)); - }; - auto ScreenToRasterizerCoordinates = [FloatToFix](const Math::Vec3<float24> vec) { - return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)}; - }; + static auto FloatToFix = [](float24 flt) { + // TODO: Rounding here is necessary to prevent garbage pixels at + // triangle borders. Is it that the correct solution, though? + return Fix12P4(static_cast<unsigned short>(round(flt.ToFloat32() * 16.0f))); + }; + static auto ScreenToRasterizerCoordinates = [](const Math::Vec3<float24>& vec) { + return Math::Vec3<Fix12P4>{FloatToFix(vec.x), FloatToFix(vec.y), FloatToFix(vec.z)}; + }; Math::Vec3<Fix12P4> vtxpos[3]{ ScreenToRasterizerCoordinates(v0.screenpos), ScreenToRasterizerCoordinates(v1.screenpos), ScreenToRasterizerCoordinates(v2.screenpos) }; - if (registers.cull_mode == Regs::CullMode::KeepClockWise) { - // Reverse vertex order and use the CCW code path. - std::swap(vtxpos[1], vtxpos[2]); - } + if (registers.cull_mode == Regs::CullMode::KeepAll) { + // Make sure we always end up with a triangle wound counter-clockwise + if (!reversed && SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0) { + ProcessTriangleInternal(v0, v2, v1, true); + return; + } + } else { + if (!reversed && registers.cull_mode == Regs::CullMode::KeepClockWise) { + // Reverse vertex order and use the CCW code path. + ProcessTriangleInternal(v0, v2, v1, true); + return; + } - if (registers.cull_mode != Regs::CullMode::KeepAll) { // Cull away triangles which are wound clockwise. - // TODO: A check for degenerate triangles ("== 0") should be considered for CullMode::KeepAll if (SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0) return; } @@ -155,9 +201,10 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, auto textures = registers.GetTextures(); auto tev_stages = registers.GetTevStages(); + // Enter rasterization loop, starting at the center of the topleft bounding box corner. // TODO: Not sure if looping through x first might be faster - for (u16 y = min_y; y < max_y; y += 0x10) { - for (u16 x = min_x; x < max_x; x += 0x10) { + for (u16 y = min_y + 8; y < max_y; y += 0x10) { + for (u16 x = min_x + 8; x < max_x; x += 0x10) { // Calculate the barycentric coordinates w0, w1 and w2 int w0 = bias0 + SignedArea(vtxpos[1].xy(), vtxpos[2].xy(), {x, y}); @@ -216,11 +263,11 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, if (!texture.enabled) continue; - _dbg_assert_(HW_GPU, 0 != texture.config.address); + DEBUG_ASSERT(0 != texture.config.address); int s = (int)(uv[i].u() * float24::FromFloat32(static_cast<float>(texture.config.width))).ToFloat32(); int t = (int)(uv[i].v() * float24::FromFloat32(static_cast<float>(texture.config.height))).ToFloat32(); - auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) { + static auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val, unsigned size) { switch (mode) { case Regs::TextureConfig::ClampToEdge: val = std::max(val, 0); @@ -228,14 +275,26 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return val; case Regs::TextureConfig::Repeat: - return (int)(((unsigned)val) % size); + return (int)((unsigned)val % size); + + case Regs::TextureConfig::MirroredRepeat: + { + int coord = (int)((unsigned)val % (2 * size)); + if (coord >= size) + coord = 2 * size - 1 - coord; + return coord; + } default: LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode); - _dbg_assert_(HW_GPU, 0); + UNIMPLEMENTED(); return 0; } }; + + // Textures are laid out from bottom to top, hence we invert the t coordinate. + // NOTE: This may not be the right place for the inversion. + // TODO: Check if this applies to ETC textures, too. s = GetWrappedTexCoord(texture.config.wrap_s, s, texture.config.width); t = texture.config.height - 1 - GetWrappedTexCoord(texture.config.wrap_t, t, texture.config.height); @@ -260,9 +319,11 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, using AlphaModifier = Regs::TevStageConfig::AlphaModifier; using Operation = Regs::TevStageConfig::Operation; - auto GetColorSource = [&](Source source) -> Math::Vec4<u8> { + auto GetSource = [&](Source source) -> Math::Vec4<u8> { switch (source) { + // TODO: What's the difference between these two? case Source::PrimaryColor: + case Source::PrimaryFragmentColor: return primary_color; case Source::Texture0: @@ -282,41 +343,13 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, default: LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source); - _dbg_assert_(HW_GPU, 0); + UNIMPLEMENTED(); return {}; } }; - auto GetAlphaSource = [&](Source source) -> u8 { - switch (source) { - case Source::PrimaryColor: - return primary_color.a(); - - case Source::Texture0: - return texture_color[0].a(); - - case Source::Texture1: - return texture_color[1].a(); - - case Source::Texture2: - return texture_color[2].a(); - - case Source::Constant: - return tev_stage.const_a; - - case Source::Previous: - return combiner_output.a(); - - default: - LOG_ERROR(HW_GPU, "Unknown alpha combiner source %d\n", (int)source); - _dbg_assert_(HW_GPU, 0); - return 0; - } - }; - static auto GetColorModifier = [](ColorModifier factor, const Math::Vec4<u8>& values) -> Math::Vec3<u8> { - switch (factor) - { + switch (factor) { case ColorModifier::SourceColor: return values.rgb(); @@ -324,27 +357,56 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>(); case ColorModifier::SourceAlpha: - return { values.a(), values.a(), values.a() }; + return values.aaa(); - default: - LOG_ERROR(HW_GPU, "Unknown color factor %d\n", (int)factor); - _dbg_assert_(HW_GPU, 0); - return {}; + case ColorModifier::OneMinusSourceAlpha: + return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>(); + + case ColorModifier::SourceRed: + return values.rrr(); + + case ColorModifier::OneMinusSourceRed: + return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>(); + + case ColorModifier::SourceGreen: + return values.ggg(); + + case ColorModifier::OneMinusSourceGreen: + return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>(); + + case ColorModifier::SourceBlue: + return values.bbb(); + + case ColorModifier::OneMinusSourceBlue: + return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>(); } }; - static auto GetAlphaModifier = [](AlphaModifier factor, u8 value) -> u8 { + static auto GetAlphaModifier = [](AlphaModifier factor, const Math::Vec4<u8>& values) -> u8 { switch (factor) { case AlphaModifier::SourceAlpha: - return value; + return values.a(); case AlphaModifier::OneMinusSourceAlpha: - return 255 - value; + return 255 - values.a(); - default: - LOG_ERROR(HW_GPU, "Unknown alpha factor %d\n", (int)factor); - _dbg_assert_(HW_GPU, 0); - return 0; + case AlphaModifier::SourceRed: + return values.r(); + + case AlphaModifier::OneMinusSourceRed: + return 255 - values.r(); + + case AlphaModifier::SourceGreen: + return values.g(); + + case AlphaModifier::OneMinusSourceGreen: + return 255 - values.g(); + + case AlphaModifier::SourceBlue: + return values.b(); + + case AlphaModifier::OneMinusSourceBlue: + return 255 - values.b(); } }; @@ -377,9 +439,28 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, return result.Cast<u8>(); } + case Operation::MultiplyThenAdd: + { + auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255; + result.r() = std::min(255, result.r()); + result.g() = std::min(255, result.g()); + result.b() = std::min(255, result.b()); + return result.Cast<u8>(); + } + + case Operation::AddThenMultiply: + { + auto result = input[0] + input[1]; + result.r() = std::min(255, result.r()); + result.g() = std::min(255, result.g()); + result.b() = std::min(255, result.b()); + result = (result * input[2].Cast<int>()) / 255; + return result.Cast<u8>(); + } + default: LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op); - _dbg_assert_(HW_GPU, 0); + UNIMPLEMENTED(); return {}; } }; @@ -401,9 +482,15 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, case Operation::Subtract: return std::max(0, (int)input[0] - (int)input[1]); + case Operation::MultiplyThenAdd: + return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255); + + case Operation::AddThenMultiply: + return (std::min(255, (input[0] + input[1])) * input[2]) / 255; + default: LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op); - _dbg_assert_(HW_GPU, 0); + UNIMPLEMENTED(); return 0; } }; @@ -414,26 +501,67 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, // combiner_output.rgb(), but instead store it in a temporary variable until // alpha combining has been done. Math::Vec3<u8> color_result[3] = { - GetColorModifier(tev_stage.color_modifier1, GetColorSource(tev_stage.color_source1)), - GetColorModifier(tev_stage.color_modifier2, GetColorSource(tev_stage.color_source2)), - GetColorModifier(tev_stage.color_modifier3, GetColorSource(tev_stage.color_source3)) + GetColorModifier(tev_stage.color_modifier1, GetSource(tev_stage.color_source1)), + GetColorModifier(tev_stage.color_modifier2, GetSource(tev_stage.color_source2)), + GetColorModifier(tev_stage.color_modifier3, GetSource(tev_stage.color_source3)) }; auto color_output = ColorCombine(tev_stage.color_op, color_result); // alpha combiner std::array<u8,3> alpha_result = { - GetAlphaModifier(tev_stage.alpha_modifier1, GetAlphaSource(tev_stage.alpha_source1)), - GetAlphaModifier(tev_stage.alpha_modifier2, GetAlphaSource(tev_stage.alpha_source2)), - GetAlphaModifier(tev_stage.alpha_modifier3, GetAlphaSource(tev_stage.alpha_source3)) + GetAlphaModifier(tev_stage.alpha_modifier1, GetSource(tev_stage.alpha_source1)), + GetAlphaModifier(tev_stage.alpha_modifier2, GetSource(tev_stage.alpha_source2)), + GetAlphaModifier(tev_stage.alpha_modifier3, GetSource(tev_stage.alpha_source3)) }; auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result); combiner_output = Math::MakeVec(color_output, alpha_output); } + if (registers.output_merger.alpha_test.enable) { + bool pass = false; + + switch (registers.output_merger.alpha_test.func) { + case registers.output_merger.Never: + pass = false; + break; + + case registers.output_merger.Always: + pass = true; + break; + + case registers.output_merger.Equal: + pass = combiner_output.a() == registers.output_merger.alpha_test.ref; + break; + + case registers.output_merger.NotEqual: + pass = combiner_output.a() != registers.output_merger.alpha_test.ref; + break; + + case registers.output_merger.LessThan: + pass = combiner_output.a() < registers.output_merger.alpha_test.ref; + break; + + case registers.output_merger.LessThanOrEqual: + pass = combiner_output.a() <= registers.output_merger.alpha_test.ref; + break; + + case registers.output_merger.GreaterThan: + pass = combiner_output.a() > registers.output_merger.alpha_test.ref; + break; + + case registers.output_merger.GreaterThanOrEqual: + pass = combiner_output.a() >= registers.output_merger.alpha_test.ref; + break; + } + + if (!pass) + continue; + } + // TODO: Does depth indeed only get written even if depth testing is enabled? if (registers.output_merger.depth_test_enable) { - u16 z = (u16)(-(v0.screenpos[2].ToFloat32() * w0 + + u16 z = (u16)((v0.screenpos[2].ToFloat32() * w0 + v1.screenpos[2].ToFloat32() * w1 + v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); u16 ref_z = GetDepth(x >> 4, y >> 4); @@ -441,20 +569,36 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, bool pass = false; switch (registers.output_merger.depth_test_func) { + case registers.output_merger.Never: + pass = false; + break; + case registers.output_merger.Always: pass = true; break; + case registers.output_merger.Equal: + pass = z == ref_z; + break; + + case registers.output_merger.NotEqual: + pass = z != ref_z; + break; + case registers.output_merger.LessThan: pass = z < ref_z; break; + case registers.output_merger.LessThanOrEqual: + pass = z <= ref_z; + break; + case registers.output_merger.GreaterThan: pass = z > ref_z; break; - default: - LOG_ERROR(HW_GPU, "Unknown depth test function %x", registers.output_merger.depth_test_func.Value()); + case registers.output_merger.GreaterThanOrEqual: + pass = z >= ref_z; break; } @@ -466,33 +610,64 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, } auto dest = GetPixel(x >> 4, y >> 4); + Math::Vec4<u8> blend_output = combiner_output; if (registers.output_merger.alphablend_enable) { auto params = registers.output_merger.alpha_blending; auto LookupFactorRGB = [&](decltype(params)::BlendFactor factor) -> Math::Vec3<u8> { - switch(factor) { + switch (factor) { case params.Zero: return Math::Vec3<u8>(0, 0, 0); case params.One: return Math::Vec3<u8>(255, 255, 255); + case params.SourceColor: + return combiner_output.rgb(); + + case params.OneMinusSourceColor: + return Math::Vec3<u8>(255 - combiner_output.r(), 255 - combiner_output.g(), 255 - combiner_output.b()); + + case params.DestColor: + return dest.rgb(); + + case params.OneMinusDestColor: + return Math::Vec3<u8>(255 - dest.r(), 255 - dest.g(), 255 - dest.b()); + case params.SourceAlpha: - return Math::MakeVec(combiner_output.a(), combiner_output.a(), combiner_output.a()); + return Math::Vec3<u8>(combiner_output.a(), combiner_output.a(), combiner_output.a()); case params.OneMinusSourceAlpha: - return Math::Vec3<u8>(255-combiner_output.a(), 255-combiner_output.a(), 255-combiner_output.a()); + return Math::Vec3<u8>(255 - combiner_output.a(), 255 - combiner_output.a(), 255 - combiner_output.a()); + + case params.DestAlpha: + return Math::Vec3<u8>(dest.a(), dest.a(), dest.a()); + + case params.OneMinusDestAlpha: + return Math::Vec3<u8>(255 - dest.a(), 255 - dest.a(), 255 - dest.a()); + + case params.ConstantColor: + return Math::Vec3<u8>(registers.output_merger.blend_const.r, registers.output_merger.blend_const.g, registers.output_merger.blend_const.b); + + case params.OneMinusConstantColor: + return Math::Vec3<u8>(255 - registers.output_merger.blend_const.r, 255 - registers.output_merger.blend_const.g, 255 - registers.output_merger.blend_const.b); + + case params.ConstantAlpha: + return Math::Vec3<u8>(registers.output_merger.blend_const.a, registers.output_merger.blend_const.a, registers.output_merger.blend_const.a); + + case params.OneMinusConstantAlpha: + return Math::Vec3<u8>(255 - registers.output_merger.blend_const.a, 255 - registers.output_merger.blend_const.a, 255 - registers.output_merger.blend_const.a); default: LOG_CRITICAL(HW_GPU, "Unknown color blend factor %x", factor); - exit(0); + UNIMPLEMENTED(); break; } }; auto LookupFactorA = [&](decltype(params)::BlendFactor factor) -> u8 { - switch(factor) { + switch (factor) { case params.Zero: return 0; @@ -505,43 +680,105 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, case params.OneMinusSourceAlpha: return 255 - combiner_output.a(); + case params.DestAlpha: + return dest.a(); + + case params.OneMinusDestAlpha: + return 255 - dest.a(); + + case params.ConstantAlpha: + return registers.output_merger.blend_const.a; + + case params.OneMinusConstantAlpha: + return 255 - registers.output_merger.blend_const.a; + default: LOG_CRITICAL(HW_GPU, "Unknown alpha blend factor %x", factor); - exit(0); + UNIMPLEMENTED(); break; } }; + using BlendEquation = decltype(params)::BlendEquation; + static auto EvaluateBlendEquation = [](const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor, + const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor, + BlendEquation equation) { + Math::Vec4<int> result; + + auto src_result = (src * srcfactor).Cast<int>(); + auto dst_result = (dest * destfactor).Cast<int>(); + + switch (equation) { + case BlendEquation::Add: + result = (src_result + dst_result) / 255; + break; + + case BlendEquation::Subtract: + result = (src_result - dst_result) / 255; + break; + + case BlendEquation::ReverseSubtract: + result = (dst_result - src_result) / 255; + break; + + // TODO: How do these two actually work? + // OpenGL doesn't include the blend factors in the min/max computations, + // but is this what the 3DS actually does? + case BlendEquation::Min: + result.r() = std::min(src.r(), dest.r()); + result.g() = std::min(src.g(), dest.g()); + result.b() = std::min(src.b(), dest.b()); + result.a() = std::min(src.a(), dest.a()); + break; + + case BlendEquation::Max: + result.r() = std::max(src.r(), dest.r()); + result.g() = std::max(src.g(), dest.g()); + result.b() = std::max(src.b(), dest.b()); + result.a() = std::max(src.a(), dest.a()); + break; + + default: + LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation); + UNIMPLEMENTED(); + } + + return Math::Vec4<u8>(MathUtil::Clamp(result.r(), 0, 255), + MathUtil::Clamp(result.g(), 0, 255), + MathUtil::Clamp(result.b(), 0, 255), + MathUtil::Clamp(result.a(), 0, 255)); + }; + auto srcfactor = Math::MakeVec(LookupFactorRGB(params.factor_source_rgb), LookupFactorA(params.factor_source_a)); auto dstfactor = Math::MakeVec(LookupFactorRGB(params.factor_dest_rgb), LookupFactorA(params.factor_dest_a)); - switch (params.blend_equation_rgb) { - case params.Add: - { - auto result = (combiner_output * srcfactor + dest * dstfactor) / 255; - result.r() = std::min(255, result.r()); - result.g() = std::min(255, result.g()); - result.b() = std::min(255, result.b()); - combiner_output = result.Cast<u8>(); - break; - } - - default: - LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", params.blend_equation_rgb.Value()); - exit(0); - } + blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); + blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); } else { LOG_CRITICAL(HW_GPU, "logic op: %x", registers.output_merger.logic_op); - exit(0); + UNIMPLEMENTED(); } - DrawPixel(x >> 4, y >> 4, combiner_output); + const Math::Vec4<u8> result = { + registers.output_merger.red_enable ? blend_output.r() : dest.r(), + registers.output_merger.green_enable ? blend_output.g() : dest.g(), + registers.output_merger.blue_enable ? blend_output.b() : dest.b(), + registers.output_merger.alpha_enable ? blend_output.a() : dest.a() + }; + + DrawPixel(x >> 4, y >> 4, result); } } } +void ProcessTriangle(const VertexShader::OutputVertex& v0, + const VertexShader::OutputVertex& v1, + const VertexShader::OutputVertex& v2) { + ProcessTriangleInternal(v0, v1, v2); +} + } // namespace Rasterizer } // namespace Pica |
