mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Pica: Add triangle clipper.
This commit is contained in:
		
							parent
							
								
									9a76a2d061
								
							
						
					
					
						commit
						94aa9da562
					
				
					 7 changed files with 230 additions and 8 deletions
				
			
		|  | @ -1,11 +1,13 @@ | |||
| set(SRCS    command_processor.cpp | ||||
| set(SRCS    clipper.cpp | ||||
|             command_processor.cpp | ||||
|             primitive_assembly.cpp | ||||
|             utils.cpp | ||||
|             vertex_shader.cpp | ||||
|             video_core.cpp | ||||
|             renderer_opengl/renderer_opengl.cpp) | ||||
| 
 | ||||
| set(HEADERS command_processor.h | ||||
| set(HEADERS clipper.h | ||||
|             command_processor.h | ||||
|             math.h | ||||
|             primitive_assembly.h | ||||
|             utils.h | ||||
|  |  | |||
							
								
								
									
										178
									
								
								src/video_core/clipper.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								src/video_core/clipper.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "clipper.h" | ||||
| #include "pica.h" | ||||
| #include "vertex_shader.h" | ||||
| 
 | ||||
| namespace Pica { | ||||
| 
 | ||||
| namespace Clipper { | ||||
| 
 | ||||
| struct ClippingEdge { | ||||
| public: | ||||
|     enum Type { | ||||
|         POS_X = 0, | ||||
|         NEG_X = 1, | ||||
|         POS_Y = 2, | ||||
|         NEG_Y = 3, | ||||
|         POS_Z = 4, | ||||
|         NEG_Z = 5, | ||||
|     }; | ||||
| 
 | ||||
|     ClippingEdge(Type type, float24 position) : type(type), pos(position) {} | ||||
| 
 | ||||
|     bool IsInside(const OutputVertex& vertex) const { | ||||
|         switch (type) { | ||||
|         case POS_X: return vertex.pos.x <= pos * vertex.pos.w; | ||||
|         case NEG_X: return vertex.pos.x >= pos * vertex.pos.w; | ||||
|         case POS_Y: return vertex.pos.y <= pos * vertex.pos.w; | ||||
|         case NEG_Y: return vertex.pos.y >= pos * vertex.pos.w; | ||||
| 
 | ||||
|         // TODO: Check z compares ... should be 0..1 instead?
 | ||||
|         case POS_Z: return vertex.pos.z <= pos * vertex.pos.w; | ||||
| 
 | ||||
|         default: | ||||
|         case NEG_Z: return vertex.pos.z >= pos * vertex.pos.w; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool IsOutSide(const OutputVertex& vertex) const { | ||||
|         return !IsInside(vertex); | ||||
|     } | ||||
| 
 | ||||
|     OutputVertex GetIntersection(const OutputVertex& v0, const OutputVertex& v1) const { | ||||
|         auto dotpr = [this](const OutputVertex& vtx) { | ||||
|             switch (type) { | ||||
|             case POS_X: return vtx.pos.x - vtx.pos.w; | ||||
|             case NEG_X: return -vtx.pos.x - vtx.pos.w; | ||||
|             case POS_Y: return vtx.pos.y - vtx.pos.w; | ||||
|             case NEG_Y: return -vtx.pos.y - vtx.pos.w; | ||||
| 
 | ||||
|             // TODO: Verify z clipping
 | ||||
|             case POS_Z: return vtx.pos.z - vtx.pos.w; | ||||
| 
 | ||||
|             default: | ||||
|             case NEG_Z: return -vtx.pos.w; | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         float24 dp = dotpr(v0); | ||||
|         float24 dp_prev = dotpr(v1); | ||||
|         float24 factor = dp_prev / (dp_prev - dp); | ||||
| 
 | ||||
|         return OutputVertex::Lerp(factor, v0, v1); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     Type type; | ||||
|     float24 pos; | ||||
| }; | ||||
| 
 | ||||
| static void InitScreenCoordinates(OutputVertex& vtx) | ||||
| { | ||||
|     struct { | ||||
|         float24 halfsize_x; | ||||
|         float24 offset_x; | ||||
|         float24 halfsize_y; | ||||
|         float24 offset_y; | ||||
|         float24 zscale; | ||||
|         float24 offset_z; | ||||
|     } viewport; | ||||
| 
 | ||||
|     viewport.halfsize_x = float24::FromRawFloat24(registers.viewport_size_x); | ||||
|     viewport.halfsize_y = float24::FromRawFloat24(registers.viewport_size_y); | ||||
|     viewport.offset_x   = float24::FromFloat32(registers.viewport_corner.x); | ||||
|     viewport.offset_y   = float24::FromFloat32(registers.viewport_corner.y); | ||||
|     viewport.zscale     = float24::FromRawFloat24(registers.viewport_depth_range); | ||||
|     viewport.offset_z   = float24::FromRawFloat24(registers.viewport_depth_far_plane); | ||||
| 
 | ||||
|     // TODO: Not sure why the viewport width needs to be divided by 2 but the viewport height does not
 | ||||
|     vtx.screenpos[0] = (vtx.pos.x / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_x / float24::FromFloat32(2.0) + viewport.offset_x; | ||||
|     vtx.screenpos[1] = (vtx.pos.y / vtx.pos.w + float24::FromFloat32(1.0)) * viewport.halfsize_y + viewport.offset_y; | ||||
|     vtx.screenpos[2] = viewport.offset_z - vtx.pos.z / vtx.pos.w * viewport.zscale; | ||||
| } | ||||
| 
 | ||||
| void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) { | ||||
| 
 | ||||
|     // TODO (neobrain):
 | ||||
|     // The list of output vertices has some fixed maximum size,
 | ||||
|     // however I haven't taken the time to figure out what it is exactly.
 | ||||
|     // For now, we hence just assume a maximal size of 1000 vertices.
 | ||||
|     const size_t max_vertices = 1000; | ||||
|     std::vector<OutputVertex> buffer_vertices; | ||||
|     std::vector<OutputVertex*> output_list{ &v0, &v1, &v2 }; | ||||
| 
 | ||||
|     // Make sure to reserve space for all vertices.
 | ||||
|     // Without this, buffer reallocation would invalidate references.
 | ||||
|     buffer_vertices.reserve(max_vertices); | ||||
| 
 | ||||
|     // Simple implementation of the Sutherland-Hodgman clipping algorithm.
 | ||||
|     // TODO: Make this less inefficient (currently lots of useless buffering overhead happens here)
 | ||||
|     for (auto edge : { ClippingEdge(ClippingEdge::POS_X, float24::FromFloat32(+1.0)), | ||||
|                        ClippingEdge(ClippingEdge::NEG_X, float24::FromFloat32(-1.0)), | ||||
|                        ClippingEdge(ClippingEdge::POS_Y, float24::FromFloat32(+1.0)), | ||||
|                        ClippingEdge(ClippingEdge::NEG_Y, float24::FromFloat32(-1.0)), | ||||
|                        ClippingEdge(ClippingEdge::POS_Z, float24::FromFloat32(+1.0)), | ||||
|                        ClippingEdge(ClippingEdge::NEG_Z, float24::FromFloat32(-1.0)) }) { | ||||
| 
 | ||||
|         const std::vector<OutputVertex*> input_list = output_list; | ||||
|         output_list.clear(); | ||||
| 
 | ||||
|         const OutputVertex* reference_vertex = input_list.back(); | ||||
| 
 | ||||
|         for (const auto& vertex : input_list) { | ||||
|             // NOTE: This algorithm changes vertex order in some cases!
 | ||||
|             if (edge.IsInside(*vertex)) { | ||||
|                 if (edge.IsOutSide(*reference_vertex)) { | ||||
|                     buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex)); | ||||
|                     output_list.push_back(&(buffer_vertices.back())); | ||||
|                 } | ||||
| 
 | ||||
|                 output_list.push_back(vertex); | ||||
|             } else if (edge.IsInside(*reference_vertex)) { | ||||
|                 buffer_vertices.push_back(edge.GetIntersection(*vertex, *reference_vertex)); | ||||
|                 output_list.push_back(&(buffer_vertices.back())); | ||||
|             } | ||||
| 
 | ||||
|             reference_vertex = vertex; | ||||
|         } | ||||
| 
 | ||||
|         // Need to have at least a full triangle to continue...
 | ||||
|         if (output_list.size() < 3) | ||||
|             return; | ||||
|     } | ||||
| 
 | ||||
|     InitScreenCoordinates(*(output_list[0])); | ||||
|     InitScreenCoordinates(*(output_list[1])); | ||||
| 
 | ||||
|     for (int i = 0; i < output_list.size() - 2; i ++) { | ||||
|         OutputVertex& vtx0 = *(output_list[0]); | ||||
|         OutputVertex& vtx1 = *(output_list[i+1]); | ||||
|         OutputVertex& vtx2 = *(output_list[i+2]); | ||||
| 
 | ||||
|         InitScreenCoordinates(vtx2); | ||||
| 
 | ||||
|         DEBUG_LOG(GPU, | ||||
|                   "Triangle %d/%d (%d buffer vertices) at position (%.3f, %.3f, %.3f, %.3f), " | ||||
|                   "(%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f) and " | ||||
|                   "screen position (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f)", | ||||
|                   i,output_list.size(), buffer_vertices.size(), | ||||
|                   vtx0.pos.x.ToFloat32(), vtx0.pos.y.ToFloat32(), vtx0.pos.z.ToFloat32(), vtx0.pos.w.ToFloat32(),output_list.size(), | ||||
|                   vtx1.pos.x.ToFloat32(), vtx1.pos.y.ToFloat32(), vtx1.pos.z.ToFloat32(), vtx1.pos.w.ToFloat32(), | ||||
|                   vtx2.pos.x.ToFloat32(), vtx2.pos.y.ToFloat32(), vtx2.pos.z.ToFloat32(), vtx2.pos.w.ToFloat32(), | ||||
|                   vtx0.screenpos.x.ToFloat32(), vtx0.screenpos.y.ToFloat32(), vtx0.screenpos.z.ToFloat32(), | ||||
|                   vtx1.screenpos.x.ToFloat32(), vtx1.screenpos.y.ToFloat32(), vtx1.screenpos.z.ToFloat32(), | ||||
|                   vtx2.screenpos.x.ToFloat32(), vtx2.screenpos.y.ToFloat32(), vtx2.screenpos.z.ToFloat32()); | ||||
| 
 | ||||
|         // TODO: Send triangle to rasterizer
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| } // namespace
 | ||||
							
								
								
									
										21
									
								
								src/video_core/clipper.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/video_core/clipper.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| namespace Pica { | ||||
| 
 | ||||
| namespace VertexShader { | ||||
|     struct OutputVertex; | ||||
| } | ||||
| 
 | ||||
| namespace Clipper { | ||||
| 
 | ||||
| using VertexShader::OutputVertex; | ||||
| 
 | ||||
| void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2); | ||||
| 
 | ||||
| } // namespace
 | ||||
| 
 | ||||
| } // namespace
 | ||||
|  | @ -50,7 +50,12 @@ struct Regs { | |||
|     INSERT_PADDING_WORDS(0x1); | ||||
|     BitField<0, 24, u32> viewport_size_y; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0xc); | ||||
|     INSERT_PADDING_WORDS(0x9); | ||||
| 
 | ||||
|     BitField<0, 24, u32> viewport_depth_range; // float24
 | ||||
|     BitField<0, 24, u32> viewport_depth_far_plane; // float24
 | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x1); | ||||
| 
 | ||||
|     union { | ||||
|         // Maps components of output vertex attributes to semantics
 | ||||
|  | @ -82,7 +87,14 @@ struct Regs { | |||
|         BitField<24, 5, Semantic> map_w; | ||||
|     } vs_output_attributes[7]; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x1a9); | ||||
|     INSERT_PADDING_WORDS(0x11); | ||||
| 
 | ||||
|     union { | ||||
|         BitField< 0, 16, u32> x; | ||||
|         BitField<16, 16, u32> y; | ||||
|     } viewport_corner; | ||||
| 
 | ||||
|     INSERT_PADDING_WORDS(0x197); | ||||
| 
 | ||||
|     struct { | ||||
|         enum class Format : u64 { | ||||
|  | @ -340,6 +352,9 @@ struct Regs { | |||
| 
 | ||||
|         ADD_FIELD(viewport_size_x); | ||||
|         ADD_FIELD(viewport_size_y); | ||||
|         ADD_FIELD(viewport_depth_range); | ||||
|         ADD_FIELD(viewport_depth_far_plane); | ||||
|         ADD_FIELD(viewport_corner); | ||||
|         ADD_FIELD(vertex_attributes); | ||||
|         ADD_FIELD(index_array); | ||||
|         ADD_FIELD(num_vertices); | ||||
|  | @ -391,8 +406,11 @@ private: | |||
| 
 | ||||
| ASSERT_REG_POSITION(viewport_size_x, 0x41); | ||||
| ASSERT_REG_POSITION(viewport_size_y, 0x43); | ||||
| ASSERT_REG_POSITION(viewport_depth_range, 0x4d); | ||||
| ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); | ||||
| ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); | ||||
| ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | ||||
| ASSERT_REG_POSITION(viewport_corner, 0x68); | ||||
| ASSERT_REG_POSITION(vertex_attributes, 0x200); | ||||
| ASSERT_REG_POSITION(index_array, 0x227); | ||||
| ASSERT_REG_POSITION(num_vertices, 0x228); | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "clipper.h" | ||||
| #include "pica.h" | ||||
| #include "primitive_assembly.h" | ||||
| #include "vertex_shader.h" | ||||
|  | @ -23,8 +24,7 @@ void SubmitVertex(OutputVertex& vtx) | |||
|             } else { | ||||
|                 buffer_index = 0; | ||||
| 
 | ||||
|                 // TODO
 | ||||
|                 // Clipper::ProcessTriangle(buffer[0], buffer[1], vtx);
 | ||||
|                 Clipper::ProcessTriangle(buffer[0], buffer[1], vtx); | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|  | @ -32,8 +32,7 @@ void SubmitVertex(OutputVertex& vtx) | |||
|             if (buffer_index == 2) { | ||||
|                 buffer_index = 0; | ||||
| 
 | ||||
|                 // TODO
 | ||||
|                 // Clipper::ProcessTriangle(buffer[0], buffer[1], vtx);
 | ||||
|                 Clipper::ProcessTriangle(buffer[0], buffer[1], vtx); | ||||
| 
 | ||||
|                 buffer[1] = vtx; | ||||
|             } else { | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="renderer_opengl\renderer_opengl.cpp" /> | ||||
|     <ClCompile Include="clipper.cpp" /> | ||||
|     <ClCompile Include="command_processor.cpp" /> | ||||
|     <ClCompile Include="primitive_assembly.cpp" /> | ||||
|     <ClCompile Include="utils.cpp" /> | ||||
|  | @ -27,6 +28,7 @@ | |||
|     <ClCompile Include="video_core.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="clipper.h" /> | ||||
|     <ClInclude Include="command_processor.h" /> | ||||
|     <ClInclude Include="gpu_debugger.h" /> | ||||
|     <ClInclude Include="math.h" /> | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ | |||
|     <ClCompile Include="renderer_opengl\renderer_opengl.cpp"> | ||||
|       <Filter>renderer_opengl</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="clipper.cpp" /> | ||||
|     <ClCompile Include="command_processor.cpp" /> | ||||
|     <ClCompile Include="primitive_assembly.cpp" /> | ||||
|     <ClCompile Include="utils.cpp" /> | ||||
|  | @ -19,6 +20,7 @@ | |||
|     <ClInclude Include="renderer_opengl\renderer_opengl.h"> | ||||
|       <Filter>renderer_opengl</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="clipper.h" /> | ||||
|     <ClInclude Include="command_processor.h" /> | ||||
|     <ClInclude Include="gpu_debugger.h" /> | ||||
|     <ClInclude Include="math.h" /> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue