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:
parent
e8d91e30ac
commit
7c7b6043c5
18 changed files with 320 additions and 101 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
54
src/render/software/RenderProgress.cpp
Normal file
54
src/render/software/RenderProgress.cpp
Normal 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();
|
||||
}
|
43
src/render/software/RenderProgress.h
Normal file
43
src/render/software/RenderProgress.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace software {
|
|||
class SoftwareRenderer;
|
||||
class SoftwareCanvasRenderer;
|
||||
class RenderConfig;
|
||||
class RenderProgress;
|
||||
|
||||
class FluidMediumManager;
|
||||
class FluidMediumInterface;
|
||||
|
|
33
src/tests/RenderProgress_Test.cpp
Normal file
33
src/tests/RenderProgress_Test.cpp
Normal 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());
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue