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

View file

@ -5,6 +5,7 @@
#include "CanvasPortion.h" #include "CanvasPortion.h"
#include "CanvasFragment.h" #include "CanvasFragment.h"
#include "Vector3.h" #include "Vector3.h"
#include "RenderProgress.h"
struct paysages::software::ScanPoint struct paysages::software::ScanPoint
{ {
@ -31,14 +32,12 @@ struct paysages::software::RenderScanlines
int right; int right;
}; };
Rasterizer::Rasterizer(SoftwareRenderer* renderer, int client_id, const Color &color): Rasterizer::Rasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id, const Color &color):
renderer(renderer), client_id(client_id) renderer(renderer), progress(progress), client_id(client_id)
{ {
this->color = new Color(color); this->color = new Color(color);
interrupted = false; interrupted = false;
predicted_poly_count = 0;
done_poly_count = 0;
} }
Rasterizer::~Rasterizer() Rasterizer::~Rasterizer()
@ -51,16 +50,6 @@ void Rasterizer::interrupt()
interrupted = true; 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) 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; ScanPoint point1, point2, point3;

View file

@ -15,19 +15,24 @@ typedef struct RenderScanlines RenderScanlines;
class SOFTWARESHARED_EXPORT Rasterizer class SOFTWARESHARED_EXPORT Rasterizer
{ {
public: public:
Rasterizer(SoftwareRenderer *renderer, int client_id, const Color &color); Rasterizer(SoftwareRenderer *renderer, RenderProgress *progress, int client_id, const Color &color);
virtual ~Rasterizer(); virtual ~Rasterizer();
inline SoftwareRenderer *getRenderer() const {return renderer;} inline SoftwareRenderer *getRenderer() const {return renderer;}
virtual void rasterizeToCanvas(CanvasPortion* canvas) = 0;
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0; virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
virtual void interrupt(); virtual void interrupt();
protected: /**
void addPredictedPolys(int count=1); * Abstract method to prepare for the rasterization process, and return the estimated progress count.
void addDonePolys(int count=1); */
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 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); void pushTriangle(CanvasPortion *canvas, const Vector3 &v1, const Vector3 &v2, const Vector3 &v3);
@ -37,6 +42,7 @@ protected:
Color* color; Color* color;
SoftwareRenderer *renderer; SoftwareRenderer *renderer;
RenderProgress *progress;
int client_id; int client_id;
bool interrupted; bool interrupted;
@ -46,9 +52,6 @@ private:
void pushScanPoint(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point); void pushScanPoint(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point);
void pushScanLineEdge(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point1, ScanPoint *point2); void pushScanLineEdge(CanvasPortion *canvas, RenderScanlines *scanlines, ScanPoint *point1, ScanPoint *point2);
void renderScanLines(CanvasPortion *canvas, RenderScanlines *scanlines); 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 "CloudsRenderer.h"
#include "Rasterizer.h" #include "Rasterizer.h"
#include "CanvasFragment.h" #include "CanvasFragment.h"
#include "RenderProgress.h"
#define SPHERE_SIZE 20000.0 #define SPHERE_SIZE 20000.0
SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id): SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, client_id, Color(0.9, 0.9, 1.0)) 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) void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
{ {
int res_i, res_j;
int i, j; int i, j;
double step_i, step_j; double step_i, step_j;
double current_i, current_j; double current_i, current_j;
Vector3 vertex1, vertex2, vertex3, vertex4; Vector3 vertex1, vertex2, vertex3, vertex4;
Vector3 camera_location, direction; 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_i = M_PI * 2.0 / (double)res_i;
step_j = M_PI / (double)res_j; step_j = M_PI / (double)res_j;
@ -68,6 +74,7 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
/* TODO Triangles at poles */ /* TODO Triangles at poles */
pushQuad(canvas, vertex1, vertex4, vertex3, vertex2); pushQuad(canvas, vertex1, vertex4, vertex3, vertex2);
} }
progress->add(res_i);
} }
} }

View file

