Aeon Engine c550894
AeonGames Open Source Game Engine
Loading...
Searching...
No Matches
PipelineTool.cpp
1/*
2Copyright (C) 2018,2019,2024,2025,2026 Rodrigo Jose Hernandez Cordoba
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#ifdef _MSC_VER
18#pragma warning( push )
19#pragma warning( disable : PROTOBUF_WARNINGS )
20#endif
22#include <google/protobuf/text_format.h>
23#include "pipeline.pb.h"
24#ifdef _MSC_VER
25#pragma warning( pop )
26#endif
27
28#include <fstream>
29#include <sstream>
30#include <ostream>
31#include <iostream>
32#include <cstring>
33#include <filesystem>
34#include "PipelineTool.h"
35#include "CodeFieldValuePrinter.hpp"
36#include "aeongames/Pipeline.hpp"
37
38namespace AeonGames
39{
42 void PipelineTool::ProcessArgs ( int argc, char** argv )
43 {
44 if ( argc < 2 || ( strcmp ( argv[1], "pipeline" ) != 0 ) )
45 {
46 std::ostringstream stream;
47 stream << "Invalid tool name, expected pipeline, got " << ( ( argc < 2 ) ? "nothing" : argv[1] ) << std::endl;
48 throw std::runtime_error ( stream.str().c_str() );
49 }
50 for ( int i = 2; i < argc; ++i )
51 {
52 if ( argv[i][0] == '-' )
53 {
54 if ( argv[i][1] == '-' )
55 {
56 if ( strncmp ( &argv[i][2], "in", sizeof ( "in" ) ) == 0 )
57 {
58 i++;
59 mInputFile = argv[i];
60 }
61 else if ( strncmp ( &argv[i][2], "out", sizeof ( "out" ) ) == 0 )
62 {
63 i++;
64 mOutputFile = argv[i];
65 }
66 }
67 else
68 {
69 switch ( argv[i][1] )
70 {
71 case 'i':
72 i++;
73 mInputFile = argv[i];
74 break;
75 case 'o':
76 i++;
77 mOutputFile = argv[i];
78 break;
79 }
80 }
81 }
82 else
83 {
84 mInputFile = argv[i];
85 }
86 }
87 if ( mInputFile.empty() )
88 {
89 throw std::runtime_error ( "No Input file provided." );
90 }
91 else if ( mOutputFile.empty() )
92 {
93 mOutputFile = mInputFile;
94 }
95 }
96
98 const std::unordered_map<const char*, std::function<std::string * ( PipelineMsg* ) >> ShaderTypeToExtension
99 {
100 { ".vert", &PipelineMsg::mutable_vert },
101 { ".frag", &PipelineMsg::mutable_frag },
102 { ".comp", &PipelineMsg::mutable_comp },
103 { ".tesc", &PipelineMsg::mutable_tesc },
104 { ".tese", &PipelineMsg::mutable_tese },
105 { ".geom", &PipelineMsg::mutable_geom }
106 };
107
109 const std::unordered_map<const char*, std::function<const std::string& ( const PipelineMsg* ) >> ShaderTypeToGetter
110 {
111 { ".vert", &PipelineMsg::vert },
112 { ".frag", &PipelineMsg::frag },
113 { ".comp", &PipelineMsg::comp },
114 { ".tesc", &PipelineMsg::tesc },
115 { ".tese", &PipelineMsg::tese },
116 { ".geom", &PipelineMsg::geom }
117 };
118
119 int PipelineTool::operator() ( int argc, char** argv )
120 {
121 ProcessArgs ( argc, argv );
122 std::filesystem::path input_path{mInputFile};
123 std::filesystem::path output_path{mOutputFile};
124 char magick_number[8] = "AEONPLN";
125 if ( !input_path.has_extension() )
126 {
127 if ( output_path.extension() != ".pln" && output_path.extension() != ".txt" )
128 {
129 throw std::runtime_error ( "Unsupported output file extension, must be .pln or .txt" );
130 }
131 PipelineMsg pipeline_msg;
132 bool found_shader{false};
133 for ( const auto& [extension, setter] : ShaderTypeToExtension )
134 {
135 std::filesystem::path shader_path{input_path.replace_extension ( extension ) };
136 if ( std::filesystem::exists ( shader_path ) )
137 {
138 std::ifstream shader_file ( shader_path );
139 if ( !shader_file )
140 {
141 throw std::runtime_error ( "Failed to open shader file: " + shader_path.string() );
142 }
143 std::string shader_code ( ( std::istreambuf_iterator<char> ( shader_file ) ), std::istreambuf_iterator<char>() );
144 shader_file.close();
145 setter ( &pipeline_msg )->assign ( shader_code );
146 found_shader = true;
147 }
148 }
149 if ( found_shader )
150 {
151 if ( output_path.extension() == ".pln" )
152 {
153 std::ofstream binary_file ( mOutputFile, std::ios::out | std::ios::binary );
154 binary_file << magick_number << '\0';
155 if ( !pipeline_msg.SerializeToOstream ( &binary_file ) )
156 {
157 std::cerr << "Failed to serialize pipeline message to binary format.";
158 throw std::runtime_error ( "Failed to serialize pipeline message to binary format." );
159 }
160 binary_file.close();
161 }
162 else
163 {
164 google::protobuf::TextFormat::Printer printer;
165 for ( int i = 1; i <= pipeline_msg.GetDescriptor()->field_count() ; ++i )
166 {
167 if ( !printer.RegisterFieldValuePrinter (
168 pipeline_msg.GetDescriptor()->FindFieldByNumber ( i ),
170 {
171 std::cout << "Failed to register field value printer." << std::endl;
172 }
173 }
174 std::string text_string;
175 std::ofstream text_file ( mOutputFile, std::ios::out );
176 if ( !printer.PrintToString ( pipeline_msg, &text_string ) )
177 {
178 std::cerr << "Failed to serialize pipeline message to text format.";
179 throw std::runtime_error ( "Failed to serialize pipeline message to text format." );
180 }
181 text_file << magick_number << std::endl;
182 text_file.write ( text_string.c_str(), text_string.length() );
183 text_file.close();
184 }
185 }
186 else
187 {
188 throw std::runtime_error ( "No suitable shader file found." );
189 }
190 }
191 else if ( input_path.extension() == ".pln" || input_path.extension() == ".txt" )
192 {
193 // Extract shaders from pipeline file
194 PipelineMsg pipeline_msg;
195 std::ifstream input_file ( mInputFile, input_path.extension() == ".pln" ? ( std::ios::in | std::ios::binary ) : std::ios::in );
196 if ( !input_file )
197 {
198 throw std::runtime_error ( "Failed to open pipeline file: " + mInputFile );
199 }
200
201 // Read and verify magic number
202 char file_magick[8];
203 if ( input_path.extension() == ".pln" )
204 {
205 input_file.read ( file_magick, 8 );
206 if ( strncmp ( file_magick, magick_number, 7 ) != 0 )
207 {
208 throw std::runtime_error ( "Invalid pipeline file format (magic number mismatch)" );
209 }
210 if ( !pipeline_msg.ParseFromIstream ( &input_file ) )
211 {
212 throw std::runtime_error ( "Failed to parse binary pipeline file" );
213 }
214 }
215 else // .txt
216 {
217 input_file.getline ( file_magick, 8 );
218 if ( strncmp ( file_magick, magick_number, 7 ) != 0 )
219 {
220 throw std::runtime_error ( "Invalid pipeline file format (magic number mismatch)" );
221 }
222 std::string text_content ( ( std::istreambuf_iterator<char> ( input_file ) ), std::istreambuf_iterator<char>() );
223 if ( !google::protobuf::TextFormat::ParseFromString ( text_content, &pipeline_msg ) )
224 {
225 throw std::runtime_error ( "Failed to parse text pipeline file" );
226 }
227 }
228 input_file.close();
229
230 // Extract each shader to its own file
231 std::filesystem::path base_path = output_path.has_extension() ? output_path.replace_extension() : output_path;
232 bool extracted_any = false;
233
234 for ( const auto& [extension, getter] : ShaderTypeToGetter )
235 {
236 const std::string& shader_code = getter ( &pipeline_msg );
237 if ( !shader_code.empty() )
238 {
239 std::filesystem::path shader_path = base_path;
240 shader_path.replace_extension ( extension );
241 std::ofstream shader_file ( shader_path, std::ios::out );
242 if ( !shader_file )
243 {
244 throw std::runtime_error ( "Failed to create shader file: " + shader_path.string() );
245 }
246 shader_file << shader_code;
247 shader_file.close();
248 std::cout << "Extracted: " << shader_path.filename().string() << std::endl;
249 extracted_any = true;
250 }
251 }
252
253 if ( !extracted_any )
254 {
255 std::cout << "Warning: No shader code found in pipeline file" << std::endl;
256 }
257 }
258 else
259 {
260 throw std::runtime_error ( "Unsupported file extension" );
261 }
262
263 return 0;
264 }
265}
Provides the DLL_PROTOBUF export/import macro for protobuf wrapper classes.
Custom protobuf field value printer for code-formatted text output.
int operator()(int argc, char **argv) override
Execute the pipeline tool.
~PipelineTool()
Destructor.
PipelineTool()
Default constructor.
<- This is here just for the literals
Definition AABB.hpp:31
const std::unordered_map< const char *, std::function< std::string *(PipelineMsg *) > > ShaderTypeToExtension
Map from shader file extension to its mutable PipelineMsg accessor.
const std::unordered_map< const char *, std::function< const std::string &(const PipelineMsg *) > > ShaderTypeToGetter
Map from shader file extension to its const PipelineMsg getter.