Browse Source

Add renderstat to get some info about rendering.

Godzil 4 years ago
parent
commit
a6f0422bd1

+ 5 - 0
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()

+ 1 - 0
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()

+ 6 - 2
source/camera.cpp

@@ -13,6 +13,7 @@
 #include <camera.h>
 
 #include <stdio.h>
+#include <renderstat.h>
 
 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;
 }

+ 6 - 1
source/include/boundingbox.h

@@ -9,6 +9,8 @@
 #ifndef DORAYME_BOUNDINGBOX_H
 #define DORAYME_BOUNDINGBOX_H
 
+#include <renderstat.h>
+
 struct BoundingBox
 {
 private:
@@ -110,9 +112,12 @@ public:
         {
             return true;
         }
+
+        stats.addDiscardedIntersect();
+
         return false;
     }
 
 };
 
-#endif //DORAYME_BOUNDINGBOX_H
+#endif /* DORAYME_BOUNDINGBOX_H */

+ 2 - 1
source/include/cone.h

@@ -12,6 +12,7 @@
 #include <shape.h>
 #include <ray.h>
 #include <intersect.h>
+#include <renderstat.h>
 
 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)); };
 };

+ 2 - 1
source/include/cube.h

@@ -12,6 +12,7 @@
 #include <shape.h>
 #include <ray.h>
 #include <intersect.h>
+#include <renderstat.h>
 
 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 */

+ 2 - 1
source/include/cylinder.h

@@ -12,6 +12,7 @@
 #include <shape.h>
 #include <ray.h>
 #include <intersect.h>
+#include <renderstat.h>
 
 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)); };

+ 2 - 1
source/include/intersection.h

@@ -12,6 +12,7 @@
 #include <stdlib.h>
 #include <ray.h>
 #include <material.h>
+#include <renderstat.h>
 
 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);

+ 3 - 1
source/include/light.h

@@ -11,6 +11,7 @@
 
 #include <tuple.h>
 #include <colour.h>
+#include <renderstat.h>
 
 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 &&

+ 3 - 1
source/include/plane.h

@@ -9,6 +9,8 @@
 #ifndef DORAYME_PLANE_H
 #define DORAYME_PLANE_H
 
+#include <renderstat.h>
+
 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; };
 };

+ 2 - 1
source/include/ray.h

@@ -10,6 +10,7 @@
 #define DORAYME_RAY_H
 
 #include <tuple.h>
+#include <renderstat.h>
 
 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; };
 };

+ 125 - 0
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 <stdint.h>
+
+#ifdef RENDER_STATS
+#include <stdio.h>
+
+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->maxIntersectOnARay<count) { this->maxIntersectOnARay = 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 */

+ 2 - 1
source/include/sphere.h

@@ -12,6 +12,7 @@
 #include <shape.h>
 #include <ray.h>
 #include <intersect.h>
+#include <renderstat.h>
 
 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 */
 };
 

+ 11 - 0
source/intersect.cpp

@@ -11,13 +11,21 @@
 #include <intersect.h>
 
 #include <float.h>
+#include <renderstat.h>
 
 #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++)
     {

+ 1 - 0
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),

+ 11 - 0
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 <renderstat.h>
+
+RenderStats stats;

+ 2 - 1
source/shapes/group.cpp

@@ -9,13 +9,14 @@
 #include <tuple.h>
 #include <ray.h>
 #include <group.h>
-#include <cone.h>
 #include <math_helper.h>
+#include <renderstat.h>
 
 #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;

+ 3 - 0
source/shapes/triangle.cpp

@@ -10,9 +10,12 @@
 #include <shape.h>
 #include <triangle.h>
 #include <math_helper.h>
+#include <renderstat.h>
 
 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();

+ 5 - 0
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;