@ -11,10 +11,15 @@ namespace software {
class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer class SOFTWARESHARED_EXPORT SkyRasterizer: public Rasterizer
{ {
public: 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 void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const 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 "RenderConfig.h"
#include "ColorProfile.h" #include "ColorProfile.h"
#include "CanvasPreview.h" #include "CanvasPreview.h"
#include "RenderProgress.h"
SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery): SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
SoftwareRenderer(scenery) SoftwareRenderer(scenery)
@ -21,12 +22,12 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
finished = false; finished = false;
interrupted = false; interrupted = false;
canvas = new Canvas(); canvas = new Canvas();
progress = 0.0; progress = new RenderProgress();
samples = 1; samples = 1;
rasterizers.push_back(new SkyRasterizer(this, 0)); rasterizers.push_back(new SkyRasterizer(this, progress, 0));
rasterizers.push_back(new WaterRasterizer(this, 1)); rasterizers.push_back(new WaterRasterizer(this, progress, 1));
rasterizers.push_back(new TerrainRasterizer(this, 2)); rasterizers.push_back(new TerrainRasterizer(this, progress, 2));
current_work = NULL; current_work = NULL;
} }
@ -34,6 +35,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer(Scenery *scenery):
SoftwareCanvasRenderer::~SoftwareCanvasRenderer() SoftwareCanvasRenderer::~SoftwareCanvasRenderer()
{ {
delete canvas; delete canvas;
delete progress;
for (auto &rasterizer: rasterizers) for (auto &rasterizer: rasterizers)
{ {
@ -41,6 +43,11 @@ SoftwareCanvasRenderer::~SoftwareCanvasRenderer()
} }
} }
double SoftwareCanvasRenderer::getProgress() const
{
return progress->get();
}
void SoftwareCanvasRenderer::setConfig(const RenderConfig &config) void SoftwareCanvasRenderer::setConfig(const RenderConfig &config)
{ {
if (not started) if (not started)
@ -62,7 +69,6 @@ void SoftwareCanvasRenderer::setSize(int width, int height, int samples)
void SoftwareCanvasRenderer::render() void SoftwareCanvasRenderer::render()
{ {
started = true; started = true;
progress = 0.0;
render_camera->setRenderSize(canvas->getWidth(), canvas->getHeight()); render_camera->setRenderSize(canvas->getWidth(), canvas->getHeight());
@ -71,32 +77,33 @@ void SoftwareCanvasRenderer::render()
// Iterate portions // Iterate portions
int nx = canvas->getHorizontalPortionCount(); int nx = canvas->getHorizontalPortionCount();
int ny = canvas->getVerticalPortionCount(); int ny = canvas->getVerticalPortionCount();
int i = 0;
int n = nx * ny; int n = nx * ny;
progress->enterSub(n);
for (int y = 0; y < ny; y++) for (int y = 0; y < ny; y++)
{ {
for (int x = 0; x < nx; x++) for (int x = 0; x < nx; x++)
{ {
CanvasPortion *portion = canvas->at(x, y); CanvasPortion *portion = canvas->at(x, y);
progress->enterSub(2);
if (not interrupted) if (not interrupted)
{ {
progress_segment = 0.2 / (double)n;
portion->preparePixels(); portion->preparePixels();
rasterize(portion); rasterize(portion);
} }
if (not interrupted) if (not interrupted)
{ {
progress_segment = 0.8 / (double)n;
applyPixelShader(portion); applyPixelShader(portion);
} }
portion->discardPixels(); portion->discardPixels();
i++;
progress = (double)i / (double)n; progress->exitSub();
} }
} }
progress->exitSub();
finished = true; finished = true;
} }
@ -126,11 +133,18 @@ bool SoftwareCanvasRenderer::saveToDisk(const std::string &filepath) const
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion) void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion)
{ {
int count = 0;
for (auto &rasterizer:rasterizers)
{
count += rasterizer->prepareRasterization();
}
progress->enterSub(count);
for (auto &rasterizer:rasterizers) for (auto &rasterizer:rasterizers)
{ {
rasterizer->rasterizeToCanvas(portion); rasterizer->rasterizeToCanvas(portion);
progress += progress_segment / (double)rasterizers.size();
} }
progress->exitSub();
} }
void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion) void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
@ -141,14 +155,8 @@ void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
int chunks_y = (portion->getHeight() - 1) / chunk_size + 1; int chunks_y = (portion->getHeight() - 1) / chunk_size + 1;
int units = chunks_x * chunks_y; 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 // 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) for (int sub_chunk_size = chunk_size; sub_chunk_size >= 1; sub_chunk_size /= 2)
{ {
if (interrupted) if (interrupted)
@ -156,12 +164,12 @@ void SoftwareCanvasRenderer::applyPixelShader(CanvasPortion *portion)
break; 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); ParallelWork work(&shader, units);
current_work = &work; current_work = &work;
work.perform(); work.perform();
current_work = NULL; 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(); virtual ~SoftwareCanvasRenderer();
inline const Canvas *getCanvas() const {return canvas;} inline const Canvas *getCanvas() const {return canvas;}
inline double getProgress() const {return progress;}
inline bool isFinished() const {return finished;} inline bool isFinished() const {return finished;}
/**
* Get the global rendering progress (0.0-1.0).
*/
double getProgress() const;
/** /**
* Set the renderer configuration. * Set the renderer configuration.
*/ */
@ -72,8 +76,7 @@ protected:
void applyPixelShader(CanvasPortion *portion); void applyPixelShader(CanvasPortion *portion);
private: private:
double progress; RenderProgress *progress;
double progress_segment;
Canvas *canvas; Canvas *canvas;
int samples; int samples;

View file

@ -9,9 +9,10 @@
#include "Scenery.h" #include "Scenery.h"
#include "CanvasPortion.h" #include "CanvasPortion.h"
#include "CanvasFragment.h" #include "CanvasFragment.h"
#include "RenderProgress.h"
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id): TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, client_id, Color(1.0, 0.9, 0.9)) 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) void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail)
{ {
if (detail < 1)
{
return;
}
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height; double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
double startx = chunk->point_nw.x; 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); 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; TerrainChunkInfo chunk;
int chunk_factor, chunk_count, i; int chunk_factor, chunk_count, i, result;
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO); Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
double progress;
double radius_int, radius_ext; double radius_int, radius_ext;
double base_chunk_size, chunk_size; double base_chunk_size, chunk_size;
@ -141,42 +137,69 @@ void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displace
radius_int = 0.0; radius_int = 0.0;
radius_ext = base_chunk_size; radius_ext = base_chunk_size;
chunk_size = base_chunk_size; chunk_size = base_chunk_size;
progress = 0.0; result = 0;
double cx = cam.x - fmod(cam.x, base_chunk_size); double cx = cam.x - fmod(cam.x, base_chunk_size);
double cz = cam.z - fmod(cam.x, base_chunk_size); double cz = cam.z - fmod(cam.x, base_chunk_size);
while (radius_int < 20000.0) while (radius_int < 20000.0)
{ {
progress = radius_int / 20000.0;
for (i = 0; i < chunk_count - 1; i++) for (i = 0; i < chunk_count - 1; i++)
{ {
_getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced); _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) if (interrupted)
{ {
return; return result;
} }
_getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced); _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) if (interrupted)
{ {
return; return result;
} }
_getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced); _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) if (interrupted)
{ {
return; return result;
} }
_getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced); _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) if (interrupted)
{ {
return; return result;
} }
} }
@ -190,16 +213,24 @@ void TerrainRasterizer::getTessellationInfo(CanvasPortion* canvas, bool displace
radius_int = radius_ext; radius_int = radius_ext;
radius_ext += chunk_size; 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); 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) void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{ {
getTessellationInfo(canvas, false); performTessellation(canvas, false);
} }
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const

