Is this bad coding ?
Okay let me first explain what I am trying to solve. I am working on a
multi-threading 3D renderer. Each thread renders a block of pixels.
For each pixel the thread shoots a ray and find a possible
intersection with the geometry. If there's an intersection, we
evaluate the color at that intersection point (lets call it Cs). That
requires to loop over the lights in the scene and mix that result with
the surface color at the intersection point. There's a class called
Shader whose work is to offer all the needed functions to compute Cs.
A pointer to an instance of the class Shader is a member variable of
the class Object so we can do this when we have an intersection:
class Shader
{
color shade(...);
};
class Object
{
Shader *shader;
};
pixelColor = objIntersected->shade(...);
Here is the problem. Lets imagine all the threads render the same
object. Therefore they will access 'shader' potentially at the same
time. Which means that if the threads need to change anything in
'shader' before it calls 'shade()' the application won't be thread
safe;
class Shader
{
float param;
color shade(...) { color c = 1; return param * c; }
}
objIntersect->param = threadId; ///< NOT THREAD SAFE
pixelColor = objIntersect->shade(); ///< NOT THREAD SAFE because by
the time shade() is executed
///< param may have
been changed by another thread !!!!
So basically the only workaround that I can see is to dynamically
allocate an instance of the class Shader each time there's an
intersection but that forces me to do a new/delete potentially a few
hundred millions times (yes that many times depending of the geometry/
shading complexity).
if (intersection) {
Shader *shd = new Shader();
shd->Kd = threadId;
pixelColor = shd->shade();
delete shd;
}
YES I KNOW I can pass threadId to the shade function as parameter but
lets say I CAN'T (because the code is in fact more complicated and
that I simple can not doit it;-).
So I am trying to find a solution to this problem. I tried to
prototype something very quickly and I came with this (see code
below). But I am afraid IT'S UGLY from a coding point of view. Because
I am passing the parameters of the shader (packed in a structure) as a
void pointer to a function and passing them back to the shader as a
void pointer and casting them to their original type. That sounds bad
hey ! But I timed that application and it's much much faster. Is it
really bad or acceptable ? Does anyone see a better way of doing
this ?
Thanks for any feedbacks or suggestion -coralie
[CODE]
#include <iostream>
#include <vector>
class Light
{
public:
Light() {}
};
typedef struct RenderContext
{
std::vector<Light *> lights;
};
class ShaderParameters
{
public:
};
typedef struct color { float r, g, b; };
typedef struct stCoordinate { float s, t; };
typedef struct ShadingInfo
{
stCoordinate st;
};
class Shader
{
public:
Shader() {}
virtual ~Shader() {}
virtual color surface(const ShadingInfo &) const = 0;
virtual void evaluate(const void *) const = 0;
};
color illuminate(const RenderContext *, const Shader *shd, const void
*);
class Plastic : public Shader
{
class PlasticShaderParams : public ShaderParameters
{
public:
float Kd, Ks;
};
const RenderContext *rc;
public:
Plastic(const RenderContext *rctx) : rc(rctx) {}
color surface(const ShadingInfo &shadingInfo) const
{
color c = { 0, 0, 0 };
PlasticShaderParams shdParams;
shdParams.Ks = .4f;
shdParams.Kd = .6f;
illuminate(rc, this, &shdParams);
return c;
}
void evaluate(const void *params) const
{
PlasticShaderParams *shadParams = (PlasticShaderParams*)params;
//fprintf(stderr, "Kd : %f\n", shadParams->Kd);
}
};
color illuminate(const RenderContext *rc, const Shader *shd, const
void *shdParams)
{
color c = { 0, 0, 0 };
shd->evaluate(shdParams);
return c;
}
class Object
{
public:
const Shader *shader;
Object(const Shader *shd) : shader(shd) {}
};
int main (int argc, char * const argv[])
{
// create
RenderContext *rc = new RenderContext;
rc->lights.push_back(new Light());
rc->lights.push_back(new Light());
Shader *plastic = new Plastic(rc);
Object *obj = new Object(plastic);
ShadingInfo shadingInfo;
// do
float t0 = clock();
for (int i = 0; i < 10e6; ++i) {
shadingInfo.st.s = .4;
shadingInfo.st.t = .6;
color c = obj->shader->surface(shadingInfo);
}
fprintf(stderr, "%f\n", (clock()-t0)/(float)CLOCKS_PER_SEC);
// delete
delete obj;
delete plastic;
while (!rc->lights.empty()) {
Light *l = rc->lights.back();
rc->lights.pop_back();
delete l;
}
delete rc;
return 0;
}
[\CODE]
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]