Texture bleeding - Blitzmax bug with virtual resolution

Started by Derron, October 13, 2017, 21:59:44

Previous topic - Next topic

Derron

#30
If you want to have a small example (using the DIG framework as described above):


SuperStrict

'keep it small
Framework BRL.standardIO
Import Brl.Max2D
Import Brl.Graphics
Import "../../base.util.registry.imageloader.bmx"
Import "../../base.util.registry.spriteloader.bmx"
Import "../../base.util.registry.bitmapfontloader.bmx"
Import "../../base.gfx.gui.input.bmx"

SetGraphicsDriver GLMax2DDriver()
Local g:TGraphics = Graphics(800, 600, 0, 60, 0 )


Local guiState:TLowerString = TLowerString.Create("test")

Local registryLoader:TRegistryLoader = New TRegistryLoader
registryLoader.LoadFromXML("res/config/startup.xml", True)
registryLoader.LoadFromXML("res/config/resources.xml", True)

GuiManager.SetDefaultFont( GetBitmapFontManager().Get("Default", 14) )

Local widget:TGUIInput = New TGUIInput.Create(New TVec2D.Init(70, 20), New TVec2D.Init(130,-1), "Sample Input", 50, guiState.ToString())
widget.SetTooltip( New TGUITooltipBase.Initialize("An input field", "Entering text does not make you an author!", New TRectangle.Init(0,0,250,-1)) )
widget.SetOverlay(GetSpriteFromRegistry("gfx_gui_overlay_player"))


Repeat
   Cls

   'fetch and cache mouse and keyboard states for this cycle
   GUIManager.StartUpdates()
   'system wide gui elements
   GuiManager.Update( guiState )
   'reset modal window states
   GUIManager.EndUpdates()

   KeyManager.Update()


   'draw a background
   SetClsColor(255,230,200); Cls
   'GetSpriteFromRegistry("gfx_startscreen").Draw(0,0)

   GuiManager.Draw( guiState )

   Flip 0
Until KeyManager.IsHit(KEY_ESCAPE) Or AppTerminate()

The Rendering of the 9patch is done in /dig/base.gfx.sprites.bmx



bye
Ron

IanMartin

I've seen this happen on many games.  On some games it only happens when run on some graphics cards, but not all, so it can be very annoying.  You will probably see it if you run Nux on a modern PC.
I think most of the time it is caused by the pixels of sprites going all the way to the edge of the sprite.  I.E. the sprite is cropped as small as possible.
To avoid this, I add two empty pixels on each side of my sprites.  So a 64x64 sprite becomes 68x68 and the actual sprite starts at 2, 2 instead of 0,0.  Yes, one extra pixel would probably also fix it, but I usually take things too far.
You have to adjust your positioning on things sometimes, unless you use AutoMidHandle of course :)
Platfinity (made with BlitzMax) on Steam:
http://store.steampowered.com/app/365440/Platfinity/

Derron

#32
Thanks for your idea Ian.


For now it does not seem to "bleeding" but more of some kind of "offset".


Also I cannot fix the bleeding-issue as I use "nine patch"-sprites in atlases. Means they have graphical borders describing stretchable areas and content-padding sizes. I could add blank pixels between the markers and the actual content - but when using the same sprites with java on an android device will lead to "offsets" as they do not know about the blank pixels.


Aside of that the blank pixels would "melt" with the actual content leading to wrong colors (eg. a mix of the "blank-with-alpha1.0"-color and the actual one). So "bleeding" would happen there too.




So while your suggestion is one of the most often provided one  it does not seem to help in that case. It would just "hide" the actual mistake/code-flaw from appearing on many sprites. But as said for my code it would still lead to an render-offset of "1,1" on the users computer (screen coord is correct but texture is offset by 1,1 pixels).



@ col
If you find the time, feel free to reply to my questions regarding "16.99999" and the value which would need to get passed instead (I assume 17.0000 is ok - as said above).

Edit: shortened the example code in msg7641 (the last with a code sample ;-)) so it does use less stuff - and should be easier to check for errors


bye
Ron

col

QuoteI assume 17.0000 is ok - as said above
It should be yes.

I can't actually get your framework to compile, not because of the framework but because MingW complains about __mod3 and __div3, something like that ::)
I did investigate this in the past and it had something to do with the order of the gcc libs in bmk. At the time I changed the order which worked for some apps but not for others - not a valid solution, and also waaaay off topic too  :P

