reworking distrib infrastructure; server still terminates execution prematurely
git-svn-id: svn://anubis/fart/trunk@237 7f9b0f55-74a9-4bce-be96-3c2cd072584d
This commit is contained in:
parent
e21d948d52
commit
5264f1de7e
146
main/Scene.cc
146
main/Scene.cc
@ -22,14 +22,10 @@ Scene::Scene(const map<string, const char *> & options,
|
|||||||
m_width = 800;
|
m_width = 800;
|
||||||
m_height = 600;
|
m_height = 600;
|
||||||
m_multisample_level = 1;
|
m_multisample_level = 1;
|
||||||
m_output_file_name = "fart.bmp";
|
|
||||||
m_vfov = 60.0;
|
m_vfov = 60.0;
|
||||||
m_verbose = true;
|
|
||||||
m_data = NULL;
|
|
||||||
m_ambient_light = Color(0.2, 0.2, 0.2);
|
m_ambient_light = Color(0.2, 0.2, 0.2);
|
||||||
m_max_depth = 10;
|
m_max_depth = 10;
|
||||||
m_transforms.push(Transform());
|
m_transforms.push(Transform());
|
||||||
m_server = true;
|
|
||||||
|
|
||||||
load(filename);
|
load(filename);
|
||||||
|
|
||||||
@ -41,79 +37,23 @@ Scene::Scene(const map<string, const char *> & options,
|
|||||||
if (it->first == "width")
|
if (it->first == "width")
|
||||||
{
|
{
|
||||||
m_width = atoi(it->second);
|
m_width = atoi(it->second);
|
||||||
m_client_options.push_back("--width");
|
|
||||||
m_client_options.push_back(it->second);
|
|
||||||
}
|
}
|
||||||
else if (it->first == "height")
|
else if (it->first == "height")
|
||||||
{
|
{
|
||||||
m_height = atoi(it->second);
|
m_height = atoi(it->second);
|
||||||
m_client_options.push_back("--height");
|
|
||||||
m_client_options.push_back(it->second);
|
|
||||||
}
|
}
|
||||||
else if (it->first == "multisample")
|
else if (it->first == "multisample")
|
||||||
{
|
{
|
||||||
m_multisample_level = atoi(it->second);
|
m_multisample_level = atoi(it->second);
|
||||||
m_client_options.push_back("--multisample");
|
|
||||||
m_client_options.push_back(it->second);
|
|
||||||
}
|
}
|
||||||
else if (it->first == "field-of-view")
|
else if (it->first == "field-of-view")
|
||||||
{
|
{
|
||||||
m_vfov = atof(it->second);
|
m_vfov = atof(it->second);
|
||||||
m_client_options.push_back("--field-of-view");
|
|
||||||
m_client_options.push_back(it->second);
|
|
||||||
}
|
|
||||||
else if (it->first == "output-file")
|
|
||||||
{
|
|
||||||
m_output_file_name = it->second;
|
|
||||||
/* no client option necessary */
|
|
||||||
}
|
}
|
||||||
else if (it->first == "max-depth")
|
else if (it->first == "max-depth")
|
||||||
{
|
{
|
||||||
m_max_depth = atoi(it->second);
|
m_max_depth = atoi(it->second);
|
||||||
m_client_options.push_back("--max-depth");
|
|
||||||
m_client_options.push_back(it->second);
|
|
||||||
}
|
}
|
||||||
else if (it->first == "verbose")
|
|
||||||
{
|
|
||||||
m_verbose = true;
|
|
||||||
/* no client option necessary */
|
|
||||||
}
|
|
||||||
else if (it->first == "host")
|
|
||||||
{
|
|
||||||
m_server_name = it->second;
|
|
||||||
m_server = false;
|
|
||||||
/* no client option necessary */
|
|
||||||
}
|
|
||||||
else if (it->first == "port")
|
|
||||||
{
|
|
||||||
m_server_port = atoi(it->second);
|
|
||||||
/* no client option necessary */
|
|
||||||
}
|
|
||||||
else if (it->first == "hosts")
|
|
||||||
{
|
|
||||||
m_hosts_file = it->second;
|
|
||||||
/* no client option necessary */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_client_options.push_back(filename);
|
|
||||||
|
|
||||||
/* start the distribution infrastructure */
|
|
||||||
if (m_server)
|
|
||||||
{
|
|
||||||
if (m_hosts_file != "")
|
|
||||||
m_distrib.readHostFile(m_hosts_file.c_str());
|
|
||||||
m_distrib.startServer();
|
|
||||||
m_distrib.startClients(m_client_options);
|
|
||||||
|
|
||||||
m_data = new unsigned char[m_width * m_height * 3]; /* 24bpp */
|
|
||||||
int num_tasks = (m_width * m_height + (UNIT_TASK_SIZE - 1))
|
|
||||||
/ UNIT_TASK_SIZE;
|
|
||||||
m_distrib.set_num_tasks(num_tasks);
|
|
||||||
m_distrib.set_data(m_data, 3 * m_width * m_height);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_distrib.startClient(m_server_name.c_str(), m_server_port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* view plane distance is calculated based on the field of view */
|
/* view plane distance is calculated based on the field of view */
|
||||||
@ -125,92 +65,6 @@ Scene::Scene(const map<string, const char *> & options,
|
|||||||
|
|
||||||
Scene::~Scene()
|
Scene::~Scene()
|
||||||
{
|
{
|
||||||
if (m_data != NULL)
|
|
||||||
delete m_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scene::render()
|
|
||||||
{
|
|
||||||
if (m_server)
|
|
||||||
{
|
|
||||||
/* server version */
|
|
||||||
if (m_verbose)
|
|
||||||
{
|
|
||||||
cout << " *** Beginning scene render ***" << endl;
|
|
||||||
cout << "Parameters:" << endl;
|
|
||||||
cout << "----------------------------------------" << endl;
|
|
||||||
cout << " Width: " << m_width << endl;
|
|
||||||
cout << " Height: " << m_height << endl;
|
|
||||||
cout << " Multisample Level: " << m_multisample_level << endl;
|
|
||||||
cout << " Vertical Field of View: " << m_vfov << endl;
|
|
||||||
cout << "----------------------------------------" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_distrib.getNumClients() < 1)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_height; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < m_width; j++)
|
|
||||||
{
|
|
||||||
renderPixel(j, i, &m_data[3 * (m_width * i + j)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* work on tasks in this thread until there are no more */
|
|
||||||
taskLoop();
|
|
||||||
/* TODO: change wait condition */
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
if (m_distrib.getNumTasksInProgress() == 0)
|
|
||||||
break;
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_verbose)
|
|
||||||
{
|
|
||||||
cout << " *** Ending scene render ***" << endl;
|
|
||||||
cout << "Writing output file '" << m_output_file_name
|
|
||||||
<< '\'' << endl;
|
|
||||||
}
|
|
||||||
BMP outputImage(m_output_file_name.c_str(), m_width, m_height, m_data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* client version */
|
|
||||||
taskLoop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scene::taskLoop()
|
|
||||||
{
|
|
||||||
unsigned char data[3 * UNIT_TASK_SIZE];
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
int task_id = m_distrib.getTask();
|
|
||||||
if (task_id < 0)
|
|
||||||
break;
|
|
||||||
int pixel = task_id * UNIT_TASK_SIZE;
|
|
||||||
int i = pixel / m_width;
|
|
||||||
int j = pixel % m_width;
|
|
||||||
for (int t = 0; t < UNIT_TASK_SIZE; t++)
|
|
||||||
{
|
|
||||||
renderPixel(j, i, &data[3 * t]);
|
|
||||||
j++;
|
|
||||||
if (j >= m_width)
|
|
||||||
{
|
|
||||||
j = 0;
|
|
||||||
i++;
|
|
||||||
if (i >= m_height)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int ret = m_distrib.send_data(task_id, data, 3 * UNIT_TASK_SIZE);
|
|
||||||
if (ret != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::renderPixel(int x, int y, unsigned char * pixel)
|
void Scene::renderPixel(int x, int y, unsigned char * pixel)
|
||||||
|
23
main/Scene.h
23
main/Scene.h
@ -13,8 +13,6 @@
|
|||||||
#include "util/Color.h"
|
#include "util/Color.h"
|
||||||
#include "util/Material.h"
|
#include "util/Material.h"
|
||||||
|
|
||||||
#include "distrib/distrib.h"
|
|
||||||
|
|
||||||
#include "shapes/shapes.h"
|
#include "shapes/shapes.h"
|
||||||
|
|
||||||
#include "parser/parser.h"
|
#include "parser/parser.h"
|
||||||
@ -30,16 +28,19 @@ class Scene
|
|||||||
Scene(const std::map<std::string, const char *> & options,
|
Scene(const std::map<std::string, const char *> & options,
|
||||||
const char * filename);
|
const char * filename);
|
||||||
~Scene();
|
~Scene();
|
||||||
void render();
|
|
||||||
void setWidth(int width) { m_width = width; }
|
void setWidth(int width) { m_width = width; }
|
||||||
void setHeight(int height) { m_height = height; }
|
void setHeight(int height) { m_height = height; }
|
||||||
void setMultisampleLevel(int level) { m_multisample_level = level; }
|
void setMultisampleLevel(int level) { m_multisample_level = level; }
|
||||||
void setVFOV(double vfov) { m_vfov = vfov; }
|
void setVFOV(double vfov) { m_vfov = vfov; }
|
||||||
void setAmbientLight(const Color & al) { m_ambient_light = al; }
|
void setAmbientLight(const Color & al) { m_ambient_light = al; }
|
||||||
|
void renderPixel(int x, int y, unsigned char * pixel);
|
||||||
|
int getWidth() { return m_width; }
|
||||||
|
int getHeight() { return m_height; }
|
||||||
|
int getMultisampleLevel() { return m_multisample_level; }
|
||||||
|
double getVFOV() { return m_vfov; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/* private methods */
|
/* private methods */
|
||||||
void renderPixel(int x, int y, unsigned char * pixel);
|
|
||||||
Color traceRay(const Ray & ray);
|
Color traceRay(const Ray & ray);
|
||||||
Color traceRayRecurse(const Ray & ray, int depth, double factor);
|
Color traceRayRecurse(const Ray & ray, int depth, double factor);
|
||||||
Shape::Intersection getRayClosestHit(const Ray & ray);
|
Shape::Intersection getRayClosestHit(const Ray & ray);
|
||||||
@ -49,7 +50,6 @@ class Scene
|
|||||||
const Vector & surfaceNormal);
|
const Vector & surfaceNormal);
|
||||||
Color calculateLightContribution(const Ray & toLight,
|
Color calculateLightContribution(const Ray & toLight,
|
||||||
refptr<Light> light);
|
refptr<Light> light);
|
||||||
void taskLoop();
|
|
||||||
|
|
||||||
/* In Scene-load.cc */
|
/* In Scene-load.cc */
|
||||||
void load(const char * filename);
|
void load(const char * filename);
|
||||||
@ -74,8 +74,6 @@ class Scene
|
|||||||
int m_width;
|
int m_width;
|
||||||
int m_height;
|
int m_height;
|
||||||
int m_multisample_level;
|
int m_multisample_level;
|
||||||
std::string m_output_file_name;
|
|
||||||
bool m_verbose;
|
|
||||||
double m_vfov;
|
double m_vfov;
|
||||||
Color m_ambient_light;
|
Color m_ambient_light;
|
||||||
int m_max_depth;
|
int m_max_depth;
|
||||||
@ -89,17 +87,6 @@ class Scene
|
|||||||
double m_sample_span;
|
double m_sample_span;
|
||||||
double m_half_sample_span;
|
double m_half_sample_span;
|
||||||
std::map< std::string, refptr<Material> > m_materials;
|
std::map< std::string, refptr<Material> > m_materials;
|
||||||
|
|
||||||
/* distribution infrastructure */
|
|
||||||
bool m_server;
|
|
||||||
std::string m_server_name;
|
|
||||||
std::string m_hosts_file;
|
|
||||||
int m_server_port;
|
|
||||||
distrib m_distrib;
|
|
||||||
std::vector<std::string> m_client_options;
|
|
||||||
|
|
||||||
/* framebuffer */
|
|
||||||
unsigned char * m_data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
134
main/fart.cc
134
main/fart.cc
@ -8,6 +8,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "Scene.h"
|
#include "Scene.h"
|
||||||
|
#include "distrib/distrib.h"
|
||||||
|
#include "BMP.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void usage(const char * progname)
|
void usage(const char * progname)
|
||||||
@ -21,7 +23,6 @@ void usage(const char * progname)
|
|||||||
cout << " -f|--field-of-view <vertical-fov>" << endl;
|
cout << " -f|--field-of-view <vertical-fov>" << endl;
|
||||||
cout << " -d|--max-depth <max-recursion-depth>" << endl;
|
cout << " -d|--max-depth <max-recursion-depth>" << endl;
|
||||||
cout << " --hosts <hosts-file>" << endl;
|
cout << " --hosts <hosts-file>" << endl;
|
||||||
cout << " -v|--verbose" << endl;
|
|
||||||
exit(42);
|
exit(42);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +31,14 @@ int main(int argc, char * argv[])
|
|||||||
int opt;
|
int opt;
|
||||||
int option_index;
|
int option_index;
|
||||||
map<string, const char *> scene_options;
|
map<string, const char *> scene_options;
|
||||||
|
vector<string> client_options;
|
||||||
bool server = true;
|
bool server = true;
|
||||||
|
bool distributed = false;
|
||||||
|
const char * hosts_file = NULL;
|
||||||
|
const char * server_name = NULL;
|
||||||
|
int server_port = 0;
|
||||||
|
unsigned char * data = NULL;
|
||||||
|
const char * output_file_name = NULL;
|
||||||
|
|
||||||
static const struct option long_options[] = {
|
static const struct option long_options[] = {
|
||||||
{ "output-file", required_argument, NULL, 'o' },
|
{ "output-file", required_argument, NULL, 'o' },
|
||||||
@ -39,7 +47,6 @@ int main(int argc, char * argv[])
|
|||||||
{ "multisample", required_argument, NULL, 'm' },
|
{ "multisample", required_argument, NULL, 'm' },
|
||||||
{ "field-of-view", required_argument, NULL, 'f' },
|
{ "field-of-view", required_argument, NULL, 'f' },
|
||||||
{ "max-depth", required_argument, NULL, 'd' },
|
{ "max-depth", required_argument, NULL, 'd' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
|
||||||
{ "help", no_argument, NULL, 256 },
|
{ "help", no_argument, NULL, 256 },
|
||||||
{ "host", required_argument, NULL, 257 },
|
{ "host", required_argument, NULL, 257 },
|
||||||
{ "port", required_argument, NULL, 258 },
|
{ "port", required_argument, NULL, 258 },
|
||||||
@ -47,44 +54,54 @@ int main(int argc, char * argv[])
|
|||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
while ((opt = getopt_long(argc, argv, "o:w:h:m:f:d:v",
|
while ((opt = getopt_long(argc, argv, "o:w:h:m:f:d:",
|
||||||
long_options, &option_index)) != -1)
|
long_options, &option_index)) != -1)
|
||||||
{
|
{
|
||||||
switch (opt)
|
switch (opt)
|
||||||
{
|
{
|
||||||
case 'o':
|
case 'o':
|
||||||
scene_options["output-file"] = optarg;
|
output_file_name = optarg;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
scene_options["width"] = optarg;
|
scene_options["width"] = optarg;
|
||||||
|
client_options.push_back("--width");
|
||||||
|
client_options.push_back(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
scene_options["height"] = optarg;
|
scene_options["height"] = optarg;
|
||||||
|
client_options.push_back("--height");
|
||||||
|
client_options.push_back(optarg);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
scene_options["multisample"] = optarg;
|
scene_options["multisample"] = optarg;
|
||||||
|
client_options.push_back("--multisample");
|
||||||
|
client_options.push_back(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
scene_options["field-of-view"] = optarg;
|
scene_options["field-of-view"] = optarg;
|
||||||
|
client_options.push_back("--field-of-view");
|
||||||
|
client_options.push_back(optarg);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
scene_options["max-depth"] = optarg;
|
scene_options["max-depth"] = optarg;
|
||||||
break;
|
client_options.push_back("--max-depth");
|
||||||
case 'v':
|
client_options.push_back(optarg);
|
||||||
scene_options["verbose"] = optarg;
|
|
||||||
break;
|
break;
|
||||||
case 256:
|
case 256:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
break;
|
break;
|
||||||
case 257:
|
case 257:
|
||||||
scene_options["host"] = optarg;
|
server_name = optarg;
|
||||||
server = false;
|
server = false;
|
||||||
|
distributed = true;
|
||||||
break;
|
break;
|
||||||
case 258:
|
case 258:
|
||||||
scene_options["port"] = optarg;
|
server_port = atoi(optarg);
|
||||||
|
distributed = true;
|
||||||
break;
|
break;
|
||||||
case 259:
|
case 259:
|
||||||
scene_options["hosts"] = optarg;
|
hosts_file = optarg;
|
||||||
|
distributed = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
@ -97,17 +114,110 @@ int main(int argc, char * argv[])
|
|||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene scene(scene_options, argv[optind]);
|
const char * filename = argv[optind];
|
||||||
|
client_options.push_back(filename);
|
||||||
|
Scene scene(scene_options, filename);
|
||||||
|
|
||||||
|
const int width = scene.getWidth();
|
||||||
|
const int height = scene.getHeight();
|
||||||
|
|
||||||
|
if (server)
|
||||||
|
{
|
||||||
|
/* allocate data for the image */
|
||||||
|
data = new unsigned char[3 * width * height];
|
||||||
|
|
||||||
|
cout << " *** Beginning scene render ***" << endl;
|
||||||
|
cout << "Parameters:" << endl;
|
||||||
|
cout << "----------------------------------------" << endl;
|
||||||
|
cout << " Width: " << width << endl;
|
||||||
|
cout << " Height: " << height << endl;
|
||||||
|
cout << " Multisample Level: " << scene.getMultisampleLevel() << endl;
|
||||||
|
cout << " Vertical Field of View: " << scene.getVFOV() << endl;
|
||||||
|
cout << "----------------------------------------" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
struct timeval before, after;
|
struct timeval before, after;
|
||||||
gettimeofday(&before, NULL); /* start timing */
|
gettimeofday(&before, NULL); /* start timing */
|
||||||
|
|
||||||
scene.render();
|
#if 0
|
||||||
|
void Scene::taskLoop()
|
||||||
|
{
|
||||||
|
unsigned char data[3 * UNIT_TASK_SIZE];
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int task_id = m_distrib.getTask();
|
||||||
|
if (task_id < 0)
|
||||||
|
break;
|
||||||
|
int pixel = task_id * UNIT_TASK_SIZE;
|
||||||
|
int i = pixel / m_width;
|
||||||
|
int j = pixel % m_width;
|
||||||
|
for (int t = 0; t < UNIT_TASK_SIZE; t++)
|
||||||
|
{
|
||||||
|
renderPixel(j, i, &data[3 * t]);
|
||||||
|
j++;
|
||||||
|
if (j >= m_width)
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
i++;
|
||||||
|
if (i >= m_height)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int ret = m_distrib.send_data(task_id, data, 3 * UNIT_TASK_SIZE);
|
||||||
|
if (ret != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (distributed)
|
||||||
|
{
|
||||||
|
/* start the distribution infrastructure */
|
||||||
|
distrib the_distrib;
|
||||||
|
if (server)
|
||||||
|
{
|
||||||
|
if (strcmp(hosts_file, ""))
|
||||||
|
the_distrib.readHostFile(hosts_file);
|
||||||
|
|
||||||
|
int num_tasks = (width * height + (UNIT_TASK_SIZE - 1))
|
||||||
|
/ UNIT_TASK_SIZE;
|
||||||
|
the_distrib.set_num_tasks(num_tasks);
|
||||||
|
the_distrib.set_data(data, 3 * width * height);
|
||||||
|
|
||||||
|
the_distrib.startServer();
|
||||||
|
the_distrib.startClients(client_options);
|
||||||
|
/* TODO: wait until all tasks are complete */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
the_distrib.startClient(server_name, server_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* "sequential" version */
|
||||||
|
unsigned char * pixel = &data[0];
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < width; j++)
|
||||||
|
{
|
||||||
|
scene.renderPixel(j, i, pixel);
|
||||||
|
pixel += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gettimeofday(&after, NULL); /* stop timing */
|
gettimeofday(&after, NULL); /* stop timing */
|
||||||
|
|
||||||
if (server)
|
if (server)
|
||||||
{
|
{
|
||||||
|
cout << " *** Ending scene render ***" << endl;
|
||||||
|
cout << "Writing output file '" << output_file_name << '\'' << endl;
|
||||||
|
|
||||||
|
/* write the image */
|
||||||
|
BMP outputImage(output_file_name, width, height, data);
|
||||||
|
|
||||||
|
/* print how much time has elapsed */
|
||||||
double time_before = before.tv_sec + before.tv_usec / 1000000.0;
|
double time_before = before.tv_sec + before.tv_usec / 1000000.0;
|
||||||
double time_after = after.tv_sec + after.tv_usec / 1000000.0;
|
double time_after = after.tv_sec + after.tv_usec / 1000000.0;
|
||||||
double total_seconds = time_after - time_before;
|
double total_seconds = time_after - time_before;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user