On Wed, 15 Aug 2001, Eric Faurot wrote:
> The different things to consider when it comes to rendering a
> primitive could be classified as follow:
> 
>  - The destinations : where the actual operation occurs. It can be
>    one or more of frame/alpha/z/stencil/.../buffer
> 
>  - The sources : what is intented to bo written. It can be a mix of
>    color, alpha or z given as single values or texture. (or a ROP?)
> 
>  - The modifiers : Every thing that will alter the actual rendering,
>    such as z/stencil buffers, clipping area, ...

...coordinates, buffer addresses, and ROPs are also sort of in this
category.

> 
>  - The shape: line, box....

Right.  In our terms, sources and destinations are ggiBuf_t objects.
A LibBuf object can be nested to contain more than one actual buffer, e.g. 
the pixel data and the Z data.

Modifiers are what LibGAlloc calls "carbs", and there's some definitions
for them in ga_prop.h

> Thus, an alpha-aware ggiDrawBow(vis,x,y,w,h) could be described as:
> 
>    dest   : vis
>    source : color=vis->fgcolor, alpha=vis->fgalpha (or fct parameter)

fgalpha is what LibBuf refers to a value set with ggiBufSetGCForeGround
on the Buf_t representing the alpha channel of the frame, that is, it
is stored in the object representing the dest Z buffer.  The dest vis
is not your usual vis, but rather one that has been created by bonding
a normal ggi_visual to some combination of ancillary buffers by LibBuf
API functions.

>    mod    : vis->clipping_area
>    shape  : box(x,y,w,h)
> 
> The extension would internaly define this as a primtive batchop
> to be processed by the target.

Right, it would consist of a set of constant (step = 0) batchparms:

parmtype               value
GGI_PT_OPCODE          GGI_OP_DRAWBOX
GGI_PT_DESTX1           x
GGI_PT_DESTY1           y
GGI_PT_DESTWIDTH        w
GGI_PT_DESTHEIGHT       h

For ggiDrawBox, these would be constant (step = 0) batchparms.
For a ggiDrawLotsOBoxes, the coordinates and perhaps the colors
would have a step value, such that they can be read from arrays
when the batchop's go() function is invoked.

> If the target can't handle this directly, it will call call generic
> function to transform this into a batchop it can handle.
> This could be, for example, transforming the box into a serie of
> hline, 

There are two options here.  The target can overload the 
ggiDrawBox function and add more than one row to the batchop
each time the function is invoked.  Generally this would not be 
done to break boxes into hlines because that would generate 
a whole lot of rows of data.  Rather this would be the 
case when breaking into multiple stages like you also mentioned.

The second option, and the right way to break something into hlines 
if it needs to be, is to have code to call an hline function from 
inside the target's "go" function which this batchop has been 
matched to.  Of course, this particular example is one which should 
be avoided, as that's a rather inefficient way to do things.

> So we need to create batchops for lots of different things here:

Let me throw some vocabulary out, as I didn't intend the term batchop 
to become quite so fuzzy:

source batchop: A description of where to take a row of data from,
what the data in the row represents, and how to go to the next row.

dest batchop: same, but for storing data (e.g. in a command fifo), and
in addition, specifies the cronological order in which the storing 
takes place.

render batchop: A variant of a dest batchop, which instead of storing
data, invokes functions based on what the source batchop contains.

So your list of "what we need to write batchops for" can be taken 
in each of these contexts.  First, in the context of a render batchop,
we need to write code to render box, line, hline, vline, perhaps
triangles -- with various combinations of Z, alpha, ROP, stretch etc.
We should subdivide this code such that we have a range of functions
from the simple/fast to the complex/not-so-fast, but at the same time
try to limit the number of versions of a given operation in the range 
(e.g. don't write a whole new version of an operation just to save
3 cycles by not looking to see if the ROP has changed.)

Then we decide how to best arrange this code into render batchops.  
This can be a target-specific decision; what is right for one target
may not be right for others. (e.g. a library that has function call 
overhead no matter what we do, vs a direct or hw accel renderer)

Now, in the context of dest batchops:  We need to write batchops
describing hw FIFO queues, e.g.

parmtype                          
GGI_PT_OPCODE_TABLE    
GGI_PT_DESTX1           
GGI_PT_DESTY1           
GGI_PT_DESTWIDTH        
GGI_PT_DESTHEIGHT       
GGI_PT_ZSTARTX
GGI_PT_ZSLOPEY
[...lots of other batchparms, one for each register field...]

These map our ggi parmtypes to actual chipset registers for fast copy
direct to registers (in the throw-caution-to-the-wind targets), or to
pre-chewed values that a KGI driver can easily validate and send on to the
chipset.

And, along with these dest batchops, we need to write "go" functions
that handle on-the-fly translation for example of x,y,w,h to
x1,y1,x2,y2 and other such things we will encounter when going
to-the-metal.  It is unavoidable that some chipsets will be peculiar
enough that they will need their very own "go" functions, but a good
amount of them will be able to use "stock" go functions, which
will find the right registers to load data into via the data in
the dest batchop.

Finally, for source batchops: A point to note, is that API
functions are *almost* uneccessary with batchops.  They are mostly
there as a convenience for users that want a function oriented
feel.  The only time they do something that a user couldn't 
is when they are used to break things into stages on targets that
need more than one row for a certain operation (the user cannot
do this, because he has no way of knowing if he needs to or not.)  
If it were not for this fact, users could skip the API entirely 
and roll their own source batchops, match them to the targets 
dest/render batchops, and call the go function themselves.

As it is, we either hide them entirely in the case of legacy 
function oriented API's like LibGGI's core, or, as in the case of
LibBlt, we provide them with a way to get a batchop most closely
representing the ops they want to perform, and give them what
amount to macros to add operations to that batchop.  But behind
the scenes, what we are really doing in libBlt is creating a virtual
graphics card FIFO, one with the advantage of being rather incredibly
configurable.  

As people become accustomed to the LibBlt API, we have the option 
of exposing some of this configurability, allowing them to essentially
change the widths/types of the data in the batchops and do
other neat stuff.  Describing how we do that would take a while and
that's a long ways from where we are right now, though.  (For now,
we have a less efficient solution to this in the ggiBltPutBatchop
function which translates rows in a user-defined batchop into 
one of the batchops gotten from the LibBlt API.)

> It's very rough, but I'd like to know if it makes sense or not.
> The more I see this, the more I feel it looks like a scene
> oriented design, with the advantage that rendering operations
> are 'objects' of the scene, just like 'data'. 

I'd say more of a "behind-the-scene-graph" design :-).

> Isn't it what libGGI3D wanted to accomplish somehow?

I think similarities with LibGGI3D are bound to crop up, because all
roads lead to Rome in this case.

--
Brian

Reply via email to