Improved render progress.

A bug was also fixed where some canvas pixels were shaded twice,
leading to a performance loss.
This commit is contained in:
Michaël Lemaire 2015-08-23 20:22:37 +02:00
parent e8d91e30ac
commit 7c7b6043c5
18 changed files with 320 additions and 101 deletions

View file

@ -6,16 +6,18 @@
#include "CanvasPixel.h"
#include "CanvasFragment.h"
#include "Rasterizer.h"
#include "RenderProgress.h"
CanvasPixelShader::CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y):
renderer(renderer), portion(portion), chunk_size(chunk_size), sub_chunk_size(sub_chunk_size), chunks_x(chunks_x), chunks_y(chunks_y)
CanvasPixelShader::CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, RenderProgress *progress, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y):
renderer(renderer), portion(portion), progress(progress), chunk_size(chunk_size), sub_chunk_size(sub_chunk_size), chunks_x(chunks_x), chunks_y(chunks_y)
{
}
void CanvasPixelShader::processParallelUnit(int unit)
{
// Locate the chunk we work on
int prev_sub_chunk_size = chunk_size * 2;
int prev_sub_chunk_size = sub_chunk_size * 2;
int chunk_x = unit / chunks_y;
int chunk_y = unit % chunks_y;
int base_x = chunk_x * chunk_size;
@ -29,6 +31,7 @@ void CanvasPixelShader::processParallelUnit(int unit)
// Iterate on sub-chunks
for (int x = 0; x < limit_x; x += sub_chunk_size)
{
int done = 0;
for (int y = 0; y < limit_y; y += sub_chunk_size)
{
if (interrupted)
@ -57,7 +60,10 @@ void CanvasPixelShader::processParallelUnit(int unit)
portion->setColor(base_x + x + fx, base_y + y + fy, composite);
}
}
done++;
}
}
progress->add(done);
}
}

View file

@ -18,13 +18,14 @@ namespace software {
class CanvasPixelShader: public ParallelWorker
{
public:
CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y);
CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, RenderProgress *progress, int chunk_size, int sub_chunk_size, int chunks_x, int chunks_y);
virtual void processParallelUnit(int unit) override;
private:
const SoftwareCanvasRenderer &renderer;
CanvasPortion *portion;
RenderProgress *progress;
int chunk_size;
int sub_chunk_size;
int chunks_x;

View file

@ -5,6 +5,7 @@
#include "CanvasPortion.h"
#include "CanvasFragment.h"
#include "Vector3.h"
#include "RenderProgress.h"
struct paysages::software::ScanPoint
{
@ -31,14 +32,12 @@ struct paysages::software::RenderScanlines
int right;
};
Rasterizer::Rasterizer(SoftwareRenderer* renderer, int client_id, const Color &color):
renderer(renderer), client_id(client_id)
Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id, const Color &color):
renderer(renderer), progress(progress), client_id(client_id)
{
this->color = new Color(color);
interrupted = false;
predicted_poly_count = 0;
done_poly_count = 0;
}
Rasterizer::~Rasterizer()
@ -51,16 +50,6 @@ void Rasterizer::interrupt()
interrupted = true;
}
void Rasterizer::addPredictedPolys(int count)
{
predicted_poly_count += count;
}
void Rasterizer::addDonePolys(int count)
{
done_poly_count += count;
}
void Rasterizer::pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3)
{
ScanPoint point1, point2, point3;

View file

@ -15,19 +15,24 @@ typedef struct RenderScanlines RenderScanlines;
class SOFTWARESHARED_EXPORT Rasterizer
{
public:
Rasterizer(SoftwareRenderer *renderer, int client_id, const Color &color);
Rasterizer(SoftwareRenderer *renderer, RenderProgress *progress, int client_id, const Color &color);
virtual ~Rasterizer();
inline SoftwareRenderer *getRenderer() const {return renderer;}
virtual void rasterizeToCanvas(CanvasPortion* canvas) = 0;
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
virtual void interrupt();
protected:
void addPredictedPolys(int count=1);
void addDonePolys(int count=1);
/**
* Abstract method to prepare for the rasterization process, and return the estimated progress count.
*/
virtual int prepareRasterization() = 0;
/**
* Abstract method to effectively do the rasterization on a canvas.
*/
virtual void rasterizeToCanvas(CanvasPortion* canvas) = 0;
protected:
void pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3);
void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3);
@ -37,6 +42,7 @@ protected:
Color* color;
SoftwareRenderer *renderer;
RenderProgress *progress;
int client_id;
bool interrupted;
@ -46,9 +52,6 @@ private:
void pushScanPoint(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point);
void pushScanLineEdge(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point1, ScanPoint *point2);
void renderScanLines(CanvasPortion *canvas, RenderScanlines *scanlines);
int predicted_poly_count;
int done_poly_count;
};
}

