« Return to Thread: Rasterizer clipping

Re: Rasterizer clipping

by Petr Kobalíček :: Rate this Message:

Reply to Author | View in Thread

Hi Stephan,

your comment is really great!

I think you understand what I'm are doing. Your guess about API is
right (API like cairo to render one primitive per function call). I
solved some problems you talked about. For example I'm not using
agg::path_storage, but I'm created own Path that is not changed during
iteration and with antigrain it's used through wrapper (antigrain
architecture is really good in wrapping everythin).

For example the path wrapper looks like this:

// Wraps Fog::Path to antigrain like vertex storage.
struct FOG_HIDDEN AggPath
{
  FOG_INLINE AggPath(const Path& path)
  {
    d = path._d;
    rewind(0);
  }

  FOG_INLINE ~AggPath()
  {
  }

  FOG_INLINE void rewind(unsigned index)
  {
    vCur = d->data + index;
    vEnd = d->data + d->length;
  }

  FOG_INLINE unsigned vertex(double* x, double* y)
  {
    if (vCur == vEnd) return Path::CmdStop;

    *x = vCur->x;
    *y = vCur->y;

    uint command = vCur->cmd.cmd();
    vCur++;
    return command;
  }

private:
  const Path::Data* d;
  const Path::Vertex* vCur;
  const Path::Vertex* vEnd;
};

It's nothing special, but allows to share path with multiple threads
without problems and removes "changing" problem during iteration. The
Fog API uses implicit sharing and copy-on-write, so I can share one
path or image (or pattern, ...) in multiple threads without copying
them.

So this problem is solved ;-)

--- Ideas...

While implementing rendering across multiple threads I'm considering
this idea (nothing finished, just idea). You have painter (vector
context) and painter operations are not flushed directly, but each job
will be queued to different thread that will prepare it for blitting.
Painter currently contains method flush() and end() that must be used
to finish rendering or to access attached image buffer.

For example this code:

  // Create image and painter context
  Image i(320, 200, Image::FormatPRGB32);
  Painter p(i);

  // Serialize commands
  p.clear(Rgba(0, 0, 0, 0));
  p.fillRect(Rgba(10, 10, 300, 300));
  p.drawText(Point(10, 10), StubAscii8("ABCDEF"), Font());

  // here some things can be drawn or not, flush() ensures it
  p.flush()

should not paint things immediately, but can paint it from different
thread while main thread is serializing work to it.

I can elaborate with it, because all painting methods will serialize
paint commands only into four virtual methods.

BTW: How is easiest way to implement "alternating scanlines"
rendering. I must go to lower layer than rasterizer ? I think that
replacement for function sweep_scanlines() can do the job.

For example here is sweep_scanlines():

        //--------------------------------------------------------------------
        template<class Scanline> bool sweep_scanline(Scanline& sl)
        {
            for(;;)
            {
                if(m_scan_y > m_outline.max_y()) return false;
                sl.reset_spans();
                unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
                const cell_aa* const* cells =
m_outline.scanline_cells(m_scan_y);
                int cover = 0;

                while(num_cells)
                {
                    const cell_aa* cur_cell = *cells;
                    int x    = cur_cell->x;
                    int area = cur_cell->area;
                    unsigned alpha;

                    cover += cur_cell->cover;

                    //accumulate all cells with the same X
                    while(--num_cells)
                    {
                        cur_cell = *++cells;
                        if(cur_cell->x != x) break;
                        area  += cur_cell->area;
                        cover += cur_cell->cover;
                    }

                    if(area)
                    {
                        alpha = calculate_alpha((cover <<
(poly_subpixel_shift + 1)) - area);
                        if(alpha)
                        {
                            sl.add_cell(x, alpha);
                        }
                        x++;
                    }

                    if(num_cells && cur_cell->x > x)
                    {
                        alpha = calculate_alpha(cover <<
(poly_subpixel_shift + 1));
                        if(alpha)
                        {
                            sl.add_span(x, cur_cell->x - x, alpha);
                        }
                    }
                }

                if(sl.num_spans()) break;
                ++m_scan_y;
            }

            sl.finalize(m_scan_y);
            ++m_scan_y;
            return true;
        }

I must debug it, but it seems that this function is not modifying
anything except some working variables (m_scan_y) and Scanline& sl
instance that will be unique to each thread, so it seems that there is
perfect way how to share rasterizer across threads without any
synchronization (and i think that sharing rasterizer means better
performance)

So, thanks for tips and good comment. On success I will inform here
about my results:)

Cheers
- Petr

2009/5/9 Stephan Assmus <superstippi@...>:

> Hi Petr,
>
> On 2009-05-09 at 12:00:45 [+0200], Petr Kobalíček
> <kobalicek.petr@...> wrote:
>> thanks for detailed reply:) I think that my code is right, because my
>> geometry model is quite different. The x1,y1 positions are first and
>> x2,y2 positions are last, but they are not in region. I think that RECT
>> structure and HREGION from WinAPI should explain this:
>>
>> width = x2 - x1;
>> height = y2 - y1;
>>
>> x2/y2 are not part of rect. Only code I'm not sure is -0.000001, I think
>> that after your explanations it's not necessary, thank you.
>
> Perfect, that's clear then.
>
>> Currently my problem is different. Part of my thesis is multithreading
>> and I don't know how to access scanlines from multiple threads, to be
>> hones I don't know how AGG rasterizer exactly works. My current idea is
>> to sweep_scanlines() from different threads, but this can't be done
>> without locking, so my second idea is rasterizer for each thread and set
>> different clipping for each. This will isolate and optimize access, but
>> the path will be computed in all threads.
>
> I think the best approach depends on your framework. Ie, do you render a
> complete scene and want this to be multi-threaded? Or do you provide a
> graphics API and render one path at a time or whatever the client code that
> uses your lib can trigger to be rendered in one call into your lib? From
> what I understand of what you are doing, it's the later. Then you have the
> following problem: There is of course a slight overhead to managing the
> threads, preparing jobs and triggering the threads to run. If the work to
> be done falls below a certain threshold, it could be beneficial to switch
> between single threaded rendering and multi-threaded rendering depending on
> estimated penalty. However, this again could pose a problem. Assume client
> code uses your library to render text, and enters your library for each
> glyph in a text run. The glyphs are small and each time you may estimate
> that it's better to stay single threaded, and effectively the whole glyph
> run is rendered in a single thread as a result. To work around this, your
> library could encourage "bulk rendering", where the client code schedules
> more data to be rendered in one API call.
>
> Anyways, in the background, I believe one "pipeline" per thread would be
> best, ie including rasterizer, renderer, scanline storage, everything that
> changes during rendering. And you need to be careful! For example, the
> agg::path_storage has the iterator implementation built-in, IAW iterating
> over a path storage is _not_ a read-only operation! So you should extract
> that from the actual path storage, so that you can have one instance of a
> path storage, but one iterator instance in each thread to iterate the same
> path data.
>
> Finally I would estimate that alternating scanlines based rendering should
> yield the best balanceing effect. As you have already your own renderer
> code where you iterate over scanlines, I would extend the AGG rasterizer
> interface to be able to provide a "skip" value, ie the increment to be used
> when iterating over scanlines during rasterizing. And the same for your
> rendering implementation.
>
>
>> BTW: The out of bounds problem is probably gone. I think I fixed it in
>> BlitJit, but I'm not sure where exactly:)
>
> Cool.
>
> Best regards,
> -Stephan
>
> ------------------------------------------------------------------------------
> The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
> production scanning environment may not be a perfect world - but thanks to
> Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
> Series Scanner you'll get full speed at 300 dpi even with all image
> processing features enabled. http://p.sf.net/sfu/kodak-com
> _______________________________________________
> Vector-agg-general mailing list
> Vector-agg-general@...
> https://lists.sourceforge.net/lists/listinfo/vector-agg-general
>

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Vector-agg-general mailing list
Vector-agg-general@...
https://lists.sourceforge.net/lists/listinfo/vector-agg-general

 « Return to Thread: Rasterizer clipping