A lot of people use the 1 pixel technique as, at the end of the day, it does solve the symptom and it's by far the easiest solution too.

Did you try turning off mip maps, and also setting the texparameter to GL_NEAREST to see has any effect?
Its a shame that we can't reproduce the issue to then verify a fix.
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

I did not try it yet ... as I want to create a "full test scenario" this evening:


- putting everything off by 0.1, 0.1 pixels (in my calls to drawsubimagerect())
- creating texture atlas with a blank border surrounding the sprite (which does not help regarding "bleeding" between a left- and a center-ninepatch-sub-sprite)
- disabling mipmaps (should not help as they were disabled already for certain spritesheets)
- gl_linear replacement



@ __mod3


Currently I have this in my BlitzMax/MinGW32 folder:

=== TDM-GCC Compiler Suite for Windows ===
---         GCC 4.5/4.6 Series         ---
***   Standard MinGW 32-bit Edition    ***

This works to compile WXMax, Reddis MaxMod2, Bah.mod/... and so on. Brucey was using a more current MinGW - which also should work. So with "NG" I compiled with "tdm64-gcc-5.1.0-2.exe" in the past (end of 2016 or so) and it worked.


@ 1 pixel technology
But as said: it doesn't handle the 1,1-pixel offset. So I cannot pixel-perfect align objects.

bye
Ron

col

Just an idea...
You're encoding more than just colour information in the atlas image - too much going on for one 1 image. I don't think that is a good idea and is the cause of 'it's difficult to implement 1 pixel border'. Separate the formatting metadata and the colour information into a separate images would remove any difficulties
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

#36

I appreciate the idea ... but ;-)
... separating the sprites into one single images would get rid of many other issues too (except wrapping-texture-bleeding). But this results in many file accesses through loading stage and results in many  texture switches (some of my game's users run old/ancient hardware).


The meta data should be there as it allows for easier control/configuration by the designer without needing additional (and fitting) images. The whole game should be moddable without much hassle and having many things in one place helps - at least I think so. Of course "single images" allow for easier replacement... so this argument isn't as valid as I thought at the first glance.


And again: not having "odd borders" (markers, graphical advices on where which sprite is stored...) does not get rid of the offset this one special computer has. So it is again just a way to minimize the pool of potential problems.




BUT ... one could think of creating a _new_ spritesheet/sprite-atlas out of the one the game loads. So during loading it reads all the needed information - and afterwards it creates a new atlas which does no longer need to have the additional information and also could have needed "additional borders" to avoid bleeding etc. . "Loading bar" could call that "Optimizing assets" (sounds nifty).
Imho this sounds possible but not very "neat" to implement ;-)




bye
Ron

col

QuoteBUT ... one could think of creating a _new_ spritesheet/sprite-atlas out of the one the game loads
But the information is already lost - what do you replace the metadata pixels with?

QuoteImho this sounds possible but not very "neat" to implement ;-)
The implementation can be as 'neat' as you make it  ;D
Could you use the existing metadata features that images use? You would have to write some code to read and write that metadata data but that would be a tidy solution  ;)
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

#38
The nine-patch information is stored in the sprite-type (a TRectangle vector for the borders and one for the content padding).


Each sprite consists of the information about where it is stored on the texture and how to behave when drawing it "stretched" (aka ninepatch-stretching or normal "scaling"-stretching).
Means, after sprite is created, the "pixel meta-information" is no longer needed. On android it is used to make the used graphic independend from how a developer or UX-designer wants it. A Button needs a background and has to be 100px wide. It depends on the designer if the background is a stretchable one or if if borders need to stay unstretched.


Short: Meta data pixels are no longer needed once they got loaded into their TSprite instances.



For this scenario it means: one could have a "SpritePack" (containing information about sprites and holding the actual texture) and a Method "RecreateAtlas()" - which then does the normal Asset-Atlas-packaging. Means, it also could optimize handcrafted spritesheets without the need of XML-files as texture-packer (and the likes) create.
Of course it costs CPU time on each asset-initialization phase (aka "loading files").
For the framework user it should be a clean solution - the dirty/tedious work is hidden in the sprite(pack)-class.


You could even extend it to move sprites from one pack to another - leading to two atlases receiving a RecreateAtlas()-call afterwards. This works as each sprite could "extract" a TImage of their own sub-texture. But I cannot imagine how "realtime" one could use that approach (talking about up to 1024x1024 texture atlases).


Edit: one thing which could be problematic is "shared textures". Imagine you have a sprite definition for "x0, y0, w400, h300" and another sprite which uses a portion of the other sprites pixels "x300, y0, w100, h300". In that case a "recreation" would duplicate pixel data. As it does not happen that often I wont handle/optimize that special situation. BUT there are chances of a sprite-texture being reused by another texture. For example a modder decides to reuse the background of a widget for another widget too. So he just defines the other sprite to use the same texture coordinates. On atlas recreation the texture data of these sprites would be used multiple times. Only way to avoid that is by using a "exists already"-check (eg. with hashes of the textures in a TMap). Hmm, dunno if the needed work on that area equals the benefit of it - maybe a duplicated texture isn't as worse as it sounds.


bye
Ron

col

#39
I meant use the image file format metadata tags. Most file formats store metadata data separate from the image data. If you're placing the markers manually in the image then this would involve writing code to do the job of writing markers in to the file format metadata tag data while keeping the actual image data untouched.

It is a solution to keeping one file to contain marker data and image data completely separate.
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

I am not sure if I got you right.


I know there is a way to store text in the png files. But why should I do that? The image contains the markers because it allows the designer to use a simple graphics tool to define stretchable areas and borders - no need to play with meta data there.
Things like "sprite names" and their coordinates/dimensions are still written in simple XML-style (I know, you _could_ have them in the png file too). One could now say "so lets write the stretchable-thingy-configuration there too but as said I try to make the sub-sprite-"graphics" compatible to android ninepatch-images (so one could simply "copy paste" them).


My intention wasn't to keep meta data (border markers) separate from the image. If I really need to do so, then my spritepack could handle that by recreating the atlas without that marker-pixels (as the sprite knows that is has these markers, it is easier to extract the actual sprite content).






So somehow  doubt I understood you correctly.


bye
Ron

col

#41
QuoteSo somehow  doubt I understood you correctly.
It turns out that you did, and therefore I'm not surprised that you replied with that answer :)

So now I feel that I didn't understand you correctly :D

I'm just thinking of solutions to reduce time spent vs a working system. The first that springs to mind is the 1 pixel border thing. I understand your concern about the UVs that I highlighted, but the whole problem immediately goes away when introducing 1 pixel borders.

So the same system that you are using is used on Android? Ie metadata/position/style markers are in the actual image?
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

@ android
On android you can use "nine patch"-images - they contain these black-line markers defining stretchable areas and content padding.


For my game I "merged" these images into a single image atlas. So: no, I do not use that exactly the same way.


@ time spent
As it all would flow into the framework (which is open source and so other blitzmaxers could benefit from it) it is not that bad if something nifty gets created and auto-magically helps the users.


@ 1 pixel border thing
How should that "immediately go away"? I understand that it does no longer show the black or pink borders in the game but the graphics are still offset by 1 pixel on X and Y axis. I mean, they are not offset on our computers, but on that "let's play"-user's computer. So as long as UVs are not used the same on all computers, there will be differences. And this gets even worse, if it uses eg. a y-offset of "1" for the center-part but no y-offset at all for the border parts. This is what could be seen on the video and screenshots too: the dark colors of the corners end later (or earlier) than the stretched areas. They do not properly "align". How to describe it... similar to "o---___________---o" instead of "o----------------o" (but not coordinate wise but "graphical wise" because of the "mix of 4 surrounding texels" gl_linear might introduce).


PS: nice to see you still replying even after I post wall of text after wall of text.

bye
Ron

Derron

To make things more clear:




1) this way it should look

2) this might happen when using a "16.9999" leading to a gl_linear-defined smoothing with using the surrounding 4 pixels (or texels?)

3) this is happens with wrong coordinates

4) this is what seems to happen on that "let's play"-user's computer (the pink bar is there because the image contains it as "helper" for manual positioning in the painting program)


What we would fix with the "keep a blank pixel border" around the content is from "4)" to "3)".


Hope this explains it better than my previous (textual) descriptions.


bye
Ron

col

#44
Yes I understand now that you're concentrating on the UVs, and rightfully so :)

Going back to the debug example I showed you...
According to the UV data coming in from the application in relation to the image. Lets concentrate on the V part of the UV data. The V component is telling the gpu to sample from texel 47 to 69. Ie its sampling from 47 to 69 inclusive, but the markers are showing 47 to 68 inclusive. Sure enough the difference is 22 pixels but you don't want to sample from 47 to 69 inclusive in this case.

https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."