View file

@ -0,0 +1,54 @@
#include "RenderProgress.h"
#include "Mutex.h"
RenderProgress::RenderProgress(int count)
{
lock = new Mutex();
global = 0.0;
step = 1.0 / (double)count;
}
paysages::software::RenderProgress::~RenderProgress()
{
delete lock;
}
void RenderProgress::add(int value)
{
lock->acquire();
global += step * (double)value;
lock->release();
}
void RenderProgress::enterSub(int count)
{
struct RenderSub sub;
sub.start = global;
sub.end = global + step;
sub.count = count;
sub.previous_step = step;
lock->acquire();
subs.push(sub);
step /= (double)count;
lock->release();
}
void RenderProgress::exitSub()
{
lock->acquire();
struct RenderSub sub = subs.top();
global = sub.end;
step = sub.previous_step;
subs.pop();
lock->release();
}

View file

@ -0,0 +1,43 @@
#ifndef RENDERPROGRESS_H
#define RENDERPROGRESS_H
#include "software_global.h"
#include <stack>
namespace paysages {
namespace software {
struct RenderSub {
double start;
double end;
int count;
double previous_step;
};
/**
* Utility to keep track of render progress.
*/
class SOFTWARESHARED_EXPORT RenderProgress
{
public:
RenderProgress(int count=1);
~RenderProgress();
inline double get() const {return global;}
void add(int value=1);
void enterSub(int count);
void exitSub();
private:
Mutex *lock;
double global;
double step;
std::stack<RenderSub> subs;
};
}
}
#endif // RENDERPROGRESS_H

View file

@ -8,25 +8,31 @@
#include "CloudsRenderer.h"
#include "Rasterizer.h"
#include "CanvasFragment.h"
#include "RenderProgress.h"
#define SPHERE_SIZE 20000.0
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id):
Rasterizer(renderer, client_id, Color(0.9, 0.9, 1.0))
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, progress, client_id, Color(0.9, 0.9, 1.0))
{
}
int SkyRasterizer::prepareRasterization()
{
res_i = renderer->render_quality * 40;
res_j = renderer->render_quality * 20;
return res_i * res_j;
}
void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
{
int res_i, res_j;
int i, j;
double step_i, step_j;
double current_i, current_j;
Vector3 vertex1, vertex2, vertex3, vertex4;
Vector3 camera_location, direction;
res_i = renderer->render_quality * 40;
res_j = renderer->render_quality * 20;
step_i = M_PI * 2.0 / (double)res_i;
step_j = M_PI / (double)res_j;
@ -68,6 +74,7 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
/* TODO Triangles at poles */
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
}
progress->add(res_i);
}
}

View file

@ -11,10 +11,15 @@ namespace software {
class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer
{
public:
SkyRasterizer(SoftwareRenderer* renderer, int client_id);
SkyRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id);
virtual int prepareRasterization() override;
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
private:
int res_i;
int res_j;
};
}

View file

@ -13,6 +13,7 @@
#include "RenderConfig.h"
#include "ColorProfile.h"
#include "CanvasPreview.h"
#include "RenderProgress.h"
SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
SoftwareRenderer(scenery)
@ -21,12 +22,12 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
finished = false;
interrupted = false;
canvas = new Canvas();
progress = 0.0;
progress = new RenderProgress();
samples = 1;
rasterizers.push_back(new SkyRasterizer(this, 0));
rasterizers.push_back(new WaterRasterizer(this, 1));
rasterizers.push_back(new TerrainRasterizer(this, 2));
rasterizers.push_back(new SkyRasterizer(this, progress, 0));
rasterizers.push_back(new WaterRasterizer(this, progress, 1));
rasterizers.push_back(new TerrainRasterizer(this, progress, 2));
current_work = NULL;
}
@ -34,6 +35,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
SoftwareCanvasRenderer::~SoftwareCanvasRenderer()
{
delete canvas;
delete progress;
for (auto &rasterizer: rasterizers)
{
@ -41,6 +43,11 @@ SoftwareCanvasRenderer::~SoftwareCanvasRenderer()
}
}
double SoftwareCanvasRenderer::getProgress() const
{
return progress->get();
}
void SoftwareCanvasRenderer::setConfig(const RenderConfig &config)
{
if (not started)
@ -62,7 +69,6 @@ void SoftwareCanvasRenderer::setSize(int width, int height, int samples)
void SoftwareCanvasRenderer::render()
{
started = true;
progress = 0.0;
render_camera->setRenderSize(canvas->getWidth(), canvas->getHeight());
@ -71,32 +77,33 @@ void SoftwareCanvasRenderer::render()
// Iterate portions
int nx = canvas->getHorizontalPortionCount();
int ny = canvas->getVerticalPortionCount();
int i = 0;
int n = nx * ny;
progress->enterSub(n);
for (int y = 0; y < ny; y++)
{
for (int x = 0; x < nx; x++)
{
CanvasPortion *portion = canvas->at(x, y);
progress->enterSub(2);
if (not interrupted)
{
progress_segment = 0.2 / (double)n;
portion->preparePixels();
rasterize(portion);
}
if (not interrupted)
{
progress_segment = 0.8 / (double)n;
applyPixelShader(portion);
}
portion->discardPixels();
i++;
progress = (double)i / (double)n;
progress->exitSub();
}
}
progress->exitSub();
finished = true;
}
@ -126,11 +133,18 @@ bool SoftwareCanvasRenderer::saveToDisk(const std::string &filepath) const
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion)
{
int count = 0;
for (auto &rasterizer:rasterizers)
{
count += rasterizer->prepareRasterization();
}
progress->enterSub(count);
for (auto &rasterizer:rasterizers)
{
rasterizer->rasterizeToCanvas(portion);
progress += progress_segment / (double)rasterizers.size();
}
progress->exitSub();
}
void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
@ -141,14 +155,8 @@ void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
int chunks_y = (portion->getHeight() - 1) / chunk_size + 1;
int units = chunks_x * chunks_y;
// Estimate chunks
int n = 0;
for (int sub_chunk_size = chunk_size; sub_chunk_size >= 1; sub_chunk_size /= 2)
{
n += chunk_size / sub_chunk_size;
}
// Render chunks in parallel
progress->enterSub(portion->getWidth() * portion->getHeight());
for (int sub_chunk_size = chunk_size; sub_chunk_size >= 1; sub_chunk_size /= 2)
{
if (interrupted)
@ -156,12 +164,12 @@ void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
break;
}
CanvasPixelShader shader(*this, portion, chunk_size, sub_chunk_size, chunks_x, chunks_y);
CanvasPixelShader shader(*this, portion, progress, chunk_size, sub_chunk_size, chunks_x, chunks_y);
ParallelWork work(&shader, units);
current_work = &work;
work.perform();
current_work = NULL;
progress += progress_segment * (double)(chunk_size / sub_chunk_size) / (double)n;
}
progress->exitSub();
}

View file

@ -23,9 +23,13 @@ public:
virtual ~SoftwareCanvasRenderer();
inline const Canvas *getCanvas() const {return canvas;}
inline double getProgress() const {return progress;}
inline bool isFinished() const {return finished;}
/**
* Get the global rendering progress (0.0-1.0).
*/
double getProgress() const;
/**
* Set the renderer configuration.
*/
@ -72,8 +76,7 @@ protected:
void applyPixelShader(CanvasPortion *portion);
private:
double progress;
double progress_segment;
RenderProgress *progress;
Canvas *canvas;
int samples;

View file

