WIP on canvas pixel shading

This commit is contained in:
Michaël Lemaire 2014-08-18 12:17:16 +02:00
parent fb3d32baf4
commit 2aeecdec62
24 changed files with 317 additions and 83 deletions

View file

@ -99,3 +99,8 @@ void CanvasPixel::updateComposite()
}
composite = result;
}
void CanvasPixel::setComposite(const Color &color)
{
composite = color;
}

View file

@ -22,11 +22,13 @@ public:
inline int getFragmentCount() const {return count;}
inline const Color &getComposite() const {return composite;}
inline const CanvasFragment &getFragment(int position) const {return fragments[position];}
const CanvasFragment *getFrontFragment() const;
void reset();
void pushFragment(const CanvasFragment &fragment);
void updateComposite();
void setComposite(const Color &color);
private:
int count;

View file

@ -0,0 +1,36 @@
#include "CanvasPixelShader.h"
#include "Color.h"
#include "SoftwareCanvasRenderer.h"
#include "CanvasPortion.h"
#include "CanvasPixel.h"
#include "CanvasFragment.h"
#include "Rasterizer.h"
CanvasPixelShader::CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int chunks_x, int chunks_y):
renderer(renderer), portion(portion), chunk_size(chunk_size), chunks_x(chunks_x), chunks_y(chunks_y)
{
}
int CanvasPixelShader::processParallelUnit(int unit)
{
// Locate the chunk we work on
int chunk_x = unit % chunks_x;
int chunk_y = unit / chunks_x;
// Resolve the pixel color
int x = chunk_x * chunk_size;
int y = chunk_y * chunk_size;
const CanvasPixel &pixel = portion->at(x, y);
int n = pixel.getFragmentCount();
Color composite = COLOR_BLACK;
for (int i = 0; i < n; i++)
{
const CanvasFragment &fragment = pixel.getFragment(i);
const Rasterizer &rasterizer = renderer.getRasterizer(fragment.getClient());
composite.mask(rasterizer.shadeFragment(fragment));
}
portion->setColor(x, y, composite);
return 0;
}

View file

@ -0,0 +1,36 @@
#ifndef CANVASPIXELSHADER_H
#define CANVASPIXELSHADER_H
#include "software_global.h"
#include "ParallelWorker.h"
namespace paysages {
namespace software {
/**
* @brief Parallel worker that can work on canvas portion to resolve pixel colors.
*
* This is used after the rasterization phase to compute pixel colors from the fragments stored in them.
*
* This worker will be set to work on a given chunk of a canvas portion.
*/
class CanvasPixelShader: public ParallelWorker
{
public:
CanvasPixelShader(const SoftwareCanvasRenderer &renderer, CanvasPortion *portion, int chunk_size, int chunks_x, int chunks_y);
virtual int processParallelUnit(int unit);
private:
const SoftwareCanvasRenderer &renderer;
CanvasPortion *portion;
int chunk_size;
int chunks_x;
int chunks_y;
};
}
}
#endif // CANVASPIXELSHADER_H

View file

@ -69,3 +69,25 @@ void CanvasPortion::pushFragment(int x, int y, const CanvasFragment &fragment)
preview->pushPixel(x, y, old_color, pixel.getComposite());
}
}
const CanvasPixel &CanvasPortion::at(int x, int y)
{
CHECK_COORDINATES();
return pixels[y * width + x];
}
void CanvasPortion::setColor(int x, int y, const Color &color)
{
CHECK_COORDINATES();
CanvasPixel &pixel = pixels[y * width + x];
Color old_color = pixel.getComposite();
pixel.setComposite(color);
if (preview)
{
preview->pushPixel(x, y, old_color, pixel.getComposite());
}
}

View file

@ -7,7 +7,7 @@ namespace paysages {
namespace software {
/**
* @brief Rectangular portion of a Canvas.
* Rectangular portion of a Canvas.
*
* Contains the pixels of a canvas region (CanvasPixel).
*/
@ -26,12 +26,26 @@ public:
void setSize(int width, int height);
/**
* @brief Add a fragment to the pixel located at (x, y).
* Add a fragment to the pixel located at (x, y).
*
* Checking x and y coordinates to be in the canvas portion should be done before this call.
*/
void pushFragment(int x, int y, const CanvasFragment &fragment);
/**
* Get the CanvasPixel at a given coordinates.
*
* Checking x and y coordinates to be in the canvas portion should be done before this call.
*/
const CanvasPixel &at(int x, int y);
/**
* Change the final color of the pixel.
*
* Checking x and y coordinates to be in the canvas portion should be done before this call.
*/
void setColor(int x, int y, const Color &color);
private:
int width;
int height;

View file

@ -136,10 +136,6 @@ void Rasterizer::pushDisplacedQuad(CanvasPortion *canvas, const Vector3 &v1, con
pushDisplacedTriangle(canvas, v4, v1, v3, ov4, ov1, ov3);
}
void Rasterizer::rasterizeToCanvas(CanvasPortion *)
{
}
void Rasterizer::scanGetDiff(ScanPoint* v1, ScanPoint* v2, ScanPoint* result)
{
result->pixel.x = v2->pixel.x - v1->pixel.x;
@ -316,25 +312,35 @@ void Rasterizer::renderScanLines(CanvasPortion *canvas, RenderScanlines* scanlin
current.x = x;
for (cury = starty; cury <= endy; cury++)
{
fy = (double)cury + 0.5;
if (fy < down.pixel.y)
if (dy == 0)
{
fy = down.pixel.y;
// Down and up are the same
current = down;
}
else if (fy > up.pixel.y)
else
{
fy = up.pixel.y;
}
fy = fy - down.pixel.y;
fy = (double)cury + 0.5;
if (fy < down.pixel.y)
{
fy = down.pixel.y;
}
else if (fy > up.pixel.y)
{
fy = up.pixel.y;
}
fy = fy - down.pixel.y;
current.y = cury;
scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, &current);
current.y = cury;
scanInterpolate(renderer->render_camera, &down, &diff, fy / dy, &current);
}
CanvasFragment fragment(current.pixel.z, Vector3(current.location.x, current.location.y, current.location.z), current.client);
Color frag_color = *color;
if (cury == starty || cury == endy)
{
frag_color.mask(Color(0.0, 0.0, 0.0, 0.3));
}
fragment.setColor(frag_color);
canvas->pushFragment(current.x, current.y, fragment);

View file

@ -20,7 +20,8 @@ public:
inline SoftwareRenderer *getRenderer() const {return renderer;}
virtual void rasterizeToCanvas(CanvasPortion* canvas);
virtual void rasterizeToCanvas(CanvasPortion* canvas) = 0;
virtual Color shadeFragment(const CanvasFragment &fragment) const = 0;
protected:
void pushProjectedTriangle(CanvasPortion *canvas, const Vector3 &pixel1, const Vector3 &pixel2, const Vector3 &pixel3, const Vector3 &location1, const Vector3 &location2, const Vector3 &location3);

View file

@ -7,6 +7,7 @@
#include "AtmosphereResult.h"
#include "CloudsRenderer.h"
#include "Rasterizer.h"
#include "CanvasFragment.h"
#define SPHERE_SIZE 20000.0
@ -15,21 +16,6 @@ SkyRasterizer::SkyRasterizer(SoftwareRenderer* renderer, int client_id):
{
}
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
{
Vector3 camera_location, direction;
Color result;
camera_location = renderer->getCameraLocation(location);
direction = location.sub(camera_location);
/* TODO Don't compute result->color if it's fully covered by clouds */
result = renderer->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
return result;
}
void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
{
int res_i, res_j;
@ -84,3 +70,19 @@ void SkyRasterizer::rasterizeToCanvas(CanvasPortion* canvas)
}
}
}
Color SkyRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
Vector3 location = fragment.getLocation();
Vector3 camera_location, direction;
Color result;
camera_location = renderer->getCameraLocation(location);
direction = location.sub(camera_location);
/* TODO Don't compute result->color if it's fully covered by clouds */
result = renderer->getAtmosphereRenderer()->getSkyColor(direction.normalize()).final;
result = renderer->getCloudsRenderer()->getColor(camera_location, camera_location.add(direction.scale(10.0)), result);
return result;
}

