blob: c2165eeec8d6d704c19541e40967ac115ee0fc52 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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)});
}