@ -9,9 +9,10 @@
#include "Scenery.h"
#include "CanvasPortion.h"
#include "CanvasFragment.h"
#include "RenderProgress.h"
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id):
Rasterizer(renderer, client_id, Color(1.0, 0.9, 0.9))
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, progress, client_id, Color(1.0, 0.9, 0.9))
{
}
@ -22,11 +23,6 @@ static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z)
void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail)
{
if (detail < 1)
{
return;
}
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
double startx = chunk->point_nw.x;
@ -40,6 +36,7 @@ void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo*
{
renderQuad(canvas, startx + (double)i * size, startz + (double)j * size, size, water_height);
}
progress->add(detail);
}
}
@ -125,12 +122,11 @@ static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChun
}
}
void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displaced)
int TerrainRasterizer::performTessellation(CanvasPortion* canvas, bool displaced)
{
TerrainChunkInfo chunk;
int chunk_factor, chunk_count, i;
int chunk_factor, chunk_count, i, result;
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
double progress;
double radius_int, radius_ext;
double base_chunk_size, chunk_size;
@ -141,42 +137,69 @@ void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displace
radius_int = 0.0;
radius_ext = base_chunk_size;
chunk_size = base_chunk_size;
progress = 0.0;
result = 0;
double cx = cam.x - fmod(cam.x, base_chunk_size);
double cz = cam.z - fmod(cam.x, base_chunk_size);
while (radius_int < 20000.0)
{
progress = radius_int / 20000.0;
for (i = 0; i < chunk_count - 1; i++)
{
_getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
processChunk(canvas, &chunk, progress);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
if (canvas)
{
processChunk(canvas, &chunk);
}
}
if (interrupted)
{
return;
return result;
}
_getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
processChunk(canvas, &chunk, progress);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
if (canvas)
{
processChunk(canvas, &chunk);
}
}
if (interrupted)
{
return;
return result;
}
_getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
processChunk(canvas, &chunk, progress);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
if (canvas)
{
processChunk(canvas, &chunk);
}
}
if (interrupted)
{
return;
return result;
}
_getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
processChunk(canvas, &chunk, progress);
if (chunk.detail_hint > 0)
{
result += chunk.detail_hint * chunk.detail_hint;
if (canvas)
{
processChunk(canvas, &chunk);
}
}
if (interrupted)
{
return;
return result;
}
}
@ -190,16 +213,24 @@ void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displace
radius_int = radius_ext;
radius_ext += chunk_size;
}
return result;
}
void TerrainRasterizer::processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, double)
void TerrainRasterizer::processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk)
{
tessellateChunk(canvas, chunk, chunk->detail_hint);
}
int TerrainRasterizer::prepareRasterization()
{
// TODO Chunks could be saved and reused in rasterizeToCanvas
return performTessellation(NULL, false);
}
void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{
getTessellationInfo(canvas, false);
performTessellation(canvas, false);
}
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const

View file