View file

@ -22,19 +22,28 @@ public:
} TerrainChunkInfo; } TerrainChunkInfo;
public: 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. * Tessellate the terrain, calling processChunk for each chunk.
* *
* The terrain will be broken in chunks, most detailed near the camera. * 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. * 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 tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail);
void renderQuad(CanvasPortion* canvas, double x, double z, double size, double water_height); 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 "SoftwareRenderer.h"
#include "WaterRenderer.h" #include "WaterRenderer.h"
#include "CanvasFragment.h" #include "CanvasFragment.h"
#include "RenderProgress.h"
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id): WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, RenderProgress *progress, int client_id):
Rasterizer(renderer, client_id, Color(0.9, 0.95, 1.0)) 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); pushQuad(canvas, v1, v2, v3, v4);
} }
int WaterRasterizer::prepareRasterization()
{
return performTessellation(NULL);
}
void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas) 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); Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
double radius_int, radius_ext, base_chunk_size, chunk_size; double radius_int, radius_ext, base_chunk_size, chunk_size;
@ -44,6 +61,7 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
base_chunk_size *= 0.5; base_chunk_size *= 0.5;
} }
result = 0;
chunk_factor = 1; chunk_factor = 1;
chunk_count = 2; chunk_count = 2;
radius_int = 0.0; radius_int = 0.0;
@ -57,15 +75,23 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
{ {
if (interrupted) if (interrupted)
{ {
return; return result;
} }
for (i = 0; i < chunk_count - 1; i++) for (i = 0; i < chunk_count - 1; i++)
{ {
rasterizeQuad(canvas, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size); result++;
rasterizeQuad(canvas, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size); if (canvas)
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); 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) 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_int = radius_ext;
radius_ext += chunk_size; radius_ext += chunk_size;
} }
}
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const return result;
{
Vector3 location = fragment.getLocation();
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
} }

