Here i would like to talk about such IGPUProgrammingServices members as:
As you may see its going crazy with number of arguments.addHighLevelShaderMaterial(...),
addHighLevelShaderMaterialFromFiles(...)
Now first one looks like:
Code: Select all
virtual s32 addHighLevelShaderMaterial(
const c8* vertexShaderProgram,
const c8* vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8* pixelShaderProgram,
const c8* pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
const c8* geometryShaderProgram,
const c8* geometryShaderEntryPointName = "main",
E_GEOMETRY_SHADER_TYPE gsCompileTarget = EGST_GS_4_0,
scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP,
u32 verticesOut = 0,
IShaderConstantSetCallBack* callback = 0,
E_MATERIAL_TYPE baseMaterial = video::EMT_SOLID,
s32 userData = 0,
E_GPU_SHADING_LANGUAGE shadingLang = EGSL_DEFAULT) = 0;
and yes, we have default values (all over the place), so our call can be very short indeed;
But i still think that this would be nice to change a bit; to change it in the way to make it easily extensible in future.
I would like to suggest to think about some kind optimization (not about execution speed, but overall look and feel... and future easy to add more arguments).
First) of all i suggest to make class that will encapsulate all ways how we can get the source of the shader program (it is a direct text string, a path to a file, a pointer to IReadFile; maybe others -- anyway, we can easy extend this single class for that).
Secondly), we need classes for each shader type (like vertex, pixel and geometry); we must eliminate code repetition, so we make kind of base class to store common data that has all shader types, and only then we inherit it to specify some single-shader-type related data (for example: arguments inType, outType and verticesOut related to geometry shader).
Thirdly), we need some top class which will hold the info about all three shader types and info related to the material (this class is not so critical, but just places all things to the same approach; so the aim to make some kind of addHighLevelShaderMaterial() which will take single argument which is highly and easily parameterized).
So next code shows how i think this should be done:
(please note, this code compiles but not works, because it is assumed to be put into IGPUProgrammingServices.h)
(watch outside on http://pastebin.com/DrVDnP76)
Code: Select all
namespace irr {
namespace video {
// Describes source of the shader (e.g. text string, name of the text file, pointer to IReadFile).
class GPUShaderSource
{
public:
GPUShaderSource(const c8* sourceString)
: source(0)
{
if (sourceString && sourceString[0])
{
size_t s = strlen(sourceString);
source = new c8[s + 1];
strcpy(source, sourceString);
}
}
GPUShaderSource(io::IReadFile* sourceFile)
: source(0)
{
source = getFileContent(sourceFile);
}
GPUShaderSource(const io::path sourcePath)
: source(0)
{
io::IReadFile* f = 0; // FIXME: cannot use here internal function call "io::createReadFile(sourcePath);"
if (f)
{
source = getFileContent(f);
f->drop();
}
}
~GPUShaderSource()
{
if (source)
delete [] source;
}
inline c8* getSource() const
{
return source;
}
inline bool isValid() const
{
return source != 0;
}
private:
c8* getFileContent(io::IReadFile* f)
{
if (f)
{
const long s = f->getSize();
if (s > 0)
{
c8* c = new c8[s + 1];
f->read(c, s);
c[s] = 0;
return c;
}
}
return 0;
}
c8* source;
};
// Describes particular shader. This is base class to all shader types, and it contains all common data.
template <class T_CompileTarget, T_CompileTarget T_CompileTargetDefaultValue>
class GPUShaderInfo
{
public:
GPUShaderInfo(const GPUShaderSource& source = GPUShaderSource((c8*)0), const c8* entryPoint = "main",
T_CompileTarget compileTarget = T_CompileTargetDefaultValue)
: sourceCode(0)
, entryPoint(0)
, compileTarget(compileTarget)
{
if (source.isValid())
{
size_t s = strlen(source.getSource());
sourceCode = new c8[s + 1];
strcpy(sourceCode, source.getSource());
}
if (entryPoint && entryPoint[0])
{
size_t s = strlen(entryPoint);
this->entryPoint = new c8[s + 1];
strcpy(this->entryPoint, entryPoint);
}
}
~GPUShaderInfo()
{
if (sourceCode)
delete [] sourceCode;
if (entryPoint)
delete [] entryPoint;
}
inline bool isValid() const
{
return sourceCode && entryPoint;
}
inline c8* getSourceCode() const
{
return sourceCode;
}
inline c8* getEntryPoint() const
{
return entryPoint;
}
inline T_CompileTarget getCompileTarget() const
{
return compileTarget;
}
private:
c8* sourceCode;
c8* entryPoint;
T_CompileTarget compileTarget;
};
typedef GPUShaderInfo<E_VERTEX_SHADER_TYPE, EVST_VS_1_1> GPUVertexShaderInfo;
typedef GPUShaderInfo<E_PIXEL_SHADER_TYPE, EPST_PS_1_1> GPUPixelShaderInfo;
// Vertex shader and pixel shader have very similar structure, that is why we have used template, but if they would go different in future,
// we can do something similar to what we are doing bottom (with geometry shader) - and we will not break old code which uses it.
class GPUGeometryShaderInfo : public GPUShaderInfo<E_GEOMETRY_SHADER_TYPE, EGST_GS_4_0>
{
public:
GPUGeometryShaderInfo(const GPUShaderSource& source = GPUShaderSource((c8*)0), const c8* entryPoint = "main",
E_GEOMETRY_SHADER_TYPE compileTarget = EGST_GS_4_0, scene::E_PRIMITIVE_TYPE inType = scene::EPT_TRIANGLES,
scene::E_PRIMITIVE_TYPE outType = scene::EPT_TRIANGLE_STRIP, u32 outVertexCount = 0)
: GPUShaderInfo<E_GEOMETRY_SHADER_TYPE, EGST_GS_4_0>(source, entryPoint, compileTarget)
, inputPrimitiveType(inType)
, outputPrimitiveType(outType)
, outputMaxVertexCount(outVertexCount)
{
}
inline scene::E_PRIMITIVE_TYPE getInputPrimitiveType() const
{
return inputPrimitiveType;
}
inline scene::E_PRIMITIVE_TYPE getOutputPrimitiveType() const
{
return outputPrimitiveType;
}
inline u32 getOutputMaxVertexCount() const
{
return outputMaxVertexCount;
}
private:
scene::E_PRIMITIVE_TYPE inputPrimitiveType;
scene::E_PRIMITIVE_TYPE outputPrimitiveType;
u32 outputMaxVertexCount;
};
// Describes shader material. Contains all necessary data to call IGPUProgrammingServices::addHighLevelShaderMaterial().
class GPUShaderMaterialInfo
{
public:
GPUShaderMaterialInfo(const GPUVertexShaderInfo& vertexShader, const GPUPixelShaderInfo& pixelShader = GPUPixelShaderInfo((c8*)0),
const GPUGeometryShaderInfo& geometryShader = GPUGeometryShaderInfo((c8*)0), IShaderConstantSetCallBack* constantSetCallBack = 0,
E_MATERIAL_TYPE baseMaterial = EMT_SOLID, s32 userData = 0, E_GPU_SHADING_LANGUAGE shadingLanguage = EGSL_DEFAULT)
: vertexShader(vertexShader)
, pixelShader(pixelShader)
, geometryShader(geometryShader)
, constantSetCallBack(constantSetCallBack)
, baseMaterial(baseMaterial)
, userData(userData)
, shadingLanguage(shadingLanguage)
{
}
inline bool isValid() const
{
// we assume that whole the material is valid, if any shader is valid
return vertexShader.isValid() || pixelShader.isValid() || geometryShader.isValid();
}
inline const GPUVertexShaderInfo& getVertexShader() const
{
return vertexShader;
}
inline const GPUPixelShaderInfo& getPixelShader() const
{
return pixelShader;
}
inline const GPUGeometryShaderInfo& getGeometryShader() const
{
return geometryShader;
}
inline IShaderConstantSetCallBack* getConstantSetCallBack() const
{
return constantSetCallBack;
}
inline E_MATERIAL_TYPE getBaseMaterial() const
{
return baseMaterial;
}
inline u32 getUserData() const
{
return userData;
}
inline E_GPU_SHADING_LANGUAGE getShadingLanguage() const
{
return shadingLanguage;
}
private:
GPUVertexShaderInfo vertexShader;
GPUPixelShaderInfo pixelShader;
GPUGeometryShaderInfo geometryShader;
IShaderConstantSetCallBack* constantSetCallBack;
E_MATERIAL_TYPE baseMaterial;
s32 userData;
E_GPU_SHADING_LANGUAGE shadingLanguage;
};
// this is hypotetic implementation of addHighLevelShaderMaterial()
// please note: it crashes since thisGPU pointer is 0.
s32 addHighLevelShaderMaterial(const GPUShaderMaterialInfo& materialInfo)
{
if (!materialInfo.isValid())
return -1;
const GPUVertexShaderInfo& vs = materialInfo.getVertexShader();
const GPUPixelShaderInfo& ps = materialInfo.getPixelShader();
const GPUGeometryShaderInfo& gs = materialInfo.getGeometryShader();
IGPUProgrammingServices* thisGPU = 0; // FIXME: lets assume we are defined inside IGPUProgrammingServices
// then addHighLevelShaderMaterial() from Irrlicht core would look like:
// NEXT LINE LEADS TO CRASH
return thisGPU->addHighLevelShaderMaterial(
vs.isValid() ? vs.getSourceCode() : (const c8*)0,
vs.isValid() ? vs.getEntryPoint() : (const c8*)0, // please note, we don't need to duplicate default value "main", we already defined it once in proper constructor (same for all other "(type)0" expressions)
vs.isValid() ? vs.getCompileTarget() : (E_VERTEX_SHADER_TYPE)0,
ps.isValid() ? ps.getSourceCode() : (const c8*)0,
ps.isValid() ? ps.getEntryPoint() : (const c8*)0,
ps.isValid() ? ps.getCompileTarget() : (E_PIXEL_SHADER_TYPE)0,
gs.isValid() ? gs.getSourceCode() : (const c8*)0,
gs.isValid() ? gs.getEntryPoint() : (const c8*)0,
gs.isValid() ? gs.getCompileTarget() : (E_GEOMETRY_SHADER_TYPE)0,
gs.isValid() ? gs.getInputPrimitiveType() : (scene::E_PRIMITIVE_TYPE)0,
gs.isValid() ? gs.getOutputPrimitiveType() : (scene::E_PRIMITIVE_TYPE)0,
gs.isValid() ? gs.getOutputMaxVertexCount() : (u32)0,
materialInfo.getConstantSetCallBack(),
materialInfo.getBaseMaterial(),
materialInfo.getUserData(),
materialInfo.getShadingLanguage()); // here "shadingLang" argument:
// i don't know actually the truth so it should be clarifyed:
// now i assume that each material defines language type for all shaders in it -- if it is not correct,
// that "shadingLanguage" should be simply moved from GPUShaderMaterialInfo to GPUShaderInfo class
}
} // end of video
} // end of irr
Code: Select all
void main()
{
// this way we add new shader material;
// please note, we do not need overloads with "FromFiles" suffix, sources can be extended via extending GPUShaderSource (check its constructors to see how actually now it can be used)
// just a test call, in real case here should be "device->getVideoDriver()->getGPUProgrammingServices()->addHighLevelShaderMaterial(...)"
s32 m = video::addHighLevelShaderMaterial(video::GPUShaderMaterialInfo(
video::GPUVertexShaderInfo("some shader program code goes here...", "main", video::EVST_VS_3_0),
video::GPUPixelShaderInfo(io::path("shaders/frag-777.hlsl")),
video::GPUGeometryShaderInfo(), // <-- this way we simply set "null" value (meaning don't use this particular shader)
0,
video::EMT_SOLID,
0,
video::EGSL_CG
));
// ------------------------------------------------------------------
What do you think about it?