blob: e9cf03e725bbfab69e04f35aa28dc6b0021d2714 [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::Option;
using de::cmdline::NamedValue;
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", "");
}
}
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) } );
}