| /*------------------------------------------------------------------------- |
| * Vulkan CTS Framework |
| * -------------------- |
| * |
| * Copyright (c) 2021 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *-------------------------------------------------------------------------*/ |
| |
| #include "vksNetwork.hpp" |
| #include "vksProtocol.hpp" |
| #include "vksServices.hpp" |
| |
| #include <iostream> |
| #include <fstream> |
| #include <future> |
| #include <atomic> |
| #include <initializer_list> |
| |
| #include "deSocket.hpp" |
| #include "deCommandLine.hpp" |
| |
| using namespace vksc_server; |
| |
| namespace opt |
| { |
| |
| DE_DECLARE_COMMAND_LINE_OPT(Port, int); |
| DE_DECLARE_COMMAND_LINE_OPT(LogFile, std::string); |
| DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerPath, std::string); |
| DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerDataDir, std::string); |
| DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerOutputFile, std::string); |
| DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerLogFile, std::string); |
| DE_DECLARE_COMMAND_LINE_OPT(PipelineCompilerArgs, std::string); |
| |
| const auto DefaultPortStr = std::to_string(DefaultPort); |
| |
| void registerOptions(de::cmdline::Parser &parser) |
| { |
| using de::cmdline::NamedValue; |
| using de::cmdline::Option; |
| |
| parser << Option<Port>(DE_NULL, "port", "Port", DefaultPortStr.c_str()); |
| parser << Option<LogFile>(DE_NULL, "log", "Log filename", "dummy.log"); |
| parser << Option<PipelineCompilerPath>(DE_NULL, "pipeline-compiler", "Path to offline pipeline compiler", ""); |
| parser << Option<PipelineCompilerDataDir>(DE_NULL, "pipeline-dir", "Offline pipeline data directory", ""); |
| parser << Option<PipelineCompilerOutputFile>(DE_NULL, "pipeline-file", "Output file with pipeline cache", ""); |
| parser << Option<PipelineCompilerLogFile>(DE_NULL, "pipeline-log", "Compiler log file", "compiler.log"); |
| parser << Option<PipelineCompilerArgs>(DE_NULL, "pipeline-args", "Additional compiler parameters", ""); |
| } |
| |
| } // namespace opt |
| |
| void Log() |
| { |
| std::cout << std::endl; |
| } |
| template <typename ARG> |
| void Log(ARG &&arg) |
| { |
| std::cout << arg << ' ' << std::endl; |
| } |
| template <typename ARG, typename... ARGs> |
| void Log(ARG &&first, ARGs &&...args) |
| { |
| std::cout << first << ' '; |
| Log(args...); |
| } |
| |
| #ifdef _DEBUG |
| template <typename... ARGs> |
| void Debug(ARGs &&...args) |
| { |
| Log("[DEBUG]", std::forward<ARGs>(args)...); |
| } |
| #else |
| template <typename... ARGs> |
| void Debug(ARGs &&...) |
| { |
| } |
| #endif |
| |
| struct Client |
| { |
| int id; |
| std::unique_ptr<de::Socket> socket; |
| std::atomic<bool> &appactive; |
| vector<u8> recvb; |
| CmdLineParams cmdLineParams; |
| std::string logFile; |
| }; |
| |
| std::future<void> CreateClientThread(Client client); |
| |
| int main(int argc, char **argv) |
| { |
| de::cmdline::CommandLine cmdLine; |
| |
| // Parse command line. |
| { |
| de::cmdline::Parser parser; |
| opt::registerOptions(parser); |
| |
| if (!parser.parse(argc, argv, &cmdLine, std::cerr)) |
| { |
| parser.help(std::cout); |
| return EXIT_FAILURE; |
| } |
| } |
| |
| std::atomic<bool> appActive{true}; |
| |
| try |
| { |
| de::SocketAddress addr; |
| addr.setHost("0.0.0.0"); |
| addr.setPort(cmdLine.getOption<opt::Port>()); |
| de::Socket listener; |
| int id{}; |
| |
| vector<std::future<void>> clients; |
| |
| listener.listen(addr); |
| Log("Listening on port", addr.getPort()); |
| |
| while (appActive) |
| { |
| remove_erase_if(clients, [](const std::future<void> &c) { return is_ready(c); }); |
| Client client{ |
| ++id, |
| std::unique_ptr<de::Socket>(listener.accept()), |
| appActive, |
| vector<u8>{}, |
| {cmdLine.getOption<opt::PipelineCompilerPath>(), cmdLine.getOption<opt::PipelineCompilerDataDir>(), |
| cmdLine.getOption<opt::PipelineCompilerOutputFile>(), |
| cmdLine.getOption<opt::PipelineCompilerLogFile>(), cmdLine.getOption<opt::PipelineCompilerArgs>()}, |
| cmdLine.getOption<opt::LogFile>()}; |
| Debug("New client with id", id - 1, "connected"); |
| clients.push_back(CreateClientThread(std::move(client))); |
| } |
| } |
| catch (const std::exception &e) |
| { |
| Log(e.what()); |
| appActive = false; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| template <typename T> |
| void SendResponse(Client &c, T &data) |
| { |
| SendPayloadWithHeader(c.socket.get(), T::Type(), Serialize(data)); |
| } |
| |
| void ProcessPacketsOnServer(Client &client, u32 type, vector<u8> packet) |
| { |
| switch (type) |
| { |
| case LogRequest::Type(): |
| { |
| auto req = Deserialize<LogRequest>(packet); |
| std::cout << req.message; |
| } |
| break; |
| case CompileShaderRequest::Type(): |
| { |
| auto req = Deserialize<CompileShaderRequest>(packet); |
| |
| vector<u8> result; |
| bool ok = CompileShader(req.source, req.commandLine, result); |
| |
| CompileShaderResponse res; |
| res.status = ok; |
| res.binary = std::move(result); |
| SendResponse(client, res); |
| } |
| break; |
| case StoreContentRequest::Type(): |
| { |
| auto req = Deserialize<StoreContentRequest>(packet); |
| bool ok = StoreFile(req.name, req.data); |
| |
| StoreContentResponse res; |
| res.status = ok; |
| SendResponse(client, res); |
| } |
| break; |
| case GetContentRequest::Type(): |
| { |
| auto req = Deserialize<GetContentRequest>(packet); |
| |
| vector<u8> content; |
| bool ok = GetFile(req.path, content, req.removeAfter); |
| |
| GetContentResponse res; |
| res.status = ok; |
| res.data = std::move(content); |
| SendResponse(client, res); |
| } |
| break; |
| case AppendRequest::Type(): |
| { |
| auto req = Deserialize<AppendRequest>(packet); |
| |
| bool result = AppendFile(req.fileName, req.data, req.clear); |
| if (!result) |
| Log("[WARNING] Can't append file", req.fileName); |
| } |
| break; |
| case CreateCacheRequest::Type(): |
| { |
| auto req = Deserialize<CreateCacheRequest>(packet); |
| |
| vector<u8> binary; |
| bool ok = false; |
| try |
| { |
| CreateVulkanSCCache(req.input, req.caseFraction, binary, client.cmdLineParams, client.logFile); |
| ok = true; |
| } |
| catch (const std::exception &e) |
| { |
| Log("[ERROR] Can't create cache:", e.what()); |
| binary = {}; |
| } |
| |
| CreateCacheResponse res; |
| res.status = ok; |
| res.binary = std::move(binary); |
| SendResponse(client, res); |
| } |
| break; |
| |
| default: |
| throw std::runtime_error("communication error"); |
| } |
| } |
| |
| struct PacketsLoop |
| { |
| Client client; |
| |
| void Loop() |
| { |
| while (client.socket->isConnected() && client.appactive) |
| { |
| RecvSome(client.socket.get(), client.recvb); |
| auto interpret = [this](u32 type, vector<u8> packet) |
| { ProcessPacketsOnServer(client, type, std::move(packet)); }; |
| while (ProccessNetworkData(client.recvb, interpret)) |
| { |
| } |
| } |
| } |
| |
| void operator()() |
| { |
| try |
| { |
| Loop(); |
| } |
| catch (const std::exception &e) |
| { |
| client.socket->close(); |
| Debug(e.what(), "from client with id", client.id); |
| } |
| Debug("Client with id", client.id, "disconnected."); |
| } |
| }; |
| |
| std::future<void> CreateClientThread(Client client) |
| { |
| return std::async(PacketsLoop{std::move(client)}); |
| } |