diff options
| author | 2017-01-28 15:59:36 -0800 | |
|---|---|---|
| committer | 2017-02-12 18:08:11 -0800 | |
| commit | e24717bca04a51fe185e5dbbb4918e31c923e8fa (patch) | |
| tree | 2895fb88f190a7cc49ef19835cff6aa3d85fc3ff /src/video_core/swrasterizer/clipper.cpp | |
| parent | video_core/shader: Document sanitized MUL operation (diff) | |
| download | yuzu-e24717bca04a51fe185e5dbbb4918e31c923e8fa.tar.gz yuzu-e24717bca04a51fe185e5dbbb4918e31c923e8fa.tar.xz yuzu-e24717bca04a51fe185e5dbbb4918e31c923e8fa.zip | |
VideoCore: Move software rasterizer files to sub-directory
Diffstat (limited to 'src/video_core/swrasterizer/clipper.cpp')
| -rw-r--r-- | src/video_core/swrasterizer/clipper.cpp | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/video_core/swrasterizer/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp new file mode 100644 index 000000000..2d80822d9 --- /dev/null +++ b/src/video_core/swrasterizer/clipper.cpp | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <boost/container/static_vector.hpp> | ||
| 9 | #include <boost/container/vector.hpp> | ||
| 10 | #include "common/bit_field.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/vector_math.h" | ||
| 14 | #include "video_core/pica_state.h" | ||
| 15 | #include "video_core/pica_types.h" | ||
| 16 | #include "video_core/shader/shader.h" | ||
| 17 | #include "video_core/swrasterizer/clipper.h" | ||
| 18 | #include "video_core/swrasterizer/rasterizer.h" | ||
| 19 | |||
| 20 | using Pica::Rasterizer::Vertex; | ||
| 21 | |||
| 22 | namespace Pica { | ||
| 23 | |||
| 24 | namespace Clipper { | ||
| 25 | |||
| 26 | struct ClippingEdge { | ||
| 27 | public: | ||
| 28 | ClippingEdge(Math::Vec4<float24> coeffs, Math::Vec4<float24> bias = Math::Vec4<float24>( | ||
| 29 | float24::FromFloat32(0), float24::FromFloat32(0), | ||
| 30 | float24::FromFloat32(0), float24::FromFloat32(0))) | ||
| 31 | : coeffs(coeffs), bias(bias) {} | ||
| 32 | |||
| 33 | bool IsInside(const Vertex& vertex) const { | ||
| 34 | return Math::Dot(vertex.pos + bias, coeffs) <= float24::FromFloat32(0); | ||
| 35 | } | ||
| 36 | |||
| 37 | bool IsOutSide(const Vertex& vertex) const { | ||
| 38 | return !IsInside(vertex); | ||
| 39 | } | ||
| 40 | |||
| 41 | Vertex GetIntersection(const Vertex& v0, const Vertex& v1) const { | ||
| 42 | float24 dp = Math::Dot(v0.pos + bias, coeffs); | ||
| 43 | float24 dp_prev = Math::Dot(v1.pos + bias, coeffs); | ||
| 44 | float24 factor = dp_prev / (dp_prev - dp); | ||
| 45 | |||
| 46 | return Vertex::Lerp(factor, v0, v1); | ||
| 47 | } | ||
| 48 | |||
| 49 | private: | ||
| 50 | float24 pos; | ||
| 51 | Math::Vec4<float24> coeffs; | ||
| 52 | Math::Vec4<float24> bias; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static void InitScreenCoordinates(Vertex& vtx) { | ||
| 56 | struct { | ||
| 57 | float24 halfsize_x; | ||
| 58 | float24 offset_x; | ||
| 59 | float24 halfsize_y; | ||
| 60 | float24 offset_y; | ||
| 61 | float24 zscale; | ||
| 62 | float24 offset_z; | ||
| 63 | } viewport; | ||
| 64 | |||
| 65 | const auto& regs = g_state.regs; | ||
| 66 | viewport.halfsize_x = float24::FromRaw(regs.rasterizer.viewport_size_x); | ||
| 67 | viewport.halfsize_y = float24::FromRaw(regs.rasterizer.viewport_size_y); | ||
| 68 | viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.rasterizer.viewport_corner.x)); | ||
| 69 | viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.rasterizer.viewport_corner.y)); | ||
| 70 | |||
| 71 | float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w; | ||
| 72 | vtx.color *= inv_w; | ||
| 73 | vtx.view *= inv_w; | ||
| 74 | vtx.quat *= inv_w; | ||
| 75 | vtx.tc0 *= inv_w; | ||
| 76 | vtx.tc1 *= inv_w; | ||
| 77 | vtx.tc2 *= inv_w; | ||
| 78 | vtx.pos.w = inv_w; | ||
| 79 | |||
| 80 | vtx.screenpos[0] = | ||
| 81 | (vtx.pos.x * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_x + viewport.offset_x; | ||
| 82 | vtx.screenpos[1] = | ||
| 83 | (vtx.pos.y * inv_w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y; | ||
| 84 | vtx.screenpos[2] = vtx.pos.z * inv_w; | ||
| 85 | } | ||
| 86 | |||
| 87 | void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) { | ||
| 88 | using boost::container::static_vector; | ||
| 89 | |||
| 90 | // Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at | ||
| 91 | // the new edge (or less in degenerate cases). As such, we can say that each clipping plane | ||
| 92 | // introduces at most 1 new vertex to the polygon. Since we start with a triangle and have a | ||
| 93 | // fixed 6 clipping planes, the maximum number of vertices of the clipped polygon is 3 + 6 = 9. | ||
| 94 | static const size_t MAX_VERTICES = 9; | ||
| 95 | static_vector<Vertex, MAX_VERTICES> buffer_a = {v0, v1, v2}; | ||
| 96 | static_vector<Vertex, MAX_VERTICES> buffer_b; | ||
| 97 | auto* output_list = &buffer_a; | ||
| 98 | auto* input_list = &buffer_b; | ||
| 99 | |||
| 100 | // NOTE: We clip against a w=epsilon plane to guarantee that the output has a positive w value. | ||
| 101 | // TODO: Not sure if this is a valid approach. Also should probably instead use the smallest | ||
| 102 | // epsilon possible within float24 accuracy. | ||
| 103 | static const float24 EPSILON = float24::FromFloat32(0.00001f); | ||
| 104 | static const float24 f0 = float24::FromFloat32(0.0); | ||
| 105 | static const float24 f1 = float24::FromFloat32(1.0); | ||
| 106 | static const std::array<ClippingEdge, 7> clipping_edges = {{ | ||
| 107 | {Math::MakeVec(f1, f0, f0, -f1)}, // x = +w | ||
| 108 | {Math::MakeVec(-f1, f0, f0, -f1)}, // x = -w | ||
| 109 | {Math::MakeVec(f0, f1, f0, -f1)}, // y = +w | ||
| 110 | {Math::MakeVec(f0, -f1, f0, -f1)}, // y = -w | ||
| 111 | {Math::MakeVec(f0, f0, f1, f0)}, // z = 0 | ||
| 112 | {Math::MakeVec(f0, f0, -f1, -f1)}, // z = -w | ||
| 113 | {Math::MakeVec(f0, f0, f0, -f1), Math::Vec4<float24>(f0, f0, f0, EPSILON)}, // w = EPSILON | ||
| 114 | }}; | ||
| 115 | |||
| 116 | // TODO: If one vertex lies outside one of the depth clipping planes, some platforms (e.g. Wii) | ||
| 117 | // drop the whole primitive instead of clipping the primitive properly. We should test if | ||
| 118 | // this happens on the 3DS, too. | ||
| 119 | |||
| 120 | // Simple implementation of the Sutherland-Hodgman clipping algorithm. | ||
| 121 | // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here) | ||
| 122 | for (auto edge : clipping_edges) { | ||
| 123 | |||
| 124 | std::swap(input_list, output_list); | ||
| 125 | output_list->clear(); | ||
| 126 | |||
| 127 | const Vertex* reference_vertex = &input_list->back(); | ||
| 128 | |||
| 129 | for (const auto& vertex : *input_list) { | ||
| 130 | // NOTE: This algorithm changes vertex order in some cases! | ||
| 131 | if (edge.IsInside(vertex)) { | ||
| 132 | if (edge.IsOutSide(*reference_vertex)) { | ||
| 133 | output_list->push_back(edge.GetIntersection(vertex, *reference_vertex)); | ||
| 134 | } | ||
| 135 | |||
| 136 | output_list->push_back(vertex); | ||
| 137 | } else if (edge.IsInside(*reference_vertex)) { | ||
| 138 | output_list->push_back(edge.GetIntersection(vertex, *reference_vertex)); | ||
| 139 | } | ||
| 140 | reference_vertex = &vertex; | ||
| 141 | } | ||
| 142 | |||
| 143 | // Need to have at least a full triangle to continue... | ||
| 144 | if (output_list->size() < 3) | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | |||
| 148 | InitScreenCoordinates((*output_list)[0]); | ||
| 149 | InitScreenCoordinates((*output_list)[1]); | ||
| 150 | |||
| 151 | for (size_t i = 0; i < output_list->size() - 2; i++) { | ||
| 152 | Vertex& vtx0 = (*output_list)[0]; | ||
| 153 | Vertex& vtx1 = (*output_list)[i + 1]; | ||
| 154 | Vertex& vtx2 = (*output_list)[i + 2]; | ||
| 155 | |||
| 156 | InitScreenCoordinates(vtx2); | ||
| 157 | |||
| 158 | LOG_TRACE(Render_Software, | ||
| 159 | "Triangle %lu/%lu at position (%.3f, %.3f, %.3f, %.3f), " | ||
| 160 | "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " | ||
| 161 | "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", | ||
| 162 | i + 1, output_list->size() - 2, vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), | ||
| 163 | vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(), vtx1.pos.x.ToFloat32(), | ||
| 164 | vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), | ||
| 165 | vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), | ||
| 166 | vtx2.pos.w.ToFloat32(), vtx0.screenpos.x.ToFloat32(), | ||
| 167 | vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), | ||
| 168 | vtx1.screenpos.x.ToFloat32(), vtx1.screenpos.y.ToFloat32(), | ||
| 169 | vtx1.screenpos.z.ToFloat32(), vtx2.screenpos.x.ToFloat32(), | ||
| 170 | vtx2.screenpos.y.ToFloat32(), vtx2.screenpos.z.ToFloat32()); | ||
| 171 | |||
| 172 | Rasterizer::ProcessTriangle(vtx0, vtx1, vtx2); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | } // namespace | ||
| 177 | |||
| 178 | } // namespace | ||