View file

@ -14,6 +14,7 @@ public:
SkyRasterizer(SoftwareRenderer* renderer, int client_id);
virtual void rasterizeToCanvas(CanvasPortion* canvas);
virtual Color shadeFragment(const CanvasFragment &fragment) const;
};
}

View file

@ -7,6 +7,9 @@
#include "WaterRasterizer.h"
#include "SkyRasterizer.h"
#include "CameraDefinition.h"
#include "ParallelWork.h"
#include "CanvasPortion.h"
#include "CanvasPixelShader.h"
SoftwareCanvasRenderer::SoftwareCanvasRenderer()
{
@ -58,6 +61,11 @@ const std::vector<Rasterizer *> &SoftwareCanvasRenderer::getRasterizers() const
return rasterizers;
}
const Rasterizer &SoftwareCanvasRenderer::getRasterizer(int client_id) const
{
return *(getRasterizers()[client_id]);
}
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion, bool threaded)
{
for (auto &rasterizer:getRasterizers())
@ -68,5 +76,14 @@ void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion, bool threaded)
void SoftwareCanvasRenderer::postProcess(CanvasPortion *portion, bool threaded)
{
// TODO
// Subdivide in chunks
int chunk_size = 32;
int chunks_x = portion->getWidth() / chunk_size + 1;
int chunks_y = portion->getHeight() / chunk_size + 1;
int units = chunks_x * chunks_y;
// Render chunks in parallel
CanvasPixelShader shader(*this, portion, chunk_size, chunks_x, chunks_y);
ParallelWork work(&shader, units);
work.perform();
}

View file

