1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *-------------------------------------------------------------------------*/
20
21 #include "vksNetwork.hpp"
22 #include "vksProtocol.hpp"
23 #include "vksServices.hpp"
24
25 #include <iostream>
26 #include <fstream>
27 #include <future>
28 #include <atomic>
29 #include <initializer_list>
30
31 #include "deSocket.hpp"
32 #include "deCommandLine.hpp"
33
34 using namespace vksc_server;
35
36 namespace opt
37 {
38
39 DE_DECLARE_COMMAND_LINE_OPT(Port, int);
40 DE_DECLARE_COMMAND_LINE_OPT(LogFile, std::string);
41 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath, std::string);
42 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir, std::string);
43 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile, std::string);
44 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile, std::string);
45 DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs, std::string);
46
47 const auto DefaultPortStr = std::to_string(DefaultPort);
48
registerOptions(de::cmdline::Parser & parser)49 void registerOptions(de::cmdline::Parser &parser)
50 {
51 using de::cmdline::NamedValue;
52 using de::cmdline::Option;
53
54 parser << Option<Port>(DE_NULL, "port", "Port", DefaultPortStr.c_str());
55 parser << Option<LogFile>(DE_NULL, "log", "Log filename", "dummy.log");
56 parser << Option<PipelineCompilerPath>(DE_NULL, "pipeline-compiler", "Path to offline pipeline compiler", "");
57 parser << Option<PipelineCompilerDataDir>(DE_NULL, "pipeline-dir", "Offline pipeline data directory", "");
58 parser << Option<PipelineCompilerOutputFile>(DE_NULL, "pipeline-file", "Output file with pipeline cache", "");
59 parser << Option<PipelineCompilerLogFile>(DE_NULL, "pipeline-log", "Compiler log file", "compiler.log");
60 parser << Option<PipelineCompilerArgs>(DE_NULL, "pipeline-args", "Additional compiler parameters", "");
61 }
62
63 } // namespace opt
64
Log()65 void Log()
66 {
67 std::cout << std::endl;
68 }
69 template <typename ARG>
Log(ARG && arg)70 void Log(ARG &&arg)
71 {
72 std::cout << arg << ' ' << std::endl;
73 }
74 template <typename ARG, typename... ARGs>
Log(ARG && first,ARGs &&...args)75 void Log(ARG &&first, ARGs &&...args)
76 {
77 std::cout << first << ' ';
78 Log(args...);
79 }
80
81 #ifdef _DEBUG
82 template <typename... ARGs>
Debug(ARGs &&...args)83 void Debug(ARGs &&...args)
84 {
85 Log("[DEBUG]", std::forward<ARGs>(args)...);
86 }
87 #else
88 template <typename... ARGs>
Debug(ARGs &&...)89 void Debug(ARGs &&...)
90 {
91 }
92 #endif
93
94 struct Client
95 {
96 int id;
97 std::unique_ptr<de::Socket> socket;
98 std::atomic<bool> &appactive;
99 vector<u8> recvb;
100 CmdLineParams cmdLineParams;
101 std::string logFile;
102 };
103
104 std::future<void> CreateClientThread(Client client);
105
main(int argc,char ** argv)106 int main(int argc, char **argv)
107 {
108 de::cmdline::CommandLine cmdLine;
109
110 // Parse command line.
111 {
112 de::cmdline::Parser parser;
113 opt::registerOptions(parser);
114
115 if (!parser.parse(argc, argv, &cmdLine, std::cerr))
116 {
117 parser.help(std::cout);
118 return EXIT_FAILURE;
119 }
120 }
121
122 std::atomic<bool> appActive{true};
123
124 try
125 {
126 de::SocketAddress addr;
127 addr.setHost("0.0.0.0");
128 addr.setPort(cmdLine.getOption<opt::Port>());
129 de::Socket listener;
130 int id{};
131
132 vector<std::future<void>> clients;
133
134 listener.listen(addr);
135 Log("Listening on port", addr.getPort());
136
137 while (appActive)
138 {
139 remove_erase_if(clients, [](const std::future<void> &c) { return is_ready(c); });
140 Client client{
141 ++id,
142 std::unique_ptr<de::Socket>(listener.accept()),
143 appActive,
144 vector<u8>{},
145 {cmdLine.getOption<opt::PipelineCompilerPath>(), cmdLine.getOption<opt::PipelineCompilerDataDir>(),
146 cmdLine.getOption<opt::PipelineCompilerOutputFile>(),
147 cmdLine.getOption<opt::PipelineCompilerLogFile>(), cmdLine.getOption<opt::PipelineCompilerArgs>()},
148 cmdLine.getOption<opt::LogFile>()};
149 Debug("New client with id", id - 1, "connected");
150 clients.push_back(CreateClientThread(std::move(client)));
151 }
152 }
153 catch (const std::exception &e)
154 {
155 Log(e.what());
156 appActive = false;
157 }
158
159 return EXIT_SUCCESS;
160 }
161
162 template <typename T>
SendResponse(Client & c,T & data)163 void SendResponse(Client &c, T &data)
164 {
165 SendPayloadWithHeader(c.socket.get(), T::Type(), Serialize(data));
166 }
167
ProcessPacketsOnServer(Client & client,u32 type,vector<u8> packet)168 void ProcessPacketsOnServer(Client &client, u32 type, vector<u8> packet)
169 {
170 switch (type)
171 {
172 case LogRequest::Type():
173 {
174 auto req = Deserialize<LogRequest>(packet);
175 std::cout << req.message;
176 }
177 break;
178 case CompileShaderRequest::Type():
179 {
180 auto req = Deserialize<CompileShaderRequest>(packet);
181
182 vector<u8> result;
183 bool ok = CompileShader(req.source, req.commandLine, result);
184
185 CompileShaderResponse res;
186 res.status = ok;
187 res.binary = std::move(result);
188 SendResponse(client, res);
189 }
190 break;
191 case StoreContentRequest::Type():
192 {
193 auto req = Deserialize<StoreContentRequest>(packet);
194 bool ok = StoreFile(req.name, req.data);
195
196 StoreContentResponse res;
197 res.status = ok;
198 SendResponse(client, res);
199 }
200 break;
201 case GetContentRequest::Type():
202 {
203 auto req = Deserialize<GetContentRequest>(packet);
204
205 vector<u8> content;
206 bool ok = GetFile(req.path, content, req.removeAfter);
207
208 GetContentResponse res;
209 res.status = ok;
210 res.data = std::move(content);
211 SendResponse(client, res);
212 }
213 break;
214 case AppendRequest::Type():
215 {
216 auto req = Deserialize<AppendRequest>(packet);
217
218 bool result = AppendFile(req.fileName, req.data, req.clear);
219 if (!result)
220 Log("[WARNING] Can't append file", req.fileName);
221 }
222 break;
223 case CreateCacheRequest::Type():
224 {
225 auto req = Deserialize<CreateCacheRequest>(packet);
226
227 vector<u8> binary;
228 bool ok = false;
229 try
230 {
231 CreateVulkanSCCache(req.input, req.caseFraction, binary, client.cmdLineParams, client.logFile);
232 ok = true;
233 }
234 catch (const std::exception &e)
235 {
236 Log("[ERROR] Can't create cache:", e.what());
237 binary = {};
238 }
239
240 CreateCacheResponse res;
241 res.status = ok;
242 res.binary = std::move(binary);
243 SendResponse(client, res);
244 }
245 break;
246
247 default:
248 throw std::runtime_error("communication error");
249 }
250 }
251
252 struct PacketsLoop
253 {
254 Client client;
255
LoopPacketsLoop256 void Loop()
257 {
258 while (client.socket->isConnected() && client.appactive)
259 {
260 RecvSome(client.socket.get(), client.recvb);
261 auto interpret = [this](u32 type, vector<u8> packet)
262 { ProcessPacketsOnServer(client, type, std::move(packet)); };
263 while (ProccessNetworkData(client.recvb, interpret))
264 {
265 }
266 }
267 }
268
operator ()PacketsLoop269 void operator()()
270 {
271 try
272 {
273 Loop();
274 }
275 catch (const std::exception &e)
276 {
277 client.socket->close();
278 Debug(e.what(), "from client with id", client.id);
279 }
280 Debug("Client with id", client.id, "disconnected.");
281 }
282 };
283
CreateClientThread(Client client)284 std::future<void> CreateClientThread(Client client)
285 {
286 return std::async(PacketsLoop{std::move(client)});
287 }
288