View file

@ -11,12 +11,16 @@ namespace software {
class WaterRasterizer: public Rasterizer class WaterRasterizer: public Rasterizer
{ {
public: 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); void rasterizeQuad(CanvasPortion* canvas, double x, double z, double size);
virtual int prepareRasterization() override;
virtual void rasterizeToCanvas(CanvasPortion* canvas) override; virtual void rasterizeToCanvas(CanvasPortion* canvas) override;
virtual Color shadeFragment(const CanvasFragment &fragment) const 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 \ CanvasPictureWriter.cpp \
clouds/CloudModelAltoCumulus.cpp \ clouds/CloudModelAltoCumulus.cpp \
clouds/CloudModelCirrus.cpp \ clouds/CloudModelCirrus.cpp \
clouds/CloudModelCumuloNimbus.cpp clouds/CloudModelCumuloNimbus.cpp \
RenderProgress.cpp
HEADERS += SoftwareRenderer.h\ HEADERS += SoftwareRenderer.h\
software_global.h \ software_global.h \
@ -90,7 +91,8 @@ HEADERS += SoftwareRenderer.h\
CanvasPictureWriter.h \ CanvasPictureWriter.h \
clouds/CloudModelAltoCumulus.h \ clouds/CloudModelAltoCumulus.h \
clouds/CloudModelCirrus.h \ clouds/CloudModelCirrus.h \
clouds/CloudModelCumuloNimbus.h clouds/CloudModelCumuloNimbus.h \
RenderProgress.h
unix:!symbian { unix:!symbian {
maemo5 { maemo5 {

View file

@ -16,6 +16,7 @@ namespace software {
class SoftwareRenderer; class SoftwareRenderer;
class SoftwareCanvasRenderer; class SoftwareCanvasRenderer;
class RenderConfig; class RenderConfig;
class RenderProgress;
class FluidMediumManager; class FluidMediumManager;
class FluidMediumInterface; 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 \ DefinitionNode_Test.cpp \
FloatNode_Test.cpp \ FloatNode_Test.cpp \
DiffManager_Test.cpp \ DiffManager_Test.cpp \
ColorHSL_Test.cpp ColorHSL_Test.cpp \
RenderProgress_Test.cpp
HEADERS += \ HEADERS += \
BaseTestCase.h BaseTestCase.h