@ -22,19 +22,28 @@ public:
} TerrainChunkInfo;
public:
TerrainRasterizer(SoftwareRenderer* renderer, int client_id);
TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id);
virtual int prepareRasterization() override;
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
private:
/**
* Method called for each chunk tessellated by getTessellationInfo.
* Method called for each chunk tessellated by performTessellation.
*/
void processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, double progress);
void processChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk);
/**
* Tessellate the terrain, calling processChunk for each chunk.
*
* The terrain will be broken in chunks, most detailed near the camera.
*
* Return the number of quads that has been pushed.
*
* *canvas* may be NULL to only simulate the tessellation.
*/
void getTessellationInfo(CanvasPortion* canvas, bool displaced);
int performTessellation(CanvasPortion* canvas, bool displaced);
/**
* Tessellate a terrain chunk, pushing the quads in the render area.
@ -42,9 +51,6 @@ public:
void tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail);
void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height);
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
};
}

View file

@ -3,9 +3,10 @@
#include "SoftwareRenderer.h"
#include "WaterRenderer.h"
#include "CanvasFragment.h"
#include "RenderProgress.h"
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id):
Rasterizer(renderer, client_id, Color(0.9, 0.95, 1.0))
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, progress, client_id, Color(0.9, 0.95, 1.0))
{
}
@ -32,9 +33,25 @@ void WaterRasterizer::rasterizeQuad(CanvasPortion* canvas, double x, double z, d
pushQuad(canvas, v1, v2, v3, v4);
}
int WaterRasterizer::prepareRasterization()
{
return performTessellation(NULL);
}
void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{
int chunk_factor, chunk_count, i;
performTessellation(canvas);
}
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
Vector3 location = fragment.getLocation();
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
}
int WaterRasterizer::performTessellation(CanvasPortion *canvas)
{
int chunk_factor, chunk_count, i, result;
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
double radius_int, radius_ext, base_chunk_size, chunk_size;
@ -44,6 +61,7 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
base_chunk_size *= 0.5;
}
result = 0;
chunk_factor = 1;
chunk_count = 2;
radius_int = 0.0;
@ -57,16 +75,24 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{
if (interrupted)
{
return;
return result;
}
for (i = 0; i < chunk_count - 1; i++)
{
result++;
if (canvas)
{
rasterizeQuad(canvas, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
rasterizeQuad(canvas, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
rasterizeQuad(canvas, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
rasterizeQuad(canvas, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
}
}
if (canvas)
{
progress->add(chunk_count - 1);
}
if (radius_int > 20.0 && chunk_count % 64 == 0 && (double)chunk_factor < radius_int / 20.0)
{
@ -78,10 +104,6 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
radius_int = radius_ext;
radius_ext += chunk_size;
}
}
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
Vector3 location = fragment.getLocation();
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
return result;
}

View file

@ -11,12 +11,16 @@ namespace software {
class WaterRasterizer: public Rasterizer
{
public:
WaterRasterizer(SoftwareRenderer* renderer, int client_id);
WaterRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id);
void rasterizeQuad(CanvasPortion* canvas, double x, double z, double size);
virtual int prepareRasterization() override;
virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const override;
private:
int performTessellation(CanvasPortion* canvas);
};
}

View file

@ -50,7 +50,8 @@ SOURCES += SoftwareRenderer.cpp \
CanvasPictureWriter.cpp \
clouds/CloudModelAltoCumulus.cpp \
clouds/CloudModelCirrus.cpp \
clouds/CloudModelCumuloNimbus.cpp
clouds/CloudModelCumuloNimbus.cpp \
RenderProgress.cpp
HEADERS += SoftwareRenderer.h\
software_global.h \
@ -90,7 +91,8 @@ HEADERS += SoftwareRenderer.h\
CanvasPictureWriter.h \
clouds/CloudModelAltoCumulus.h \
clouds/CloudModelCirrus.h \
clouds/CloudModelCumuloNimbus.h
clouds/CloudModelCumuloNimbus.h \
RenderProgress.h
unix:!symbian {
maemo5 {

View file

@ -16,6 +16,7 @@ namespace software {
class SoftwareRenderer;
class SoftwareCanvasRenderer;
class RenderConfig;
class RenderProgress;
class FluidMediumManager;
class FluidMediumInterface;

View file

@ -0,0 +1,33 @@
#include "BaseTestCase.h"
#include "RenderProgress.h"
TEST(RenderProgress, RecursiveSections)
{
RenderProgress progress(2);
EXPECT_DOUBLE_EQ(0.0, progress.get());
progress.enterSub(5);
EXPECT_DOUBLE_EQ(0.0, progress.get());
progress.add();
EXPECT_DOUBLE_EQ(0.1, progress.get());
progress.add(2);
EXPECT_DOUBLE_EQ(0.3, progress.get());
progress.enterSub(4);
EXPECT_DOUBLE_EQ(0.3, progress.get());
progress.add(1);
EXPECT_DOUBLE_EQ(0.325, progress.get());
progress.exitSub();
EXPECT_DOUBLE_EQ(0.4, progress.get());
progress.exitSub();
EXPECT_DOUBLE_EQ(0.5, progress.get());
progress.add(1);
EXPECT_DOUBLE_EQ(1.0, progress.get());
}

View file

@ -26,7 +26,8 @@ SOURCES += main.cpp \
DefinitionNode_Test.cpp \
FloatNode_Test.cpp \
DiffManager_Test.cpp \
ColorHSL_Test.cpp
ColorHSL_Test.cpp \
RenderProgress_Test.cpp
HEADERS += \
BaseTestCase.h