Design suggestions?

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Design suggestions?

Post by albinopapa » September 3rd, 2015, 11:37 pm

In practicing SSE, I've decided to make some image filters, blending and lighten and so on. I'm having troubles deciding how I should go about implementing them. I tried a few different methods, but keep running into limitations with each.

Storing a surface in the filter class: The idea was I have the surface stored int he image filter class, and call one of the functions to manipulate the surface. The problem I had was that storing the manipulated pixels back on to the surface became cumulative. So each successive pass for each frame made the pixels brighter and brighter for instance when calling the Lighten function.

Passing a surface to the filter: The idea was that I could pass a source surface to the filter, and store the result in the filter's surface in the Go function, then call the filter.draw function in the ComposeFrame function of Game. The problem with this is the size of the filter's surface would have to match the surface being passed in. So the filter's surface would have to be resized for each different surface I wanted to pass through the filter. This would be slow always allocating and deallocating memory.

Passing a surface through the filter: There were two options here, one would have the same problem as storing a surface in the filter, where storing the converted pixels back onto the source surface would accumulate the result each frame. The second option was to just have the filter draw directly to the backbuffer, which provides the result I'm looking for, but doesn't feel right.

Passing a filter to a surface: What I would like is to have a filter be a class, where I just pass in the filter to the surfaces draw function, and then the surface will call the fitlers' Apply function and the surface will be responsible for putting the pixels on the back buffer and the filter will just work with the pixels passed to it. The problem is each filter needs a different set of inputs, so I'm not sure if this is possible. If I don't inherit from a base class, I would have to overload the Surface::Draw function for each type of filter I create.

Any suggestions?
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Design suggestions?

Post by albinopapa » September 4th, 2015, 3:31 pm

A nagging thought keeps popping in my head as possible solutions.

First thought is to create a "scratch pad" of equal size for each surface.

Second thought is to create just one scratch pad, a memory pool if you will, and just store the address of where in the pool that surface's data is located.

In either case, I would need double the memory requirements as before. The first solution might be the easiest to manage though.

Third though is to take the memory pool idea and just reuse the same space, making draw calls just after each filter has been applied, meaning before working with the next surface I draw the result of each filter. The pool would probably just be a second backbuffer.

The benefits of course are not having to allocate/deallocate each frame or call to a surface's draw function. This is probably close to what is done in games anyway. You create a texture, upload to video card, create another texture that DirectX renders to, then you take that texture perform your filtering, then render to the back buffer, then flip or move to the next item to be rendered.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Design suggestions?

Post by chili » September 5th, 2015, 1:15 am

My solution would be to use a std::function object pass your filter in. The problem in which each filter needs a different set of inputs can be solved with std::bind.

The idea in which the surface will be responsible for putting the pixels on the back buffer and the filter will just work with the pixels passed to it will probably sacrifice performance. You call the filter's process() method and pass it a ref& to the surface and it accesses the surface's buffer directly.
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Design suggestions?

Post by albinopapa » September 6th, 2015, 8:09 pm

chili wrote:My solution would be to use a std::function object pass your filter in. The problem in which each filter needs a different set of inputs can be solved with std::bind.
I've a little experience using packed parameters and variadic templates thanks to cameron's event class. Nice to know std::bind seems to do the decay work for you. It did cross my mind to use variadic templates, but was a merely a fleeting thought. I'm not keen on recursively calling functions, and I haven't fully worked out how to do what bind does, but then again, I didn't know about std::bind. I'll have to give it a go.
chili wrote: The idea in which the surface will be responsible for putting the pixels on the back buffer and the filter will just work with the pixels passed to it will probably sacrifice performance. You call the filter's process() method and pass it a ref& to the surface and it accesses the surface's buffer directly.
Well damn. The biggest reason for wanting to use a common function to draw from is to keep code redundancy down. There's clipping of sprites to screen edges, then there's handling edge cases with SSE. I really didn't want to have to duplicate those bits for each filter. Then again, I could clip in the surfaces draw call, and pass a clip rect to the filter.

Something like:

Code: Select all

filter.Process(const UINT X, const UINT Y, const RECT &clip, const UINT SurfaceWidth, D3DCOLOR *const SourceSurface, D3DGraphics &gfx);
X and Y are the position to start drawing.
clip would be if the sprite is off screen or larger than back buffer.
SurfaceWidth would be the pitch of the surface in case the width of clip is different.
SourceSurface would be the surfaces' surface or buffer of pixels

the destination would of course be the back buffer which can be gotten from a gfx.getbuffer.

The edge cases and unaligned pixels I think I'll just handle using unaligned loads and stores, my brain hurts too much to constantly figure out the shifting, shuffling and masking otherwise.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

cameron
Posts: 794
Joined: June 26th, 2012, 5:38 pm
Location: USA

Re: Design suggestions?

Post by cameron » September 6th, 2015, 8:53 pm

An alternative to std::bind is to store the wrap your function call in a lambda. I've found this yields better performance, but lacks placeholders.

Code: Select all

std::function function;
template<class O, typename Func, typename... Args> Function(O& o, Func(O::*func), Args&&... args)
{
function = [&o, func, &args...]()->RT
{
return (RT)((o.*func)(std::forward<Args>(args)...));
};
}
A faster way is to use placement new over operator=
Computer too slow? Consider running a VM on your toaster.

Post Reply