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.

quinta-feira, 20 de junho de 2013

Porting the postprocessing code

Porting the posteffect code from Crystal Space soc2011 branch to the newest(soc2013/postprocessing), wasn't too hard as I expected.
In the porting process I learned a bit more on how the current postprocessing code work, I found the critical parts to start the work and, of course, I found bugs too.


  • hdr is buggy, something related to frame buffers deallocation
  • Basic posteffect chaining is working
  • Removing/adding the effcts is buggy, I think it's due to the PostEffectsSupport not chaining the rendertargets correctly
  • The posteffect code doesn't yet reflect a design focused in the use of multiples effects


There are some minor bugs too, like wrongly offseted texture coordinates and pixelSize shader variable not setup correctly, for these I will commit fixes soon.

I also did a small demo to test posteffect chaining. Here is an image showing three simple posteffects chained


The next step in the work is to start improving the desing of the posteffect code to reflect the needs of the proposed effects.

segunda-feira, 17 de junho de 2013

Generating msvc projects for CrystalSpace

 Generate the msvc projects was tiring , everything that could go wrong went. Then I decided to make an entire tutorial explaining how to get things working.


First of all, download cygwin (if you are using windows) .

Installing ftjam
Then install ftjam, download it here, and copy the binary to  cygwin/bin or cygwin/usr/sbin folder.

Installing perl and Template Toolkit
Before installing it, you need to get gcc-4, if you doesn't already have it run the cygwin setup and in the devel packages select gcc-4 C/C++ package.
If you are using cygwin, also install mingw gcc C/C++ compiler or you will get a error when running ./configure script, because gcc-4 doesn't support a flag used by the configure script.

To install Template Toolkit go to the  cygwin/linux terminal:

# cpan install Template

Or the hardest way, download ActivePerl here, go to the activeperl/bin folder and run cpan.bat, and then in the prompt run the command:

# install Template

Run the configure script

# ./configure

Notice that if your  CS svn repository is configured to use CRLF windows line endings these scripts will fail to run, therefore change the line endings to LF.

Before running jam you will need to run (only once, to create a configuration file):

# ttree

it will asks you to create a configuration file, confirm and now you are ready to run:

# jam msvcgen

If you don't run the ttree before using jam, it will fail because jam uses ttree and ttree will asks to create the configuration file without execute the command passed by jam.