Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I was toying around with infinitely scrolling 2D textures using the XNA framework and came across a rather strange observation.

Using the basic draw code:

spriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointWrap, null, null);
spriteBatch.Draw(texture, Vector2.Zero, sourceRect, Color.White, 0.0f, Vector2.Zero, 2.0f, SpriteEffects.None, 1.0f);
spriteBatch.End();

with a small 32x32 texture and a sourceRect defined as:

sourceRect = new Rectangle(0, 0, Window.ClientBounds.Width, Window.ClientBounds.Height);

I was able to scroll the texture across the window infinitely by changing the X and Y coordinates of the sourceRect.

Playing with different coordinate locations, I noticed that if I made either of the coordinates too large, the texture no longer drew and was instead replaced by either a flat color or alternating bands of color. Tracing the coordinates back down, I found the following at around (0, -16,777,000):

stretched texture

As you can see, the texture in the top half of the image is stretched vertically.

My question is why is this occurring? Certainly I can do things like bind the x/y position to some low multiple of 32 to give the same effect without this occurring, so fixing it isn't an issue, but I'm curious about why this happens. My initial thought was perhaps it was overflowing the coordinate value or some such thing, but looking at a data type size chart, the next closest below is an unsigned short with a range of about 32,000, and above is an unsigned int with a range of around 2,000,000,000 so that isn't likely the cause.

Update 1: The image above shows the texture stretching vertically near (0, -16,777,000), and the texture stretches horizontally at around (-16,777,000, 0). If I go beyond this point on both axes, the texture is stretched in both directions. This occurs in both the positive and negative directions.

The above positions were noted with a 32x32 texture. With a 16x16 texture, the distortion occurs at the same location. Interestingly, with a 64x64 texture, the distortion occurs in the same location on the x-axis, but is actually farther on the y-axis, more near (0, -17,301,000). It occurs at the same location with a 64x64 texture too, I mistakenly used a 64x66 texture before.

Update 2: I have repeated the test with a 1024x1024 texture and have found the same results. I looked farther and noticed that it stretches again at double the distance, somewhere near (-33,554,000, -33,554,00), but then doesn't happen again until around 4 times the initial stretching point, near (-67,109,000, -67,109,000).

It seems to occur only at powers of two times that initial value, 16,777,216 * 2^n. The answer below by mh01 suggested to check the MaxTextureRepeat cap of my graphics card (a GeForce GTX 560). According to KluDX this cap has a value of 8192, and it's description indicates that texture coordinates are stored in 32-bit signed integers.

Correction: The stretch points occur at 2^n intervals because my texture sizes are 2^n. Testing with a texture size of 25x33 resulted in the first stretch point appearing near (-13,107,000, -17,301,000). So while it's related to the texture's aspect ratio, it doesn't seem to be directly related to the texture's size.

share|improve this question
1  
Interesting that this happens at -16,777,000, 24-bit unsigned numbers go from 0 to 16,777,216. I'm not sure what the significance is. –  Nic Foster Jun 25 '12 at 5:42
    
Could you run a test for us? Try some other texture sizes other than 32x32, and see if the 16,777,216 limit changes depending on it. I'm asking this because the values in the source rectangle are converted inside the SpriteBatch to texture coordinates by dividing them by the texture's size, so it is possible that the size will affect the limit. –  David Gouveia Jun 25 '12 at 7:01
    
Updated question with data from 16x16 and 64x64 texture sizes. –  Shaun Hamman Jun 25 '12 at 16:13
    
Puzzling. Just in case, try something very large like 512x512 or 1024x1024. –  David Gouveia Jun 25 '12 at 16:53
    
Even more interesting, see this: altdevblogaday.com/2012/02/05/dont-store-that-in-a-float - "The number ‘60’, like all integers up to 16777216, can be exactly represented in a float. The watch window shows that the next value after 60 that can be represented by a float is about 60.0000038". –  Jimmy Shelter Jun 25 '12 at 19:30
show 6 more comments

3 Answers 3

I think you've found some weird corner case bug, but you should never be in the scenario that your source rect has a coordinate like -16.777,000. It looks like you're not 100% sure what source rect is for.

The source rectangle should 'never' (I'll get back to that) be larger than the size of the texture. What the source rectangle does is select the part of the texture that will be drawn to the sprite you've selected. Say you have a texture that is 200x200, now if you only want the top left quadrant to be drawn you would use a source rectangle like (0,0, 100, 100).

Now for infinitely scrolling textures. As you've noticed you can keep updating the x and y coordinates of the source rectangle to have it keep scrolling. This happens because in certain texture modes (like Linear Wrap) a coordinate that would be outside the texture is placed inside the texture again by using the modulo of the texture size. So to counter the bug you've encountered we need to add this code ourselves so that the linear wrap algorithm never encounters such large numbers.

So some code:

int x; //x-coordinate of source rectangle
int y; //y-coordinate of source rectangle
int width; //texture width
int height; //texture height

x = x % width; //x will always be smaller than 2x the texture width
y =y % height; //y will always be smaller than 2x the texture height

Rectangle sourceRect = new Rectangle(x,y, width, height);

This should fix your problem :)

share|improve this answer
1  
The OP wrote on his last paragraph that fixing the problem was not the issue. What he wants is a explanation for why the the problem is occurring. –  David Gouveia Jun 25 '12 at 6:47
    
blush was that there? I guess it was. Hmm that makes this anwser kind of irrelevant. Also couldn't find something relevant here (no 24bit data types) msdn.microsoft.com/en-us/library/windows/desktop/… –  Roy T. Jun 25 '12 at 7:34
    
Good to have a solution here in case someone looking for it passes by, but it unfortunately doesn't answer my question. Had I seen a data type with a range close to that I'd have just assumed it was some kind of overflow error or something, but I saw no such thing according to this Wikipedia page. Also, bonus points for a very clean, commented example. :-) –  Shaun Hamman Jun 25 '12 at 16:17
add comment

Check the value for MaxTextureRepeat in your DirectX Caps Viewer (you'll need the DX SDK installed for this of course).

share|improve this answer
    
I used the nifty KluDX caps viewer instead as I don't have the DX SDK installed, but it revealed that the MaxTextureRepeat of my card (a GeForce GTX 560) is 8192. It also revealed that the texture coordinates are stored in 32-bit signed integers. –  Shaun Hamman Jun 25 '12 at 21:01
add comment
up vote 0 down vote accepted

Having been unable to come up with a better solution, I'm going to settle on this being due to floating point precision errors at such extreme coordinates. As Roy T. pointed out, you should never come across such coordinates in a properly set up environment, so it is effectively a non-issue, but hopefully this question will provide answers for those experiencing this issue.

share|improve this answer
    
I'm still curious though :\ –  David Gouveia Oct 14 '12 at 18:01
    
I'm still a little curious too, but have run out of ways to test it. Every example of this issue I've seen online has been attributed to floating point precision, though none give a great explanation of why. I can play with spritebatch settings all day but I suspect, if an answer can be found, it's going to have to be by someone with lower-level GPU knowledge. –  Shaun Hamman Oct 14 '12 at 21:22
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.