From a6f0422bd105fd90171a97d74a9247e1f0460eb5 Mon Sep 17 00:00:00 2001 From: Godzil Date: Thu, 27 Feb 2020 17:20:55 +0000 Subject: [PATCH] Add renderstat to get some info about rendering. --- CMakeLists.txt | 5 ++ source/CMakeLists.txt | 1 + source/camera.cpp | 8 ++- source/include/boundingbox.h | 7 +- source/include/cone.h | 3 +- source/include/cube.h | 3 +- source/include/cylinder.h | 3 +- source/include/intersection.h | 3 +- source/include/light.h | 4 +- source/include/plane.h | 4 +- source/include/ray.h | 3 +- source/include/renderstat.h | 125 ++++++++++++++++++++++++++++++++++ source/include/sphere.h | 3 +- source/intersect.cpp | 11 +++ source/matrix.cpp | 1 + source/renderstat.cpp | 11 +++ source/shapes/group.cpp | 3 +- source/shapes/triangle.cpp | 3 + source/world.cpp | 5 ++ 19 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 source/include/renderstat.h create mode 100644 source/renderstat.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c34c618..65fbd30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/external/covera option(PACKAGE_TESTS "Build the tests" ON) option(ENABLE_COVERAGE "Build for code coverage" OFF) +option(SHOW_STATS "Show rendering stat" ON) +if (SHOW_STATS) + add_compile_options(-DRENDER_STATS) +endif() + if (ENABLE_COVERAGE AND COVERALLS) message(FATAL_ERROR "You can't enable both ENABLE_COVERAGE and COVERALLS at the same time") endif() diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4211899..e25cfbb 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -16,6 +16,7 @@ file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_D target_include_directories(rayonnement PUBLIC include pattern) target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES}) target_link_libraries(rayonnement LodePNG) + if (USE_OPENMP) target_link_libraries(rayonnement OpenMP::OpenMP_CXX) endif() diff --git a/source/camera.cpp b/source/camera.cpp index 4bfa1b2..90bef2c 100644 --- a/source/camera.cpp +++ b/source/camera.cpp @@ -13,6 +13,7 @@ #include #include +#include Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize), horizontalSize(hsize), fieldOfView(fov) { @@ -33,7 +34,6 @@ Camera::Camera(uint32_t hsize, uint32_t vsize, double fov) : verticalSize(vsize) this->pixelSize = (this->halfWidth * 2) / this->horizontalSize; this->setTransform(Matrix4().identity()); - } void Camera::setTransform(Matrix transform) @@ -62,7 +62,7 @@ Canvas Camera::render(World world, uint32_t depth) uint32_t x, y; Canvas image = Canvas(this->horizontalSize, this->verticalSize); -#pragma omp parallel private(x, y) shared(image) +#pragma omp parallel private(x, y) shared(image, stats) { #pragma omp for for (y = 0 ; y < this->verticalSize ; y++) @@ -71,10 +71,14 @@ Canvas Camera::render(World world, uint32_t depth) { Ray r = this->rayForPixel(x, y); Tuple colour = world.colourAt(r, depth); + + stats.addPixel(); image.putPixel(x, y, colour); } } } + stats.printStats(); + return image; } \ No newline at end of file diff --git a/source/include/boundingbox.h b/source/include/boundingbox.h index d3fc9ab..f78df56 100644 --- a/source/include/boundingbox.h +++ b/source/include/boundingbox.h @@ -9,6 +9,8 @@ #ifndef DORAYME_BOUNDINGBOX_H #define DORAYME_BOUNDINGBOX_H +#include + struct BoundingBox { private: @@ -110,9 +112,12 @@ public: { return true; } + + stats.addDiscardedIntersect(); + return false; } }; -#endif //DORAYME_BOUNDINGBOX_H +#endif /* DORAYME_BOUNDINGBOX_H */ diff --git a/source/include/cone.h b/source/include/cone.h index 9772ba2..38f7aa3 100644 --- a/source/include/cone.h +++ b/source/include/cone.h @@ -12,6 +12,7 @@ #include #include #include +#include class Cone : public Shape { protected: @@ -27,7 +28,7 @@ public: double minCap; double maxCap; - Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) {}; + Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) { stats.addCone(); }; BoundingBox getBounds(); bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); }; }; diff --git a/source/include/cube.h b/source/include/cube.h index 22aa96c..be8c360 100644 --- a/source/include/cube.h +++ b/source/include/cube.h @@ -12,6 +12,7 @@ #include #include #include +#include class Cube : public Shape { private: @@ -22,7 +23,7 @@ private: Tuple localNormalAt(Tuple point); public: - Cube() : Shape(SHAPE_CUBE) {}; + Cube() : Shape(SHAPE_CUBE) { stats.addCube(); }; }; #endif /* DORAYME_CUBE_H */ diff --git a/source/include/cylinder.h b/source/include/cylinder.h index e911be9..ea8ca27 100644 --- a/source/include/cylinder.h +++ b/source/include/cylinder.h @@ -12,6 +12,7 @@ #include #include #include +#include class Cylinder : public Shape { private: @@ -27,7 +28,7 @@ public: double minCap; double maxCap; - Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) {}; + Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) { stats.addCylinder(); }; BoundingBox getBounds(); bool haveFiniteBounds() { return !(isinf(this->minCap) || isinf(this->maxCap)); }; diff --git a/source/include/intersection.h b/source/include/intersection.h index 4d4105e..3786e71 100644 --- a/source/include/intersection.h +++ b/source/include/intersection.h @@ -12,6 +12,7 @@ #include #include #include +#include class Shape; class Intersect; @@ -74,7 +75,7 @@ public: Shape *object; public: - Intersection(double t, Shape *object) : t(t), object(object) { }; + Intersection(double t, Shape *object) : t(t), object(object) { stats.addIntersection(); }; bool nothing() { return (this->object == nullptr); }; Computation prepareComputation(Ray r, Intersect *xs = nullptr); diff --git a/source/include/light.h b/source/include/light.h index b83fc29..1e32a17 100644 --- a/source/include/light.h +++ b/source/include/light.h @@ -11,6 +11,7 @@ #include #include +#include enum LightType { @@ -26,7 +27,8 @@ public: public: Light(LightType type = POINT_LIGHT, Tuple position=Point(0, 0, 0), - Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) { }; + Colour intensity=Colour(1, 1, 1)) : type(type), position(position), intensity(intensity) + { stats.addLight(); }; bool operator==(const Light &b) const { return this->intensity == b.intensity && this->position == b.position && diff --git a/source/include/plane.h b/source/include/plane.h index c2debbb..ee52008 100644 --- a/source/include/plane.h +++ b/source/include/plane.h @@ -9,6 +9,8 @@ #ifndef DORAYME_PLANE_H #define DORAYME_PLANE_H +#include + class Plane : public Shape { private: @@ -16,7 +18,7 @@ private: Tuple localNormalAt(Tuple point); public: - Plane() : Shape(SHAPE_PLANE) { }; + Plane() : Shape(SHAPE_PLANE) { stats.addPlane(); }; BoundingBox getBounds(); bool haveFiniteBounds() { return false; }; }; diff --git a/source/include/ray.h b/source/include/ray.h index 59f52e3..14e8db4 100644 --- a/source/include/ray.h +++ b/source/include/ray.h @@ -10,6 +10,7 @@ #define DORAYME_RAY_H #include +#include class Ray { @@ -17,7 +18,7 @@ public: Tuple direction; Tuple origin; - Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { }; + Ray(Tuple origin, Tuple direction) : origin(origin), direction(direction) { stats.addRay(); }; Tuple position(double t) { return this->origin + this->direction * t; }; }; diff --git a/source/include/renderstat.h b/source/include/renderstat.h new file mode 100644 index 0000000..3792e53 --- /dev/null +++ b/source/include/renderstat.h @@ -0,0 +1,125 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Render statistics header + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#ifndef DORAYME_RENDERSTAT_H +#define DORAYME_RENDERSTAT_H + +#include + +#ifdef RENDER_STATS +#include + +class RenderStats +{ +private: + uint64_t coneCount; /* Total number of cones */ + uint64_t cylinderCount; /* Total number of cylinder */ + uint64_t cubeCount; /* Total number of cubes */ + uint64_t groupCount; /* Total number of groups */ + uint64_t lightCount; /* Total number of light */ + uint64_t planeCount; /* Total number of plane */ + uint64_t sphereCount; /* Total number of sphere */ + uint64_t triangleCount; /* Total number of triangle */ + + uint64_t pixelCount; /* Total number of rendered pixels */ + uint64_t rayCount; /* Total number of rays */ + uint64_t lightRayEmitedCount; /* Total number of ray launched for light tests */ + uint64_t reflectionRayCount; /* Total number of reflection ray launched */ + uint64_t refractedRayCount; /* Total number of refracted ray launched */ + uint64_t intersectCount; /* Total number of intersect object created */ + uint64_t intersectionCount; /* Total number of intersection for all casted rays, including light and reflections */ + uint64_t reallocCallCount; /* Total number of time realloc being called */ + uint64_t mallocCallCount; /* Total number of time malloc/calloc being called */ + uint64_t discardedIntersectCount; /* Number of time a bounding box check said "no need to test me" */ + uint64_t maxDepthAttained; /* Report the lowest depth attained during ray recursion */ + uint64_t maxIntersectOnARay; /* Biggest intersect done */ + +public: + RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0), + pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0), + intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0), + discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0) {}; + + void addCone() { this->coneCount++; }; + void addCylinder() { this->cylinderCount++; }; + void addCube() { this->cubeCount++; }; + void addGroup() { this->groupCount++; }; + void addLight() { this->lightCount++; }; + void addPlane() { this->planeCount++; }; + void addSphere() { this->sphereCount++; }; + void addTriangle() { this->triangleCount++; }; + + void addPixel() { this->pixelCount++; }; + void addRay() { this->rayCount++; }; + void addLightRay() { this->lightRayEmitedCount++; }; + void addReflectRay() { this->reflectionRayCount++; }; + void addRefractRay() { this->refractedRayCount++; }; + void addIntersect() { this->intersectCount++; }; + void addIntersection() { this->intersectionCount++; }; + void addMalloc() { this->mallocCallCount++; }; + void addRealloc() { this->reallocCallCount++; }; + void addDiscardedIntersect() { this->discardedIntersectCount++; }; + void setMaxDepth(uint32_t depth) { if (this->maxDepthAttained>depth) { this->maxDepthAttained = depth; } }; + void setMaxIntersect(uint32_t count) { if (this->maxIntersectOnARaymaxIntersectOnARay = count; } }; + void printStats() { + printf("Rendering statistics:\n"); + printf("Cones : %ld\n", this->coneCount); + printf("Cubes : %ld\n", this->cubeCount); + printf("Cylinders : %ld\n", this->cylinderCount); + printf("Groups : %ld\n", this->groupCount); + printf("Lights : %ld\n", this->lightCount); + printf("Planes : %ld\n", this->planeCount); + printf("Spheres : %ld\n", this->sphereCount); + printf("Triangles : %ld\n", this->triangleCount); + printf("==================================================\n"); + printf("Pixel rendered : %ld\n", this->pixelCount); + printf("Ray casted : %ld\n", this->rayCount); + printf("Light Ray casted : %ld\n", this->lightRayEmitedCount); + printf("Reflection ray casted : %ld\n", this->reflectionRayCount); + printf("Refraction ray casted : %ld\n", this->refractedRayCount); + printf("Intersect object created: %ld\n", this->intersectCount); + printf("Intersection created : %ld\n", this->intersectionCount); + printf("Malloc called : %ld\n", this->mallocCallCount); + printf("Realloc called : %ld\n", this->reallocCallCount); + printf("Bounding box missed : %ld\n", this->discardedIntersectCount); + printf("Min depth atteined : %ld\n", this->maxDepthAttained); + printf("Max intersect count : %ld\n", this->maxIntersectOnARay); + printf("==================================================\n"); + }; +}; +#else +class RenderStats +{ +public: + static void addCone() {}; + static void addCylinder() {}; + static void addCube() {}; + static void addGroup() {}; + static void addLight() {}; + static void addPlane() {}; + static void addSphere() {}; + static void addTriangle() {}; + static void printStats() {}; + static void addPixel() {}; + static void addRay() {}; + static void addLightRay() {}; + static void addReflectRay() {}; + static void addRefractRay() {}; + static void addIntersection() {}; + static void addDiscardedIntersect() {}; + static void setMaxDepth(uint32_t depth) {}; + static void addIntersect() {}; + static void addMalloc() {}; + static void addRealloc() {}; + static void setMaxIntersect(uint32_t count) {}; +}; +#endif + +extern RenderStats stats; + +#endif /* DORAYME_RENDERSTAT_H */ diff --git a/source/include/sphere.h b/source/include/sphere.h index 3bc3f62..b812f53 100644 --- a/source/include/sphere.h +++ b/source/include/sphere.h @@ -12,6 +12,7 @@ #include #include #include +#include class Sphere : public Shape { @@ -20,7 +21,7 @@ protected: Tuple localNormalAt(Tuple point); public: - Sphere() : Shape(SHAPE_SPHERE) { }; + Sphere() : Shape(SHAPE_SPHERE) { stats.addSphere(); }; /* All sphere are at (0, 0, 0) and radius 1 in the object space */ }; diff --git a/source/intersect.cpp b/source/intersect.cpp index af01aa5..a6ccf4a 100644 --- a/source/intersect.cpp +++ b/source/intersect.cpp @@ -11,13 +11,21 @@ #include #include +#include #define MIN_ALLOC (2) +/* TODO: Memory allocation, even if using standard calloc/realloc have a huge impact on performances. need to find a way + * to reuse the intersect object without reallocating from scratch all the time. We use a lot of Intersect objects as + * there is at least 2 per ray (one for Ray intersect object, one object per light) + */ + Intersect::Intersect() { this->allocated = MIN_ALLOC; this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC); + stats.addMalloc(); + stats.addIntersect(); this->num = 0; } @@ -35,10 +43,13 @@ void Intersect::add(Intersection i) if ((this->num + 1) > this->allocated) { this->allocated *= 2; + stats.addRealloc(); this->list = (Intersection **)realloc(this->list, sizeof(Intersection *) * this->allocated); } this->list[this->num++] = new Intersection(i.t, i.object); + stats.setMaxIntersect(this->num); + /* Now sort.. */ for(j = 1; j < (this->num); j++) { diff --git a/source/matrix.cpp b/source/matrix.cpp index ec730fc..a336350 100644 --- a/source/matrix.cpp +++ b/source/matrix.cpp @@ -101,6 +101,7 @@ Matrix Matrix::operator*(const Matrix &b) const return ret; } +/* TODO: Check if we can optimise this function. It is called a lot */ Tuple Matrix::operator*(const Tuple &b) const { return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3), diff --git a/source/renderstat.cpp b/source/renderstat.cpp new file mode 100644 index 0000000..469779f --- /dev/null +++ b/source/renderstat.cpp @@ -0,0 +1,11 @@ +/* + * DoRayMe - a quick and dirty Raytracer + * Render statistics implementation + * + * Created by Manoël Trapier + * Copyright (c) 2020 986-Studio. + * + */ +#include + +RenderStats stats; \ No newline at end of file diff --git a/source/shapes/group.cpp b/source/shapes/group.cpp index aa63e75..7d634f2 100644 --- a/source/shapes/group.cpp +++ b/source/shapes/group.cpp @@ -9,13 +9,14 @@ #include #include #include -#include #include +#include #define MIN_ALLOC (2) Group::Group() : Shape(SHAPE_GROUP) { + stats.addGroup(); this->allocatedObjectCount = MIN_ALLOC; this->objectList = (Shape **)calloc(sizeof(Shape *), MIN_ALLOC); this->objectCount = 0; diff --git a/source/shapes/triangle.cpp b/source/shapes/triangle.cpp index 33526a3..a0a9d8e 100644 --- a/source/shapes/triangle.cpp +++ b/source/shapes/triangle.cpp @@ -10,9 +10,12 @@ #include #include #include +#include Triangle::Triangle(Point p1, Point p2, Point p3) : Shape(SHAPE_TRIANGLE), p1(p1), p2(p2), p3(p3) { + stats.addTriangle(); + this->e1 = p2 - p1; this->e2 = p3 - p1; this->normal = e2.cross(e1).normalise(); diff --git a/source/world.cpp b/source/world.cpp index 1dc60ed..3afaf25 100644 --- a/source/world.cpp +++ b/source/world.cpp @@ -123,6 +123,8 @@ Tuple World::colourAt(Ray r, uint32_t depthCount) Intersect allHits = this->intersect(r); Intersection hit = allHits.hit(); + stats.setMaxDepth(depthCount); + if (hit.nothing()) { return Colour(0, 0, 0); @@ -140,6 +142,7 @@ bool World::isShadowed(Tuple point, uint32_t light) Tuple direction = v.normalise(); Ray r = Ray(point, direction); + stats.addLightRay(); Intersect xs = this->intersect(r); int i; @@ -166,6 +169,7 @@ Colour World::reflectColour(Computation comps, uint32_t depthCount) /* So it is reflective, even just a bit. Let'sr reflect the ray! */ Ray reflectedRay = Ray(comps.overHitPoint, comps.reflectVector); + stats.addReflectRay(); Tuple hitColour = this->colourAt(reflectedRay, depthCount - 1); hitColour = hitColour * comps.material->reflective; @@ -188,6 +192,7 @@ Colour World::refractedColour(Computation comps, uint32_t depthCount) Tuple direction = comps.normalVector * (nRatio * cos_i - cos_t) - comps.eyeVector * nRatio; Ray refractedRay = Ray(comps.underHitPoint, direction); + stats.addRefractRay(); Tuple hitColour = this->colourAt(refractedRay, depthCount - 1) * comps.material->transparency;