I have a Win32 non-game windowed app that uses a Direct2D device context/HWND render target to draw a window. Currently it uses a DXGI swap chain with the DXGI_SWAP_EFFECT_DISCARD swap effect.
Microsoft recommends using the new flip model swap effects, either DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL or DXGI_SWAP_EFFECT_FLIP_DISCARD. I'm interested in using them primarily because they would allow me to specify a list of dirty rects when calling Present1(), which should improve performance/power usage.
Simply changing the SwapEffect to either of the new flip model values produces a weird (but actually expected) result of drawing a black window each second frame, with artifacts of the previous frames visible onscreen.
So the question is: is it possible to use the new flip model swap effects in this situation, and if yes, how should things be set up?
Given that the app needs to draw the dirty rects into an otherwise valid buffer, it seems that a correct approach would involve maintaining two buffers with essentially the same content (one to draw into, and one to give to the DWM for composition), so not sure if it would be possible to achieve any performance gains this way in an app that doesn't redraw each frame completely. But perhaps I'm missing something important.
The swap chain is currently set up as follows:
swapChainDesc.Width = ...;
swapChainDesc.Height = ...;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
swapChainDesc.Flags = 0;
EDIT 1
It turns out that DXGI_SWAP_EFFECT_DISCARD forces BufferCount to 1, so my initial value of 2 was somewhat misleading, as only one buffer is used. Source (3rd comment).
Also the docs for DXGI_SWAP_EFFECT say that UWP apps are forced into the flip model, so this should be a solvable problem.
There are two good ways to do it.
The first way is a little heavier on energy usage. You can draw your contents into an intermediate buffer/render texture, and copy it to swapchain just before every present. That way you can only actually render the parts that changed in your intermediate buffer, and not care about what the state of the swapchain is.
The second way is more complicated, but can yield optimal energy usage. Instead of using intermediate buffer and drawing only what changes since the last frame there, you draw directly into the swapchain buffer. For this to work correctly, you need to redraw not what changes between current and last frame, but between current and (current - BufferCount) frame. For instance:
Frame 1 - you draw a green rectancle at (200 x 200) with dimensions of (150 x 150). The dirty region is entire frame because it's the first frame.
Frame 2 - you draw a blue rectangle at (250 x 250) with dimensions of (50 x 50). The dirty region is (250, 250, 300, 300).
Frame 3 - you draw a red rectangle at (225 x 225) with dimensions of (50 x 50). The dirty region is (225, 225, 50, 50).
If your buffer count is 2, that means when you draw frame 3, you need to not only redraw the dirty region of (225, 225, 50, 50), but also the dirty region of (250, 250, 300, 300).
Related
Repeatedly drawing a semi-opaque black rectangle over the entire canvas before each animation frame is an easy way to get an afterimage effect for moving shapes and it gives me exactly what I need - up to a point. With too slow a fade it doesn't fade all the way to black. Here's an example:
var canv = document.createElement('canvas');
document.body.appendChild(canv);
var ctx = canv.getContext('2d');
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, 100, 100);
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fillRect(20, 20, 60, 60);
window.requestAnimationFrame(doFade);
function doFade() {
// Never fades away completely
ctx.fillStyle = 'rgba(0, 0, 0, 0.02)';
ctx.fillRect(20, 20, 60, 60);
window.requestAnimationFrame(doFade);
}
jsfiddle
This looks to me like a numeric precision problem - you can't expect the canvas to keep floating point pixel values around - but I'm not sure how to get around this.
I tried reading the image into a pattern, blanking the canvas, and then filling with the pattern at lower opacity in the hope that I could make rounding error work in my favor, but it seems to have the same result.
Short of reading out the image data and setting to black any pixels below a certain threshold, which would be prohibitively slow, I'm running out of ideas and could use some suggestions.
Thanks!
I thought I'd share my solution for the benefit of anyone else who might run into this problem. I was hoping to avoid doing any pixel-level manipulation, but beyond a certain threshold it's just not possible with the built-in canvas operations because the underlying bitmap is only 8 bits per channel and small fades will work out to less than one least significant bit and won't have any effect on the image data.
My solution was to create an array representing the age of each pixel. After each frame is drawn, I scan the imageData array, looking only at the alpha channel. If the alpha is 255 I know the pixel has just been written, so I set the age to 0 and set the alpha to 254. For any other non-zero alpha values, I increment the pixel age and then set the new alpha based on the pixel age.
The mapping of pixel age to alpha value is done with a lookup table that's populated when the fade rate is set. This lets me use whatever decay curve I want without extra math during the rendering loop.
The CPU utilization is a bit higher, but it's not too much of a performance hit and it can do smooth fades over several seconds and always fades entirely to black eventually.
I'm currently creating a 2d tile game and I'm wondering if the tiles has to move or the character.
I ask this question because I already have created the "2d tile map" but it is running too slow, and I can't fix it. I tried everything now and the result is that I get 30 fps.
The reason that it is running too slow is because every 1ms with a timer, the tiles are being redrawn. But I can't figure out how to fix this problem.
This is how I make the map :
public void makeBoard()
{
for (int i = 0; i < tileArray.GetLength(0); i++)
{
for (int j = 0; j < tileArray.GetLength(1); j++)
{
tileArray[i, j] = new Tile() { xPos = j * 50, yPos = i * 50 };
}
}
}
Here I redraw each 1ms or higher the tiles and sprites :
private void Wereld_Paint_1(object sender, PaintEventArgs e)
{
//label1.Text = k++.ToString();
using (Graphics grap = Graphics.FromImage(bmp))
{
for (int i = 0; i < tileArray.GetLength(0); i++)
{
for (int j = 0; j < tileArray.GetLength(1); j++)
{
grap.DrawImage(tileArray[i, j].tileImage, j * 50, i * 50, 50, 50);
}
}
grap.DrawImage(player.movingObjectImage, player.xPos, player.yPos, 50, 50);
grap.DrawImage(enemyGoblin.movingObjectImage, enemyGoblin.xPos, enemyGoblin.yPos, 50, 50);
groundPictureBox.Image = bmp;
// grap.Dispose();
}
}
This is the Timer with a specific interval :
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if(player.Update()==true) // true keydown event is fired
{
this.Invalidate();
}
label1.Text = lastFrameRate.ToString(); // for fps rate show
CalculateFrameRate(); // for fps rate show
}
Are you writing the tile implementation yourself? Probably the issue is that at every frame you're drawing all tiles.
2D engines with scrolling tiles should draw tiles on a larger sprite than the screen, then draw that sprite around which is a fast operation (you'd need to specify the language you're using so I can provide some hint on how to actually make that fast - basically an in video memory accelerated blit, but every language has it's way to make it happen)
when the border of this super-sprite is closer to the screen border than a threshold (usually half tile), the larger sprite is redrawn around the current position - but there is no need to draw all the tiles on this! start copying the supersprite on this recentered sprite and you only need to draw the tiles missing from the previous supersprite because of the offset.
As mentioned in the comments your concept is wrong. So here's just a simple summary of how to do this task:
Tile map is static
From functional point of view does not matter if player moves or the map but from performance point of view the number of tiles is hugely bigger then number of players so moving player is faster.
To achieve player centered or follow views you have to move the camera too.
rendering
Repainting every 1ms is insane and most likely impossible on nowadays computers if you got medium complexity of the scene. Human vision can't detect it anyway so there is no point in repainting more that 25-40 fps. The only reason for higher fps needs is to be synchronized with your monitor refreshing to avoid scan line artifacts (even LCD use scan lines refreshing). To have more fps then the refresh rate of your monitor is pointless (many fps players would oppose but our perception is what it is no matter what they say).
Anyway if your rendering took more then 1ms (which is more then likely) then your timer is screwed because it should be firing several times before the first handler even stops. That usually causes massive slowdowns due to synchronisation problems so the resulting fps is usually even smaller then the rendering engine could provide. So how to remedy that?
set timer interval to 20ms or more
add bool _redraw=false
And use it to redraw only when you need to repaint screen. So on any action like player movement, camera movement or turn, animation change set it to true
inside timer event handler call your repaint only if _redraw==true and set it to false afterwards.
This will boost performance a lot. even if your repaint will take more than the timer interval still this will be much much faster then your current approach.
To avoid flickering use Back buffering.
camera and clipping
Your map is most likely much bigger then the screen so there is no point to repaint all the tiles. You can look at the camera as a means to select the right part of your map. If your game does not use rotations then you need just position and may be zoom/scale. If you want rotations then 2D 3x3 homogeneous matrices are the way.
Let assume you got only position (no zoom or rotating) then you can use this transformations:
screen_x=world_x-camera_x
screen_y=world_y-camera_y
world_x=screen_x+camera_x
world_y=screen_y+camera_y
So camera is your camera view position, world is you tile position in map grid and screen is the position on screen. If you got indexes of your tile in map then just multiply them by tile size in pixels to obtain the world coordinates.
To select only visible tiles you need to obtain the corner positions of your screen, convert them into world coordinates, then into indexes in map and finally render only tiles inside rectangle that these points form in your map + some margin of error (for example render 1 tile enlarged rectangle in all directions). This way the rendering will be independent on your map size. This process is called clipping.
I strongly recommend to look at these related QAs:
Improving performance of click detection on a staggered column isometric grid
2D Diamond (isometric) map editor ... read the comments there !!!
The demo in the linked QAs use only GDI and direct pixel access to bitmaps in win32 form app so you can compare performance with your code (they should be similar) and tweak your code until it behaves as should.
I am trying to implement background scrolling using SDL 2.
As far as I understand one can only move source rectangle by an integer value.
My scrolling works fine when I move it by one every iteration of the game loop.
But I want to move it slower. I tried to move it using this code
moved += speed;
if (moved >= 1.0) {
++src_rect.x;
moved -= 1;
}
Here moved and speed are doubles . I want my background to move something like ten times slower, therefore I set speed to 0.1. It does move ten times slower, but the animation is no longer smooth. It kind of jumps from one pixel to another, which looks and feels ugly when the speed is low.
I am thinking of making my background larger and scrolling it using an integer. Maybe when background is large enough the speed of 1 will seem slower.
Is there a way to scroll not a very large background slowly and smoothly and the same time?
Thanks.
What I would do is have a set of floats that would track the virtual screen position, then you just cast the floats to integers when you actually render, that way you don't ever lose the precision of the floats.
To give you an example, I have an SDL_Rect, I want to move it every frame. I have two floating point variables that track the x and y position of the rect, every frame I would update those x and y positions, cast them to an integer, and then render the rect, EX:
// Rect position
float XPos = 0.0f;
float YPos = 0.0f;
SDL_Rect rect = {0, 0, 64, 64};
// Update virtual positions
XPos += 20.0f * DeltaTime;
YPos += 20.0f * DeltaTime;
// Move rect down and to the right
rect.x = (int)XPos;
rect.y = (int)YPos;
While this doesn't give you the exact result you are wanting, it is the only way that I know of to do this, it will let you delay your movement more precisely without giving you that ugly chunkiness in the movement, it also will let you add stuff like more precise acceleration too. Hope this helps.
Alpha invisibility.
I currently define circular regions on some images as "hot spots". For instance, I could have my photo on screen and overlay a circle on my head. To check for interaction with my head in realtime, I would returnOverlaps and do some manipulation on all objects overlapping the circle. For debugging, I make the circle yellow with alpha 0.5, and for release I decrease alpha to 0, making the circle invisible (as it should be).
Does this slow down the program? Is there another way to make the circle itself invisible while still remaining capable of interaction? Is there some way to color it "invisible" without using a (potentially) costly alpha of 0? Cache as bitmap matrix? Or some other efficient way to solve the "hot spot" detection without using masks?
Having just a few invisible display objects should not slow it down that much, but having many could. I think a more cleaner option may be to just handle it all in code, rather then have actual invisible display objects on the stage.
For a circle, you would define the center point and radius. Then to get if anyone clicked on it, you could go:
var xDist:Number = circle.x - mousePoint.x;
var yDist:Number = circle.y - mousePoint.y;
if((xDist * xDist) + (yDist * yDist) <= (circle.radius * circle.radius)){
// mousePoint is within circle
} else {
// mousePoint is outside of circle
}
If you insist on using display objects to set these circular hit areas (sometimes it can be easier visually, then by numbers), you could also write some code to read those display objects (and remove them from being rendered) in to get their positions and radius size.
added method:
// inputX and inputY are the hotspot's x and y positions, and inputRadius is the radius of the hotspot
function hitTestObj(inputA:DisplayObject, inputX:int, inputY:int, inputRadius:int):Boolean {
var xDist:Number = inputX - inputA.x;
var yDist:Number = inputY - inputA.y;
var minDist:Number = inputRadius + (inputA.width / 2);
return (((xDist * xDist) + (yDist * yDist)) =< (minDist * minDist))
}
An alpha=0 isn't all that costly in terms of rendering as Flash player will optimize for that (check here for actual figures). Bitmap caching wouldn't be of any help as the sprite is invisible. There's other ways to perform collision detection by doing the math yourself (more relevant in games with tens or even hundreds of sprites) but that would be an overkill in your case.
I have implemented a marquee text widget using Qt4. I painted the text content onto a pixmap first. And then paint a portion of this pixmap onto a paint device by calling painter.drawTiledPixmap(offsetX, offsetY, myPixmap)
My Imagination is that, Qt will fill the whole marquee text rectangle with the content from myPixmap.
Is there a ever faster way, to shift all existing content to left by 1px and than fill the newly exposed 1px wide and N-px high area with the content from myPixmap?
Well. This is a trick I used to do with slower hardware back in the old days. Basically, the image buffer is allocated twice as wide as needed with 1 extra line at the beginning. Build the image to the left of the buffer. Then draw the image repeatedly with the buffer advancing 1 pixel at a time in the buffer.
int w = 200;
int h = 100;
int rowBytes = w * sizeof(QRgb) * 2; // line buffer is twice as the width
QByteArray buffer(rowBytes * (h + 1), 0xFF); // 1 more line than the height
uchar * p = (uchar*)buffer.data() + rowBytes; // start drawing the image content at 2nd line
QImage image(p, w, h, rowBytes, QImage::Format_RGB32); // 1st line is used as the padding at the start of scroll
image.fill(qRgb(255, 0, 0)); // well. do something to the image
p = image.bits() - rowBytes / 2; // start scrolling at the middle of the 1st (blank) line
for(int i=0;i<w;++i, p+=sizeof(QRgb)) {
QImage scroll(p, w, h, rowBytes, QImage::Format_RGB32); // scrool 1 pixel at a time
scroll.save(QString("%1.png").arg(i));
}
I am not sure this will be any faster than just change the offset of the image and draw it strait. The hardware today is really powerful which renders a lot of old tricks useless. But it's fun to play obscure tricks. :)
Greetings,
one possibility to achieve this would be to:
Create a QGraphicsScene + View and put the pixmap on that twice (as QGraphicsPixmapItem), so they are right next to each other.
Size the view to fit the size of the (one) pixmap.
Then, instead of repainting the pixmap, you simply reposition the view's viewport, moving from one pixmap to the next.
Jump back at the end to create the loop.
This may or may not be faster (in terms of performance) - I have not tested it. But may be worth a try, if only for the sake of experiment.
Your approach is probably one of the fastest one since you use low level painting methods. You can implement an intermediate approach between low level painting and the QGraphicsScene option : using a scroll area containing a label.
Here is a sample of code that create a new scroll area containing a text label. You may scroll the label automatically using a QTimer to trigger the scrolling effect, that gives you a nice marquee widget.
QScrollArea *scrollArea = new QScrollArea();
// ensure that scroll bars never show
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QLabel *label = new QLabel("your scrolling text");
// resize the scroll area : 50px length and an height equals to its content height.
scrollArea->resize(50, label->size().height());
scrollArea->setWidget(label);
label->show(); // optionnal if the scroll area is not yet visible
The text label inside the scroll area can be moved from left to right by one pixel using the QScrollArea::scrollContentsBy(int dx, int dy) with a dx parameter equals to -1.
Why not just do it on a pixel by pixel basis? Due to the way caches work writing the pixel to the one before it all the way until you get to the end. Then you can fill the final column by reading from your other image.
Its then pretty easy to SIMD optimise it as well; though you start getting into per-platform optimisations at this point.