quarta-feira, 26 de junho de 2013

Crystal Space Postprocessing Requirements & Design

The postprocessing requirementes are:
  • posteffect can have multiple passes (layers)
  • each pass can uses multiples inputs
  • the pass inputs can be others layers outputs, or custom textures
  • each pass can have multiple outputs (render targets)
  • output textures can have custom format, downsample and mipmap values
  • # of output textures should be minimal
  • output textures can be reused if not strictly specified the opposite
  • a posteffect can have default shaders parameters
  • it should be data-driven, that is, fully set up using the posteffects xml files

Implementation

As some part of these requirements has already been inherited from the previous code, then I'm doing small changes in the codebase to fit the rest of the requirements.

Texture usage optimization
I started to work in optimizing the texture usage, sharing common textures between layers. The old method (using ping-pong textures) would not work properly, imagine the case where we have 3 layers:

Layer 2 uses the output from layer 1 and layer 3 uses both outpus (1 and 2).

In such a case we clearly realize that we can't reuse the output of layer 1 until layer 3 finishes its work, the same occurs for layer 2.

From this example we conclude that:
- All inputs of layer i can't be reused until layer i is drawn
- All outputs of layer i can't be shared between themselves (obvious)

For that problem I wrote a simple algorithm that resolve the needed texture usage (pseudo code):

ResolveDependency( layers )
avail_tex : list;
used_tex : list;

for each output of last layer
    used_tex.put(output);

for i = last layer to  first layer
    //remove textures where assigned layer number > i
    // and put it on avail_tex
    updateLists(i);
    for each input of layer i
        //find if there is any texture  that match the
        //properties from the layer output referenced by input
        tex = avail_tex.find(format,mipmap,downsample);

        if tex = not_found then
            tex = CreateNewEntry(format,mipmap,downsample);

        asign tex to layer i
        used_tex.put(tex);
end

Of course it's a shallow overview of the implementation details.

I also did some minor changes in some structs related to the layer options. I will list the structs first then I explain them.


struct TextureAllocationInfo
{
    bool mipmap;
    int maxMipmap;
    int downsample;
    bool reusable;
    csString format;
    TextureAllocationInfo();
    bool operator==(const TextureAllocationInfo& other);
};
As the name sugests this struct defines the properties of the layer's outpus, and will also be used as input param by  a Texture cache that will be implemented to share textures (RT's) between posteffects.


struct PostEffectLayerOptions
{
    TextureAllocationInfo info;
    csRef<iTextureHandle> renderTarget;
    csRect targetRect;
    csString name;
    bool operator==(const PostEffectLayerOptions& other);
};
This struct defines the full output options, its name and a manual render target if desired.

enum LayerInputType { AUTO, STATIC, MANUAL };
struct PostEffectLayerInputMap
{
    LayerInputType type;
    csRef<iTextureHandle> inputTexture;
    csString sourceName;
    csString svTextureName;
    csString svTexcoordName;
    csString svPixelSizeName;
    csRect sourceRect;
    PostEffectLayerInputMap () : type (AUTO),
        svTextureName ("tex diffuse"),
        svTexcoordName ("texture coordinate 0") {}
};
The input map had small but important changes, first it now has a type that defines if it references a layer output, a texture resource or is manually seted up. The sourceName variable depending on the type can be: "layername.outputname" so that we can link layers input/output, or a path like "/data/lookuptex.png" for AUTO and STATIC type respectively. For manual textures the sourceName is unused.

For last but not least:
struct LayerDesc
{
    csArray<PostEffectLayerInputMap> inputs;
    csArray<PostEffectLayerOptions> outputs;
    csRef<iShader> layerShader;
    csString name;

    LayerDesc () {}
    LayerDesc (iShader* shader)
    LayerDesc (iShader* shader, const char * layerName)
    LayerDesc (iShader* shader, PostEffectLayerInputMap &inp)
    LayerDesc (iShader* shader, PostEffectLayerInputMap &inp, 
                   PostEffectLayerOptions &opt)
    LayerDesc (iShader* shader, 
                   csArray<PostEffectLayerInputMap> &inp,
                   PostEffectLayerOptions &opt)
    LayerDesc (iShader* shader, PostEffectLayerOptions &opt);
};
The layer descritor contain all the parameters needed to create and setup a layer, and of course, alot of constructors to easily create a layer.

The next step will be create a texture cache where all posteffects can request textures, link layers inputs to outputs and create a loader for STATIC type inputs.

Nenhum comentário:

Postar um comentário