@ -36,11 +36,16 @@ public:
*/
void render();
/*!
* \brief Get the list of objects that can be rasterized to polygons on a canvas.
/**
* @brief Get the list of objects that can be rasterized to polygons on a canvas.
*/
virtual const std::vector<Rasterizer*> &getRasterizers() const;
/**
* Get a rasterizer by its client id.
*/
const Rasterizer &getRasterizer(int client_id) const;
protected:
/**
* @brief Rasterize the scenery into a canvas portion.

View file

@ -9,6 +9,7 @@
#include "Scenery.h"
#include "ParallelQueue.h"
#include "CanvasPortion.h"
#include "CanvasFragment.h"
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer, int client_id):
Rasterizer(renderer, client_id, Color(0.5, 0.3, 0.3))
@ -20,12 +21,6 @@ static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z)
return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, 1), z);
}
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &point, void*)
{
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
}
void TerrainRasterizer::tessellateChunk(CanvasPortion* canvas, TerrainChunkInfo* chunk, int detail)
{
if (detail < 1)
@ -245,3 +240,10 @@ void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
queue->wait();
}
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
Vector3 point = fragment.getLocation();
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
}

View file

@ -51,6 +51,7 @@ public:
virtual void rasterize();
virtual void rasterizeToCanvas(CanvasPortion* canvas);
virtual Color shadeFragment(const CanvasFragment &fragment) const;
private:
ParallelQueue* queue;

View file

@ -3,17 +3,13 @@
#include "SoftwareRenderer.h"
#include "WaterRenderer.h"
#include "ParallelQueue.h"
#include "CanvasFragment.h"
WaterRasterizer::WaterRasterizer(SoftwareRenderer* renderer, int client_id):
Rasterizer(renderer, client_id, Color(0.1, 0.3, 0.6))
{
}
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &location, void*)
{
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
}
static inline Vector3 _getFirstPassVertex(SoftwareRenderer* renderer, double x, double z)
{
Vector3 result;
@ -84,3 +80,9 @@ void WaterRasterizer::rasterizeToCanvas(CanvasPortion *canvas)
radius_ext += chunk_size;
}
}
Color WaterRasterizer::shadeFragment(const CanvasFragment &fragment) const
{
Vector3 location = fragment.getLocation();
return renderer->getWaterRenderer()->getResult(location.x, location.z).final;
}

View file

@ -16,6 +16,7 @@ public:
void rasterizeQuad(CanvasPortion* canvas, double x, double z, double size);
virtual void rasterizeToCanvas(CanvasPortion* canvas);
virtual Color shadeFragment(const CanvasFragment &fragment) const;
};
}

View file

@ -45,7 +45,8 @@ SOURCES += SoftwareRenderer.cpp \
Rasterizer.cpp \
CanvasLiveClient.cpp \
CanvasPreview.cpp \
RenderConfig.cpp
RenderConfig.cpp \
CanvasPixelShader.cpp
HEADERS += SoftwareRenderer.h\
software_global.h \
@ -80,7 +81,8 @@ HEADERS += SoftwareRenderer.h\
Rasterizer.h \
CanvasLiveClient.h \
CanvasPreview.h \
RenderConfig.h
RenderConfig.h \
CanvasPixelShader.h
unix:!symbian {
maemo5 {

View file

@ -53,6 +53,7 @@ namespace software {
class CanvasFragment;
class CanvasLiveClient;
class CanvasPreview;
class CanvasPixelShader;
}
}

View file

@ -2,37 +2,72 @@
#include "Thread.h"
#include "System.h"
#include "ParallelWorker.h"
#include <cassert>
/**
* Compatibility class for code that uses ParallelUnitFunction.
*/
class ParallelWorkerCompat:public ParallelWorker
{
public:
ParallelWorkerCompat(ParallelWork *work, ParallelWork::ParallelUnitFunction func, void* data):
ParallelWorker(), work(work), func(func), data(data)
{
}
virtual int processParallelUnit(int unit)
{
return func(work, unit, data);
}
private:
ParallelWork* work;
ParallelWork::ParallelUnitFunction func;
void* data;
};
ParallelWork::ParallelWork(ParallelWorker *worker, int units)
{
this->units = units;
this->running = 0;
this->worker = worker;
this->worker_compat = false;
}
ParallelWork::ParallelWork(ParallelUnitFunction func, int units, void* data)
{
this->units = units;
this->running = 0;
this->unit_function = func;
this->data = data;
this->worker = new ParallelWorkerCompat(this, func, data);
this->worker_compat = true;
}
ParallelWork::~ParallelWork()
{
assert(not running);
if (worker_compat)
{
delete worker;
}
}
static void* _workerThreadCallback(ParallelWork::ParallelWorker* worker)
static void* _workerThreadCallback(ParallelWork::ParallelWorkerThread* thread)
{
worker->result = worker->work->unit_function(worker->work, worker->unit, worker->work->data);
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_DONE;
thread->result = thread->worker->processParallelUnit(thread->unit);
thread->status = ParallelWork::PARALLEL_WORKER_STATUS_DONE;
return NULL;
}
static int _runNextWorker(ParallelWork::ParallelWorker workers[], int worker_count, int unit)
static int _runNextWorker(ParallelWork::ParallelWorkerThread threads[], int thread_count, int unit)
{
int i;
while (1)
{
for (i = 0; i < worker_count; i++)
for (i = 0; i < thread_count; i++)
{
ParallelWork::ParallelWorker* worker = workers + i;
ParallelWork::ParallelWorkerThread* worker = threads + i;
if (worker->status == ParallelWork::PARALLEL_WORKER_STATUS_VOID)
{
worker->status = ParallelWork::PARALLEL_WORKER_STATUS_RUNNING;
@ -62,47 +97,47 @@ static int _runNextWorker(ParallelWork::ParallelWorker workers[], int worker_cou
}
}
int ParallelWork::perform(int nbworkers)
int ParallelWork::perform(int thread_count)
{
int i, done, result;
assert(not running);
result = 0;
if (nbworkers <= 0)
if (thread_count <= 0)
{
nbworkers = System::getCoreCount();
thread_count = System::getCoreCount();
}
if (nbworkers > PARALLEL_MAX_THREADS)
if (thread_count > PARALLEL_MAX_THREADS)
{
nbworkers = PARALLEL_MAX_THREADS;
thread_count = PARALLEL_MAX_THREADS;
}
running = 1;
/* Init workers */
for (i = 0; i < nbworkers; i++)
for (i = 0; i < thread_count; i++)
{
workers[i].status = PARALLEL_WORKER_STATUS_VOID;
workers[i].work = this;
threads[i].status = PARALLEL_WORKER_STATUS_VOID;
threads[i].worker = worker;
}
/* Perform run */
for (done = 0; done < units; done++)
{
if (_runNextWorker(workers, nbworkers, done))
if (_runNextWorker(threads, thread_count, done))
{
result++;
}
}
/* Wait and clean up workers */
for (i = 0; i < nbworkers; i++)
for (i = 0; i < thread_count; i++)
{
if (workers[i].status != PARALLEL_WORKER_STATUS_VOID)
if (threads[i].status != PARALLEL_WORKER_STATUS_VOID)
{
workers[i].thread->join();
delete workers[i].thread;
if (workers[i].result)
threads[i].thread->join();
delete threads[i].thread;
if (threads[i].result)
{
result++;
}

View file

@ -23,22 +23,24 @@ public:
typedef struct
{
Thread* thread;
ParallelWork* work;
ParallelWorker* worker;
ParallelWorkerStatus status;
int unit;
int result;
} ParallelWorker;
} ParallelWorkerThread;
public:
/**
* Create a parallel work handler.
*
* This will spawn an optimal number of threads to process a given number of work units.
* This will spawn a number of threads.
*/
ParallelWork(ParallelWorker *worker, int units);
/**
* Create a parallel work handler.
*
* @param func The callback that will be called from threads to process one unit.
* @param units Number of units to handle.
* @param data Custom data that will be passed to the callback.
* @return The newly allocated handler.
* This is a compatibility constructor for older code, use the constructor with ParallelWorker instead.
*/
ParallelWork(ParallelUnitFunction func, int units, void* data);
@ -52,15 +54,15 @@ public:
/**
* Start working on the units.
*
* @param workers Number of threads to spaws, -1 for an optimal number.
* @param threads Number of threads to spaws, -1 for an optimal number.
*/
int perform(int workers=-1);
int perform(int thread_count=-1);
int units;
int running;
ParallelUnitFunction unit_function;
ParallelWorker workers[PARALLEL_MAX_THREADS];
void* data;
ParallelWorker *worker;
bool worker_compat;
ParallelWorkerThread threads[PARALLEL_MAX_THREADS];
};
}

View file

@ -0,0 +1,9 @@
#include "ParallelWorker.h"
ParallelWorker::ParallelWorker()
{
}
ParallelWorker::~ParallelWorker()
{
}

View file

@ -0,0 +1,29 @@
#ifndef PARALLELWORKER_H
#define PARALLELWORKER_H
#include "system_global.h"
namespace paysages {
namespace system {
/**
* @brief Worker that can be used by the ParallelWork object to perform tasks in several threads.
*/
class SYSTEMSHARED_EXPORT ParallelWorker
{
public:
ParallelWorker();
virtual ~ParallelWorker();
/**
* Abstract method to reimplement to process a work unit.
*
* This method will be called from any thread in the thread pool used by the ParallelWork.
*/
virtual int processParallelUnit(int unit) = 0;
};
}
}
#endif // PARALLELWORKER_H

View file

@ -25,7 +25,8 @@ SOURCES += \
CacheFile.cpp \
PictureWriter.cpp \
Logs.cpp \
ParallelPool.cpp
ParallelPool.cpp \
ParallelWorker.cpp
HEADERS += \
system_global.h \
@ -40,7 +41,8 @@ HEADERS += \
CacheFile.h \
PictureWriter.h \
Logs.h \
ParallelPool.h
ParallelPool.h \
ParallelWorker.h
unix:!symbian {
maemo5 {

View file

@ -18,6 +18,7 @@ namespace system {
class ParallelQueue;
class ParallelWork;
class ParallelPool;
class ParallelWorker;
class Thread;
class Mutex;
}