Results 1 to 7 of 7

Thread: Those tricky arrays....

  1. #1
    Join Date
    Mar 2005
    Location
    Eskilstuna, Sweden
    Posts
    62

    Default Those tricky arrays....

    Arrays in mental ray shader programming is often a problem for budding shader wizards.

    The implementation may seem odd, but it is implemented the way it is due to all parameters to a shader should be expressable in a single contiguous memory block (the detail of the "why's" is ironed out pretty clearly in the documentation with perdy' pics and all).

    Hence, the array contains three parts:

    • - The array start point within the parameter structure (When you declare this array you always declare it of size 1)
      - The index of the start of the array
      - The size of the array


    I.e. for a simple light array, the declaration in the shader structure looks like this:

    Code:
    	int		i_light;	/* index of first light */
    	int		n_light;	/* number of lights */
    	miTag		light[1];	/* list of lights */
    To many people this may seem a bit odd. But the reason, as said, is data layout.

    For an array of one object, or an array that comes at the end of the parameter struct, the array items can really be stored in the position where the array item appears in the struct. But consider a shader with a declaration like this:

    Code:
    	int		i_light;	/* index of first light */
    	int		n_light;	/* number of lights */
    	miTag		light[1];	/* list of lights */
    	miScalar		beep; 	/* Some value */
    In the above example, one light still fits in the memory position of light[0], but since the light array is of size 1 in the actual structure, addressing the second element, light[1], would actually be addressing the "beep" parameter (with possibly dreary consequences)!!

    Hence data must be stored beyond the memory position of "beep" and the array must hence begin at a nonzero index!

    Therefor it is imperative to look at the i_.... parameters for arrays, such as our i_lights above.

    Now read this correctly:

    CORRECT: Arrays begin at index i_light and runs for n_light number of items.

    WRONG: Arrays begin at index i_light and ends at index n_light.

    WRONG: Arrays begin at index 0 and runs for n_light number of items.


    Now, how to index them? There are basically two schools of thought.

    One method is to offset your original pointer into the array with the i_... parameter, and be done with it, like this:

    Code:
    	miTag		*light;		/* tag of light instance */
    	int		i_l;		/* offset of light sources */
    	int		n_l;		/* number of light sources */
    
    	n_l   = *mi_eval_integer(&paras->n_light);
    	i_l   = *mi_eval_integer(&paras->i_light);
    	light =  mi_eval_tag(paras->light) + i_l;
    This means your lookup does not need to care about this offset stuff, and simply indexes into the lights array:

    Code:
    	for &#40;i = 0; i < n_l; i++&#41;
    	&#123;
    		my_light = light&#91;i&#93;;
    		&#46;&#46;&#46;
    The ALTERNATE method is to look at the i_... variable at lookup time. So the code would look like above except:

    Code:
    	light =  mi_eval_tag&#40;paras->light&#41;; // no i_l !!!
    And the lookup line would instead be
    Code:
    	for &#40;i = 0; i < n_l; i++&#41;
    	&#123;
    		my_light = light&#91;i + i_l&#93;; // i_l here!
    		&#46;&#46;&#46;
    However, if you accidentally mix both you are in troble. This is a mistake that is very easy to do, and you get code that has i_l in both places. This is bad, and is a good cause of possible bugs.

    Also writing lookup loops like...
    Code:
    	for &#40;i = i_l; i < i_l + n_l; i++&#41;
    	&#123;
    		my_light = light&#91;i&#93;; 
    		&#46;&#46;&#46;
    ....is discouraged because it's not very readable, and your "i" loop variable will not be going from 0 to the number, which my be handy for othed indexing, error printouts, or whatnot.



    Another thing to look out for is how one declares and mi_evals the array itself.

    It is important to note that it is an array, which can in C been seen as an equivalent to a pointer.

    Now, when one use mi_eval functions, one is quite used to type things like

    Code:
    	&#46;&#46;&#46; = *mi_eval&#40;& &#46;&#46;&#46;
    I.e. the * and & are almost second-naturedly typed in. But please note that this will be wrong for arrays. The array is already a pointer as seen from the C code, so

    WRONG:
    Code:
    	miTag *lights;
    	lights = *mi_eval&#40;&param->lights&#41;;
    CORRECT:
    Code:
    	miTag *lights;
    	lights = mi_eval&#40;param->lights&#41;;
    Because param->lights already evaluates to the pointer of the array AND you want to assign the pointer. NOT as you want with a normal mi_eval() call, assign the value.

    Of course, if one enjoys to type & it can be good to know that param->lights and &param->lights[0] are equivalent... (address of first item equals the array adress itself)

    Hope this helps anyone struggling with mental ray arrays.

    /Z
    Zap Andersson - Autodesk Rendering Architect -
    mentalraytips.blogspot.com
    --
    "Travelling parallel to a tangent just isn't... normal."

  2. #2
    Join Date
    Oct 2005
    Location
    Soho, London
    Posts
    5

    Default

    I am having issues with retrieving elements from an array declared in a userdata block.

    In the docs i read that userdata and shader params should be handled in the same manor, does the above post still apply in this situation?
    I was hoping to get the 3rd element in the array from the following code:

    //-- mi declarations --
    declare data
    "test_data" (
    array integer "my_num",
    integer "my_other_num"
    )
    end declare

    data "test" "test_data" ( my_num [11,22,33,44],
    my_other_num 50
    )

    camera "perspShape"
    data "test"
    resolution 640 480
    aspect 2.35
    aperture 1.41732
    frame 1 1.
    clip 0.1 1000.
    focal 1.37795
    end camera

    //-- shader code --
    typedef struct
    {
    miInteger passid[4];
    miInteger my_other_var;
    } camuserdata_t;

    miTag tag = state->camera->userdata;
    camuserdata_t cam_ud;
    mi_query(miQ_DATA_PARAM, miNULLTAG, tag, &cam_ud);
    fprintf(stderr, "PARAM ARRAY STRING: %i\n", cam_ud.passid[2]);

  3. #3
    Join Date
    Mar 2005
    Location
    Eskilstuna, Sweden
    Posts
    62

    Default

    Yes, all of the above applies, so your array is NOT

    {
    miInteger passid[4];
    miInteger my_other_var;
    } camuserdata_t;


    ... but it is...

    {
    miInteger i_passid;
    miInteger n_passid;
    miInteger passid[1];
    miInteger my_other_var;
    } camuserdata_t;


    ...and third item is...

    passid[i_passid+2]

    ...for this reason, it is always more efficient to have arrays at the end of parameter structs. If the array is not at the end, the array item itself is only used if array size==1, if anything else, the i_ member is set such that the array actually beings *after* the struct in memory.

    /Z
    Zap Andersson - Autodesk Rendering Architect -
    mentalraytips.blogspot.com
    --
    "Travelling parallel to a tangent just isn't... normal."

  4. #4
    Join Date
    Jul 2005
    Posts
    17

    Default

    There's something I keep wondering: If you decide to, for whatever reason, pass multiple arrays through a shader, would you get the same problem that the manual mentions, where elements from one array occupy the same address as elements of the other array?

  5. #5
    Join Date
    Mar 2005
    Location
    Eskilstuna, Sweden
    Posts
    62

    Default

    Uhm, there is no "problem": That's what the "index" parameter is for, it indexes the startpoint correctly to make sure arrays do not collide in memory.

    Iwrote one shader accepting 4 arrays... nemas problemas....

    /Z
    Zap Andersson - Autodesk Rendering Architect -
    mentalraytips.blogspot.com
    --
    "Travelling parallel to a tangent just isn't... normal."

  6. #6
    Join Date
    Dec 2004
    Location
    Marina Del Rey, California
    Posts
    4,143

    Default

    Yeah, I understand why it could be confusing. The manual explains why it would be a problem if it didn't have that i_*. That could be misunderstood. But as Zap says, if you use them correctly, have as many array inputs as you want.
    Barton Gawboy

  7. #7
    Join Date
    Jul 2005
    Posts
    17

    Default

    Ahh...whoops. Guess I did read that a bit incorrectly.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •