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::Option;
52 using de::cmdline::NamedValue;
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 }
64
Log()65 void Log () { std::cout << std::endl; }
66 template <typename ARG>
Log(ARG && arg)67 void Log (ARG&& arg) { std::cout << arg << ' ' << std::endl; }
68 template <typename ARG, typename... ARGs>
Log(ARG && first,ARGs &&...args)69 void Log (ARG&& first, ARGs&&... args) { std::cout << first << ' '; Log(args...); }
70
71 #ifdef _DEBUG
72 template <typename... ARGs>
Debug(ARGs &&...args)73 void Debug (ARGs&&... args)
74 {
75 Log("[DEBUG]", std::forward<ARGs>(args)...);
76 }
77 #else
78 template <typename... ARGs>
Debug(ARGs &&...)79 void Debug (ARGs&&...) { }
80 #endif
81
82 struct Client
83 {
84 int id;
85 std::unique_ptr<de::Socket> socket;
86 std::atomic<bool>& appactive;
87 vector<u8> recvb;
88 CmdLineParams cmdLineParams;
89 std::string logFile;
90 };
91
92 std::future<void> CreateClientThread (Client client);
93
main(int argc,char ** argv)94 int main (int argc, char** argv)
95 {
96 de::cmdline::CommandLine cmdLine;
97
98 // Parse command line.
99 {
100 de::cmdline::Parser parser;
101 opt::registerOptions(parser);
102
103 if (!parser.parse(argc, argv, &cmdLine, std::cerr))
104 {
105 parser.help(std::cout);
106 return EXIT_FAILURE;
107 }
108 }
109
110 std::atomic<bool> appActive{true};
111
112 try
113 {
114 de::SocketAddress addr;
115 addr.setHost("0.0.0.0");
116 addr.setPort(cmdLine.getOption<opt::Port>());
117 de::Socket listener;
118 int id{};
119
120 vector<std::future<void>> clients;
121
122 listener.listen(addr);
123 Log("Listening on port", addr.getPort());
124
125 while (appActive)
126 {
127 remove_erase_if(clients, [](const std::future<void>& c) { return is_ready(c); });
128 Client client{ ++id, std::unique_ptr<de::Socket>(listener.accept()), appActive, vector<u8>{},
129 {
130 cmdLine.getOption<opt::PipelineCompilerPath>(),
131 cmdLine.getOption<opt::PipelineCompilerDataDir>(),
132 cmdLine.getOption<opt::PipelineCompilerOutputFile>(),
133 cmdLine.getOption<opt::PipelineCompilerLogFile>(),
134 cmdLine.getOption<opt::PipelineCompilerArgs>()
135 },
136 cmdLine.getOption<opt::LogFile>() };
137 Debug("New client with id", id - 1, "connected");
138 clients.push_back(CreateClientThread(std::move(client)));
139 }
140 }
141 catch (const std::exception& e) { Log(e.what()); appActive = false; }
142
143 return EXIT_SUCCESS;
144 }
145
146 template <typename T>
SendResponse(Client & c,T & data)147 void SendResponse (Client& c, T& data)
148 {
149 SendPayloadWithHeader(c.socket.get(), T::Type(), Serialize(data));
150 }
151
ProcessPacketsOnServer(Client & client,u32 type,vector<u8> packet)152 void ProcessPacketsOnServer (Client& client, u32 type, vector<u8> packet)
153 {
154 switch (type)
155 {
156 case LogRequest::Type():
157 {
158 auto req = Deserialize<LogRequest>(packet);
159 std::cout << req.message;
160 }
161 break;
162 case CompileShaderRequest::Type():
163 {
164 auto req = Deserialize<CompileShaderRequest>(packet);
165
166 vector<u8> result;
167 bool ok = CompileShader(req.source, req.commandLine, result);
168
169 CompileShaderResponse res;
170 res.status = ok;
171 res.binary = std::move(result);
172 SendResponse(client, res);
173 }
174 break;
175 case StoreContentRequest::Type():
176 {
177 auto req = Deserialize<StoreContentRequest>(packet);
178 bool ok = StoreFile(req.name, req.data);
179
180 StoreContentResponse res;
181 res.status = ok;
182 SendResponse(client, res);
183 }
184 break;
185 case GetContentRequest::Type():
186 {
187 auto req = Deserialize<GetContentRequest>(packet);
188
189 vector<u8> content;
190 bool ok = GetFile(req.path, content, req.removeAfter);
191
192 GetContentResponse res;
193 res.status = ok;
194 res.data = std::move(content);
195 SendResponse(client, res);
196 }
197 break;
198 case AppendRequest::Type():
199 {
200 auto req = Deserialize<AppendRequest>(packet);
201
202 bool result = AppendFile(req.fileName, req.data, req.clear);
203 if (!result) Log("[WARNING] Can't append file", req.fileName);
204 }
205 break;
206 case CreateCacheRequest::Type():
207 {
208 auto req = Deserialize<CreateCacheRequest>(packet);
209
210 vector<u8> binary;
211 bool ok = false;
212 try
213 {
214 CreateVulkanSCCache(req.input, req.caseFraction, binary, client.cmdLineParams, client.logFile);
215 ok = true;
216 }
217 catch (const std::exception& e)
218 {
219 Log("[ERROR] Can't create cache:", e.what());
220 binary = {};
221 }
222
223 CreateCacheResponse res;
224 res.status = ok;
225 res.binary = std::move(binary);
226 SendResponse(client, res);
227 }
228 break;
229
230 default:
231 throw std::runtime_error("communication error");
232 }
233 }
234
235 struct PacketsLoop
236 {
237 Client client;
238
LoopPacketsLoop239 void Loop ()
240 {
241 while (client.socket->isConnected() && client.appactive)
242 {
243 RecvSome(client.socket.get(), client.recvb);
244 auto interpret = [this](u32 type, vector<u8> packet) { ProcessPacketsOnServer(client, type, std::move(packet)); };
245 while (ProccessNetworkData(client.recvb, interpret)) {}
246 }
247 }
248
operator ()PacketsLoop249 void operator() ()
250 {
251 try { Loop(); }
252 catch (const std::exception& e)
253 {
254 client.socket->close();
255 Debug(e.what(), "from client with id", client.id);
256 }
257 Debug("Client with id", client.id, "disconnected.");
258 }
259 };
260
CreateClientThread(Client client)261 std::future<void> CreateClientThread (Client client)
262 {
263 return std::async( PacketsLoop{ std::move(client) } );
264 }
265