SyntaxBomb - Indie Coders

Languages & Coding => BlitzMax / BlitzMax NG => Topic started by: Derron on October 13, 2017, 21:59:44

Title: Texture bleeding - Blitzmax bug with virtual resolution
Post by: Derron on October 13, 2017, 21:59:44
Just a short question to you all:


I saw a let's play of my game and the user seems to have issues with my sprite atlases: pink lines are visible (surrounding sprites in my atlases) - or black ones (ninepatch sprites).
(if you want to see it live: https://  [...] youtu.be/0tipTUY_JaI?list=PLXsYWPpDygXUpyERvNJYxNJgnNTXL2xEs - or download it on your own at http://www.tvtower.org (http://www.tvtower.org))
)


I assume it is happening on a windows machine - using OGL (with DX he does not have this issues). On my machines neither OGL nor DX have this issue. My Sprites are _not_ power-of-two. Would this already solve the issue? Should I consider creating them "live on load"? Will save some GPU-memory when not doing that.




How do you tackle that?




PS: Sprite atlases are used to improve render speed - and I think I would have some hundreds to thousands of sprites if doing them "separately".

PPS: Excuse the youtube-link but I do not know how to "disable" autoconversion of youtubelinks to "video embed".



Thanks in advance for your ideas and tips.




bye
Ron
Title: Re: Texture bleeding
Post by: Kippykip on October 14, 2017, 12:52:34
Can't say I've ever had this happen before, although I'm only using the built in Max2D functions.
I should also mention I've only used the OpenGL rendering modes on the non-NG version of BlitzMax. But I didn't have any issues with it.

Blitz3D on the other hand using fast image, I ran into this issue a lot and solved it partially by making everything by the power of two.
Title: Re: Texture bleeding
Post by: Derron on October 14, 2017, 13:18:13
I also only use the built-in functionality. Especially "DrawImageSubRect()" to draw portions of an image -> sprite atlas. It also happens to "non-stretched / non-repeated"-images, so it seems to be not a problem of the texture itself but some kind of coordinate mismatch or stretching of the whole texture.




Let's Player reported issue to be only on OGL not in DX. Luckily my "LoadImage" is encapsulated already in a "RegistryLoader"-class so it should be possible to resize the textures accordingly when loading on game start. But for now I am still open for discussion about other potential flaws resulting in this texture bleeding.




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 14, 2017, 15:47:03
At a guess my thoughts are steering towards the texture sampler. Is it possible to space the textures out another pixel or 2? That may help.
Title: Re: Texture bleeding
Post by: TomToad on October 15, 2017, 10:28:06
Had a similar problem before.  Turns out that texture filtering (anti-aliasing, MIP maping, etc...) was being done on the entire sprite sheet causing portions of one sprite to bleed into the next.  Solution was to make sure at least a 1 pixel border surrounded each sprite.
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 11:35:36
Instead of bleeding (mixing in outside pixel's colors) it is more or less a problem of "displacement".


(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2Fcx1UpTp.png&hash=623ff2f9746beee7768fb9bc7e2c55df2c0d9f67)


The right side of the "red surrounded area" is using a stretched sprite of the atlas, the left side is using another sprite + an overlayed icon. As you see, the "bottom" is flawless while the "top" seems to have an offset of 1 pixel. This is not the case on my machines and also no other user complained yet so I think it is no issue of my "sprite position calculations" (talking of floating point determinism ;-)).




As you might see, it also does not happen to all sprites:
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2Fj8rwkcF.png&hash=5d31505d495fc3e10fc11ca2e0daea27e825f88c)




The sprite atlas is this one:
(https://github.com/TVTower/TVTower/blob/master/res/gfx/misc/datasheet_general.png?raw=true)
(the black lines are markers for my nine-patch code - so they define stretchable areas and content padding).




If the engine scaled the texture up to the next power of two - why should it "offset" textures? "Bleeding" is merging in other colors" - it still might be the case here but after some closer looks I would more tend to say it is some "offset" problem.




If you want to check if it happens on your computer too (if so it might be more probably that the error is in my framework - see "floating point determinism"):
https://github.com/GWRon/Dig (https://github.com/GWRon/Dig)
especially the sample:
https://github.com/GWRon/Dig/tree/master/samples/gui (https://github.com/GWRon/Dig/tree/master/samples/gui)
(as it loads some of the gui-widgets based on the same priciples - sprite atlases).



Edit: having a closer look at aboves screenshots: if the texture "displacement" was a generic problem, then the whole "green bar" would have been displaced - so both should be "off by one pixel" but as this happens to only the stretched area in that case, it might be an issue with "DrawSubImageRect()" (and maybe the given parameters). But if that was the case: why does it happen to other sprites (non stretched, just using the absolute atlas positions) too?


(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FTKd6uO5.png&hash=71df35d2613054ec02bec943014eade62d0b95a5)


Ok so, let's assume it is a flaw in my code (the calculation of sub-texture offsets): shouldn't it then happen in all cases, OGL _and_ DX (as both get passed "x=101" instead of "x=100" to their internal draw/render-commands). As the player told me it only happens on "OGL" I tend to say it is some texture filtering thing or so.



bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 12:54:32
Oh my... this will be a trial and error thi g to resolve  ;D
If you're 100% sure that your math is correct then suspect the texture sample settings. Sampling can happen across more than the immediate adjacent 1 pixel dependong on the sampling mode. If you try moving the image down maybe 1, 2 or 3 pixels to test if sampling occurs o
at a different place on the texture, maybe turn mip mapping off.

It doesnt help that you cant reproduce it so it'll be like finding a needle in a haystack.
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 13:44:24
But how are "sampling settings" and "x offset = 1" and/or "y offset = 1" connected? If it was a incorrect calculation it should happen with DX and OGL. If the textures are somehow "resized/scaled" (a little bit - during GPU-wise-powerOfTwo-correction) then why does it affect nearly all of these "multiple-sprite-compositions" (with exactly only the "stretchable" portion of the whole texture being displaced).


Might there be issues in Max2D regarding this? So some problems when passing floats to them? It all puzzles me because a "source code flaw" should be visible on one of my machines (I assume so) and a driver/gpu-issue should not create "offsets". Argh.




Did you run the DIG/sample code (should work with NG and vanilla - as we tried that on android as a somewhat more "complex" example :-)) ?




Regarding DrawSubImageRect():

Function DrawSubImageRect( image:TImage,x#,y#,w#,h#,sx#,sy#,sw#,sh#,hx#=0,hy#=0,frame=0 )
   Local x0#=-hx*w/sw,x1#=x0+w
   Local y0#=-hy*h/sh,y1#=y0+h
   Local iframe:TImageFrame=image.Frame(frame)
   If iframe iframe.Draw x0,y0,x1,y1,x+gc.origin_x,y+gc.origin_y,sx,sy,sw,sh
End Function

Source: https://github.com/blitz-research/blitzmax/blob/master/mod/brl.mod/max2d.mod/max2d.bmx (https://github.com/blitz-research/blitzmax/blob/master/mod/brl.mod/max2d.mod/max2d.bmx)


It happens to use "floats" as params, so I am passing calculations like "area.GetW() - 2 * NINEPATCH_MARKER_WIDTH" to it.


Might it be, that these values result eg. in "15.999997 - 2 * 1", so "13.999997" (or 14.000002) and then somehow the internal command (GL, DX) round it somewhat (eg. to "13" or "14.000002 to 15") ?
BUT - if that really was happening (rounding of values) then shouldn't these "offsets" happen in one run of the game, and in the other run it should not happen for that sprite? I mean: the "floating point determinism"-problem should result in a "random" value on calculation - so each "run" should result in a different value (rather than a precalculated value stored in the binary).






I also thought on creating a sample binary which exposes the texture used there: so one could check on a screenshot then, if the texture was altered. Or couldn't I already do that in a way of using GrabPixmap and then checking the pixmap data against the texture-pixmap-data (ignoring alpha) - as I assume, the pixmap-data is "unaltered" (compared to the TGLImageFrame and the likes). So I could automatically print "OK" or "failed" on certain tests.






Bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 14:05:59
Its surprising what happens from cpu to gpu, and even between gpu stages. At where you are now I would install a gpu diagnostic tool and take a look at what the gpu is doing. I use RenderDoc myself as it supports the later APIs but unfortunately it doesnt work with <gl3.2. With a gpu debugger you can get rid of all the guess work and examine the complete pipeline state at various stages of rendering. Googling 'opengl gpu debugger' brings up some potential tools that you could use. Ati/Amd and Nvidia do their own tools too.

Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 14:31:44
That would be a solution if that issue was existent on my computer too - but I cannot convince a normal end-user to install a tool as described. So solutions are, to write a programm which does similar things but allows eg. to use the same texture but pre-resized to power-of-two or to use hardcoded coordinates for sprites and the likes.


This is why I am asking for further "sources" of this problem - what else could create that "bugged" behaviour?






bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 14:39:07
Yep, I only meant for you to install the tool.
It may still show up the problem even if the final image gets compensated somehow - you will see the values sent from the cpu to gpu - so you can compare these. You can debug single vertices and single pixels all the way from entering the pipeline to being rastered to the render target. If anything looks little off if will be a direction to investigate further - as opposed to having no clue what so ever.

I'll have a go at installing one and see if anything shows up...

Can you create a working sample code with an image of how you're doing things for me to test?
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 15:06:14
@ Sample


Either download my game (so you can at least have a nice little play afterwards :-)). The issue if apparent would be already visible in the start screen (GUI elements).


Also you could use the DIG-framework and the GUI-Sample (as it uses gui widgets...[sic!]) or the demo-app, doing similar stuff.




Regarding a specific application:
- so drawing the spritesheet
- drawing eg. a gui button
?


What platform should I compile for? Linux32, Linux64(with NG), Mac32 or Windows32 ?




bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 15:31:51
Ok - so instead of sending you a binary (with unknown content ;-)):

- Download my Dig-Framework (contains the GUI stuff etc) and extract it:
https://github.com/GWRon/Dig (https://github.com/GWRon/Dig)

- Copy this code into a new file and save it as "DIG/samples/gui/spritetest.bmx":

SuperStrict

'keep it small
Framework BRL.standardIO
Import "../../base.framework.graphicalapp.bmx"
Import "../../base.util.registry.bitmapfontloader.bmx"
Import "../../base.gfx.gui.button.bmx"


Global MyApp:TMyApp = New TMyApp
MyApp.debugLevel = 1


'kickoff
MyApp.SetTitle("Sprite Test")
MyApp.Run()
End




'-------------------

Type TMyApp Extends TGraphicalApp
   Field mouseCursorState:Int = 0
   Field guiSystemState:TLowerString = TLowerString.Create("SYSTEM")


   Method Prepare:Int()
      Super.Prepare()

      Local gm:TGraphicsManager = TGraphicsManager.GetInstance()
      'manually set renderer
      'gm.SetRenderer(TGraphicsManager.RENDERER_OPENGL)
      'gm.SetRenderer(TGraphicsManager.RENDERER_DIRECTX9)
      'gm.SetRenderer(TGraphicsManager.RENDERER_DIRECTX11)
   
      'scale everything from 800x600 to 1024x768
      'gm.SetResolution(1024, 768)
      'gm.SetDesignedResolution(800, 600)
      'gm.InitGraphics()

      '30 logic updates, unlimited FPS
      GetDeltatimer().Init(30, -1)
      GetGraphicsManager().SetVsync(False)
      GetGraphicsManager().SetResolution(800,600)

      'we use a full screen background - so no cls needed
      autoCls = True

      '=== LOAD RESOURCES ===
      Local registryLoader:TRegistryLoader = New TRegistryLoader
      'afterwards we can display background images and cursors
      '"TRUE" indicates that the content has to get loaded immediately
      registryLoader.LoadFromXML("res/config/startup.xml", True)
      'load this "deferred"
      registryLoader.LoadFromXML("res/config/resources.xml")

      '=== CREATE DEMO SCREENS ===
      GetScreenManager().Set(New TTestScreen.Init("page1"))
      GetScreenManager().SetCurrent( GetScreenManager().Get("page1") )
   End Method


   Method Update:Int()
      'fetch and cache mouse and keyboard states for this cycle
      GUIManager.StartUpdates()

      '=== UPDATE GUI ===
      'system wide gui elements
      GuiManager.Update( guiSystemState )

      'run parental update (screen handling)
      Super.Update()

      'check if new resources have to get loaded
      TRegistryUnloadedResourceCollection.GetInstance().Update()

      'reset modal window states
      GUIManager.EndUpdates()
   End Method


   Method Render:Int()
      Super.Render()
   End Method


   Method RenderContent:Int()
      '=== RENDER GUI ===
      'system wide gui elements
      GuiManager.Draw( guiSystemState )
   End Method


   Method RenderLoadingResourcesInformation:Int()
      'do nothing if there is nothing to load
      If TRegistryUnloadedResourceCollection.GetInstance().FinishedLoading() Then Return True

      'reduce instance requests
      Local RURC:TRegistryUnloadedResourceCollection = TRegistryUnloadedResourceCollection.GetInstance()

      SetAlpha 0.2
      SetColor 50,0,0
      DrawRect(0, GraphicsHeight() - 20, GraphicsWidth(), 20)
      SetAlpha 1.0
      SetColor 255,255,255
      DrawText("Loading: "+RURC.loadedCount+"/"+RURC.toLoadCount+"  "+String(RURC.loadedLog.Last()), 0, 580)
   End Method


   Method RenderHUD:Int()
      '=== DRAW RESOURCEL LOADING INFORMATION ===
      'if there is a resource loading currently - display information
      RenderLoadingResourcesInformation()

      '=== DRAW MOUSE CURSOR ===
      GetSpriteFromRegistry("gfx_mousecursor"+mouseCursorState).Draw(MouseManager.x, MouseManager.y, 0)
   End Method
End Type






Type TTestScreen Extends TScreen
   Field guiState:TLowerString


   Method Setup:Int()
      GuiManager.SetDefaultFont( GetBitmapFontManager().Get("Default", 14) )
      'buttons get a bold font
      TGUIButton.SetTypeFont( GetBitmapFontManager().Get("Default", 14, BOLDFONT) )
     
      Local guiRowX1:Int = 70

      Local button:TGUIButton = New TGUIButton.Create(New TVec2D.Init(guiRowX1, 20), New TVec2D.Init(130,-1), "Sample Button", Self.GetName())
      button.SetTooltip( New TGUITooltipBase.Initialize("A button click", "Clicking on a button might lead to a longer life...might!", New TRectangle.Init(0,0,250,-1)) )

      'register demo click listener - only listen to click events of
      'the "button" created above
      'EventManager.RegisterListenerFunction("guiobject.onclick", onClickMyButton, button)
      'EventManager.RegisterListenerFunction("guiobject.onclick", onClickAGuiElement)
      'EventManager.RegisterListenerFunction("guiobject.onclick", onClickOnAButton, "tguibutton")
   End Method


   Function onClickAGuiElement:Int(triggerEvent:TEventBase)
      Local obj:TGUIObject = TGUIObject(triggerEvent.GetSender())
      Print "a gui element of type "+ obj.GetClassName() + " was clicked"
   End Function


   Function onClickOnAButton:Int(triggerEvent:TEventBase)
      'sender in this case is the gui object
      'cast as button to see if it is a button (or extends from one)
      Local button:TGUIButton = TGuiButton(triggerEvent.GetSender())
      'not interested in other widgets
      If Not button Then Return False

      Local mouseButton:Int = triggerEvent.GetData().GetInt("button")
      Print "a TGUIButton just got clicked with mouse button "+mouseButton
   End Function


   Function onClickMyButton:Int(triggerEvent:TEventBase)
      'sender in this case is the gui object
      'cast as button to see if it is a button (or extends from one)
      Local button:TGUIButton = TGuiButton(triggerEvent.GetSender())
      'not interested in other widgets
      If Not button Then Return False

      Local mouseButton:Int = triggerEvent.GetData().GetInt("button")
      Print "my button just got clicked with mouse button "+mouseButton
   End Function


   Method Update:Int()
      If Not guiState Then guiState = TLowerString.Create(Self.name)
      GuiManager.Update(guiState)
   End Method
   

   Method Render:Int()
      'draw a background
      SetColor(255,255,255)
      GetSpriteFromRegistry("gfx_startscreen").Draw(0,0)

      Local centerY:Int = 5
      GetBitmapFont("default").DrawBlock("Button:", 10, 20 + centerY, 60, 35)
      GetBitmapFont("default").DrawBlock("Spritesheet:", 10, 60 + centerY, 60, 35)
     
      'draw the raw spritesheet
      GetSpritePackFromRegistry("gfx_guiTest").Render( 70, 60 + centerY)


      'draw button and tooltip of hovered button
      If Not guiState Then guiState = TLowerString.Create(Self.name)
      GuiManager.Draw( guiState )
     
   End Method
End Type


Now it could reuse existing code (configuration files) and should do similar stuff than my game (the TScreen-stuff is handled differently in my game but this should not bork stuff).


On my linux computer it looks like this:
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FPTvW5Xk.png&hash=fb8a734c3deafe262c498bcf3a3f69e73b93284a)

But on the users computer the button contained it's left "nine patch marker"-black-line when hovering - and things like the tooltip (hover over the button and wait) would expose an offset of the top-border in its "stretched area".


PS: Interesting side note:
Using custom TTFs fonts via "Freetype" results in heavier (= more "bold") fonts on Windows. I load the fonts into a sprite atlas to render from them (to speed up rendering compared to the single-texture-per-glyph approach of vanilla). On windows my "bold" fonts look bigger than on linux.
But texts on the players "let's play"-videos seem to look unaffected from my "DrawSubImageRect()"-calls (so they are not oddly placed or so). Interesting for us is now: my bitmap class function and especially the texture atlas creation there is using a "power of two"-texture. An indicator of the issue? Or just luck that there is automatic "space between chars" even if I do not pad them manually.


To see how this texture looks: append the following code to the "Method Render:Int()"-portion:



If KeyManager.IsDown(KEY_SPACE)
SetColor 0,0,0
DrawRect(0,0,400,400)
SetColor 255,255,255

GetBitmapFont("default").spriteSet.Render(0,0)
EndIf





bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 15:54:32
Excellent, I'll give a go.

I've downloaded GpuPerfStudio as it supports >=OpenGL 1.0.
It looks like it will be help to verify the numbers.
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 16:03:51
The gui apps don't build with a 'TIntMap' type not found error.
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 16:12:05
Ahh my fault ... I use Bruceys maxmods/brl.mod and maxmods/pub.mod

https://github.com/maxmods/brl.mod (https://github.com/maxmods/brl.mod)
https://github.com/maxmods/pub.mod (https://github.com/maxmods/pub.mod)

The updated mods are needed to get a newer "map.mod", sound fixes, AppSuspend-fixes (for linux...) etc.


bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 16:27:22
No worries :)

Building the modules now.
Does the test app scale the sprite sheet to the exact same size as is used in the game?
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 16:42:35
There is no "scaling" done on the sprites except for "stretching" via DrawSubImageRect(). So yes, it does the same scaling.




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 20:49:26
First off... Note I also DO NOT have the issue on my test unit which uses a Radeon and an Nvidia combo card setup, but I thought I'd check anyway.

As I'm sure you'll appreciate that this kind of thing is time consuming ( but nowhere near as bad as guessing ;) ) but I may have found something that is more than likely of significance.

Some evidential data:
(https://i.imgur.com/Os1jL0D.png)

That is a snap of one of that guys youtube vids that clearly shows multiple errors. Other than the obvious pink line there is also a black line to the left of the 'centre left of the 9patch' texture that has bled 1 pixel from the left. I decided to investigate just that one issue for the time being to see if anything shows up.

Tracing through the millions of api calls ( check out the call number at the bottom of the pic - 1842834 ;D ) for frame 292 I found the point at which that texture is used for that centre left part of a 9patch image.
The following image shows the draw calls to render that one section, which I've highlighted in green to the left, to render the part of the image highlighted in red to the right..

(https://i.imgur.com/NdWLEqn.png)

Doing the math...
The image is 512 x 512. The UVs for the top left of the texture are 0.423828, 0.0917969 and the bottom right are 0.435547, 0.134766 - this gives texture image coords of 216.999936, 47.0000128 to 223.000064, 69.000192. The section of the texture image that is highlighted in red are pixel positions 217,47 to 223, 68 ( note that this also highlights a plausible reason for the pink lines to the left and bottom of some sprites in the first image ). This particular section of the sprite is then drawn to the currently set framebuffer, at 46,171 with a width of 6 and height 16, ie 40, 171 to 46, 187.

EDIT: The coordinates shown in the window that shows the texture image are from where the mouse is when I took the snap shot - you can verify the real coordinates in the real image used in the game.

You can see a potential issue here with texture coords. Most gpus will sample from the centre of the closest texel and you have... x = 216 in one call and y = 47 in another. So I'd then ask Ok, why does it work on most gpus and only some have the issue... to that I'd hazard a wild guess that it would have something to do with rendering smaller than what you're sampling? I don't know, and my guess is more than likely wildly wrong :P

Next it would be good to know how texture sampling is setup. You use glTexParameter to do that. Searching back up the call chain ( over 800,000 calls before ) we find this ( which would be the same for all textures used in BlitzMax Max2D ) :

(https://i.imgur.com/Km1Bpkk.png)

Looking through the OpenGL docs at https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml (https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexParameter.xml)  the paramerter of GL_TEXTURE_MAG_FILTER with a value of GL_LINEAR will return a weighted average of the closest 4 pixels to the center of the required pixel.

A quick solution... leave a gap around all of your sprites in the atlas.

Hope it helps :)
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 21:55:55
Wow - that is pretty detailled. I hope you had a bit of "fun" crawling through this stuff (read "everything has to be done at least one time in a life").

To keep the reply short: you suggested to leave 1 pixel "free" in all directions.


1) how to handle nine patch then?
Should I always have a "blank line" between the markers and the content? Think this is counter-intuitive and incompatible to the basic design of it on android.


2) how does "weighted average of the closest 4 pixels to the center of the required pixel." help for non-scaled images?
As I understood the docs you linked for "glTexParameter" the 4 pixels are only taken into consideration when doing a "scale". Means it only happens when doing the stretched draw of the "stretchable" sprite portions (eg. center of buttons).

How does that explain the "pink border" on non-scaled sprites (like the overlay-icons of the player and antenna-symbol in the first screen of your post).

But yes, it explains the black border seen in the "chat background" (on the left side) - leading to question 1) again.


3) Doing a "leaving a pixel border blank" leads to "visual offsets"
If you add a blank pixel on top/bottom/left/right of the sprite: only the left, right, top and bottom of a nine patch sprite will "benefit" from it. All other "sub-portions" (like the center) will "blend" into the outer parts when this magic "4 pixels average" is calculated - nothing bad for my GFX but when doing some pixel-art-stuff (fine pixel-curves) it will break things visually.

Also it is an issue if the unstretched part is using the original color while the stretched one mixes in the blank lines (I assume so at least) which leads to shallow/faded-out colors (1.0*srcRGBA + 1.0*blank)/2.

Wouldn't it be better to disable the used TexParameter so it does the same on all computers this specific app is run on? like disabling "smoothing on scaling" for certain textures.


----
Edit: Might it help to disable "mipmapping" for these images? Eg. the GUI-spritesheet is loaded with "FILTEREDIMAGE | MIPMAPPED" (via "config/gui.xml").


Stated there: https://gamedev.stackexchange.com/questions/46963/how-to-avoid-texture-bleeding-in-a-texture-atlas
One user wrote that mipmap is not useful for 2D - and suggested to offset the pixels on the texture by "0.5" - he calls it "half pixel correction".


Thoughts on this?
----

Again: big thanks for the time you already spend on "digging".

bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 22:11:59
Yeah I found it fun  ;) I don't mind when you can use the tools to find the problem hehe.
GpuPerfStudio actually kept crashing so I ended up using ApiTrace - super invaluable tools for gpu stuff !!! They are as essential to debugging gpus as a regular debugger is to debugging your cpu code.

As I say I use RenderDoc and it's way more detailed than the app that I used here.

For the solution.. hehe that's your job!! :P I just helped out with finding the problem  ;)

Kidding.
You could try disabling mip maps and also setting the value to GL_NEAREST to see if it works? It may make it worse in which case fixing the UVs would then be best.
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 22:16:38
The 0.5 offset is a misnomer. There's something wrong with the viewport, render target size, view projection matrix, pixel position math - any or all of them - if you need the 0.5 offset. You should be rendering at X to X + width in pixels to be accurate. If the calculation both sides of the image are coming from a scale then you can end up with wrong values due to rounding errors.

By all means try the 0.5 offset - I mean if it works then all is good yes?
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 22:24:27
I will ask the user to remove that filter-lines from the XML so the image is loaded "non-mipmapped". But imho this should only solve "bleeding" (like faded out borders). Cannot imagine how this introduces the "black lines" (of the ninepatch sprites) when drawing the sprite unscaled (so no "gl_linear" should be ever be of interest).

Edit: I saw some "errors" (in that video) in sprites drawn in the mainscreen too (talking about plants in the building - or the human beings on the bottom right of the interface). They are already loaded without "mipmapping" (FILTEREDIMAGE,MASKEDIMAGE,DYNAMICIMAGE for the plants, and "0" for the humans). So especially for the "humans" it is just drawing them "plain" without stretching or scaling - and still they get this pink border (In that case I know that a "blank border" will help to get rid of the line


But most important is: How does it explain the "offset" (not talking about bleeding - which would result eg. in a mix of "pink border + actual pixel color") ?




@ Tools
I already did not get over with Valgrind and the likes. Glad to get "gdb" running so I can hand over some backtrace to Brucey if things are borked up with the NG compiler results :p
Digging in "logs" is something I do not like at all - last time I did that was for analyzing some wireshark traffic logs.




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 22:28:41
QuoteHow does that explain the "pink border" on non-scaled sprites
If you look at the UV coords in the samples gpu calls they are 1 off to the left and 1 down.

Yeah digging through logs is boring. These are visual tools so it's a bit easier to see whats going on.
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 22:33:54
I edited my previous post meanwhile, so you might have missed the "edit:" part.


@ 1 pixel offset
But where does it come from? And if you already saw this offset in your run, why did it not lead to artifacts/bleeding on your computer? I am talking about sprites which end right before a pink border, not sprites with a bit of blank space until a pink border comes.

If there was a mistake in the calculation of sub-image-coordinates I thought of seeing it on my computer too.




Edit:
Quote from: col
Doing the math...
The image is 512 x 512. The UVs for the top left of the texture are 0.423828, 0.0917969 and the bottom right are 0.435547, 0.134766 - this gives texture image coords of 216.999936, 47.0000128 to 223.000064, 69.000192. The section of the texture image that is highlighted in red are pixel positions 217,47 to 223, 68 ( note that this also highlights a plausible reason for the pink lines to the left and bottom of some sprites in the first image ). This particular section of the sprite is then drawn to the currently set framebuffer, at 46,171 with a width of 6 and height 16, ie 40, 171 to 46, 187.
If this is the reason for the offset I seem to not have understood that paragraph correctly. What is wrong with the calculated values (when rounding them to integers, not floats)? It seems to correctly describe the portion on the spritesheet (especially when seeing your rect-rectangle - it is not offset or so).

bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 22:47:20
QuoteIf there was a mistake in the calculation of sub-image-coordinates I thought of seeing it on my computer too.

I know, I know, I would have thought that I'd see it ad well. I remember that I had a very similar issue with the d3d11 driver once - on some, but not all, ati cards the image would be 1 pixel off in both directions. It didn't affect Nvidia cards at all. Who knows what tricks go on in the circuitry to make it all look ok. But at the end of the day... the numbers are there.

I used ApiTrace from https://apitrace.github.io/ (https://apitrace.github.io/). It may be worth you downloading and taking a look for yourself - its very easy to use.
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 22:51:13
QuoteWhat is wrong with the calculated values (when rounding them to integers, not floats)?
Gpus will round to the nearest centre, ie the nearest 0.5, not to the nearest 0.0.

216.999936 != 217.5          - gpu will use 216.5, 1 pixel to the left
47.0000128 == 47.5
223.000064 == 223.5
69.000192 != 68.5          - gpu will use 69.5, 1 pixel down

You intend to render the pixels from
217,47 to 223, 68

but using the UVs given to it the gpu will use coords of
216.5, 47.5 to 223.5, 69.5

ie 216, 47 to 223, 69.

Hence the reason people add 0.5 to the coords.. to fix a kludge somewhere else. Gpus already do the 0.5 thing internally.
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 23:00:15
Ah, so the "216.9999936" should normally be already something "near" 217.5 or so?


Already installed the tool - clicking on something always needs a "replay" until it reaches that point in the trace. A bit tedius to find the right spot :-)
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FiXJzGrs.png&hash=6c988f6ad9070e3b3ed9ac259a07cef2e5949b4c)


Shouldn't the "sample" code (doing just a button + drawing the spritesheet) be faster to debug?
Think I need to catch some sleep before even trying to fix that as I am not knowing the "correct value" now - even if you wrote that already in the previous posts...
(But I guess i will still try instead of sleeping)




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 15, 2017, 23:06:56
QuoteAlready installed the tool
Good man ;D

Yes, they all replay but that one replays quite slow, maybe for accuracy - and that's more important. Others can be very fast in replaying.

As I said before it can be slow work and it involves manual math too  :P it's definitively a process that you don't want to rush and when you set everything up correctly you can be confident that it will work correctly on everyones pc.

I'd catch some zzz's and do it tomorrow with a fresh mind and fresh perspective ;)
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 23:39:11
Ok, then a last question until I will have a look on how my pillow feels :p

Quote from: colThe UVs for the top left of the texture are 0.423828, 0.0917969 and the bottom right are 0.435547, 0.134766 - this gives texture image coords of 216.999936, 47.0000128 to 223.000064, 69.000192

Ok, so the coords "216.9999" are incorrect as they _may_ lead to use 216.5 instead of 217.5 ?
If so - the calculation of the subimage-coordinate is resulting in ~217:

This is what I pass (using a "print-debug" in my sample file just drawing the input-widgets "left border"):
DrawSubImageRect(parent.image, 70.0000000, 26.0000000, 6.00000000, 16.0000000, 217.000000, 47.0000000, 6.00000000, 22.0000000)

So the "217" is there. I assume I am not supposed to manually add the 0.5 here - but couldn't this help (similar to the "int(float + 0.5)" rounding mechanism) ?
I mean, if I add 0.5 to 217 it might result in either "216.99999 + 0.49999 = 217.499998" or "217.00001 + 0.500001 = 217.500002" which is in both cases "near" 217.5 instead of 216.5
BUT ... if that was the solution to use, shouldn't  this correction be in max2D (drawsubimagerect()) rather than my code?
Ok, so I added that 0.5 to the sourceX and sourceY and ... results were weird as I then saw black borders in other portions (of course, as I now "added" the right-side-ninepatch-borders as it seems that my GPU "rounds up" instead of "down" as the let's players gpu does).
Adding "0.1" resulted in a correct display here. Might it be that the users GPU/driver (a nvidia 650GT if I remember correctly) truncates these values - so eg. "216.9999" becomes 216 - and so it uses "216.5"? In that case the "216.99999 + 0.1" leads to "217" and then "217.5". On our "normal" computer setups it should still work as "0.1" does not round to the next texel, I think.



Thanks a lot for your assistance.

bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 15, 2017, 23:54:08
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
Title: Re: Texture bleeding
Post by: IanMartin on October 16, 2017, 02:34:28
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 :)
Title: Re: Texture bleeding
Post by: Derron on October 16, 2017, 08:39:06
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
Title: Re: Texture bleeding
Post by: col on October 16, 2017, 11:20:25
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.
Title: Re: Texture bleeding
Post by: Derron on October 16, 2017, 11:33:30
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
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 06:52:17
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
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 08:25:53

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
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 09:42:13
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  ;)
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 10:15:47
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
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 11:45:10
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.
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 12:01:43
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
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 12:16:00
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?
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 12:30:58
@ 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
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 12:52:18
To make things more clear:

(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FCRxdRie.png&hash=a62394446219d508f3675f3452761e81ee64245b)


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
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 14:32:06
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.

Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 14:49:05
So you pointing me to "x1=0 and x2=22" means 23 pixels, not 22 pixels as in the difference. Means one should not do "from X1 to X1+Width" in that case?


(not able to try it out now - outdoor fun with sun is starting _now_)




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 17, 2017, 15:28:19
It certainly looks that way in this case.

For testing I'd turn off the linear sampling and have a go in DrawSubImageRect or make my own for testing that doesn't involve the DrawImage command.

Also you could write your own UVs mapping to map the pixel positions from 0.5/dimension to 1-0.5/dimension, remembering that its the edges of your smaller image that you want to map to .5 texel positions from the larger image. This should guarantee that the edge of your smaller image UVs are lined up to the dead centres of the larger image texels. You would probably keep the filtering on if you do that.

Another idea would be to position the smaller 'tiles' at positions in the atlas so that the math doesn't produce numbers such as 216.999916?
Title: Re: Texture bleeding
Post by: Derron on October 17, 2017, 20:49:55
These numbers like "216.9999997" are based on floating point inaccuracy. So even if I pass them as int (217) they might get passed to the next call as "216.999997"

This is a problem I see with the 0-1.0 way of OpenGL as a "integer position" cannot be passed that way (it is always inaccurate).


@ custom uv mapping
This is something I would not like to tackle. I am ok with coding custom "atlas stuff" or so, but as soon as it opens a can of new "knowledge areas" I am afraid of not catching on to it. It is one of the reasons I use BlitzMax with a premade "Max2D-engine" rather than C++ with an own engine.


@ linear sampling
Yes, this is on my plan already (still have to work on other areas, so for now it is "todo").


bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 18, 2017, 20:48:04
Just saw another video of the guy ("Part 11") - and there he went into the settings. He is playing it with a virtual resolution of "1600x900" (game is designed for 800x600). So Blitzmax is scaling it up to that resolution (and adding the black borders left and right). As it scales up it might explain a bit why things might differ on there compared to our results... ?




bye
Ron
Title: Re: Texture bleeding
Post by: col on October 19, 2017, 10:29:12
Hmm,
I think that would be a long shot, although... anywhere where there is scaling can introduce an issue if its not accurate. Its worth doing some tests for that for the sake of ruling it out.

What I meant with 'position the smaller 'tiles' at positions in the atlas so that the math doesn't produce numbers such as 216.999916' is placing tiles at positions that will divide more evenly with the atlas dimensions. For example working with numbers such as finding the normalised position ( 0.0 to 1.0 ) of 217 within 512 will could introduce the 'possibility' of rounding errors when combined with an already scaled screen. I mean it shouldn't right? But there is something wrong. These are just suggestions to try for testing only.

Going back to basics... is it worth installing older drivers to see if the problem shows up?

I just googled for some more information and NVidia did a paper on texture atlassing.
It may be worth studying it, together with the debug tools it may help shed more light on things:
http://download.nvidia.com/developer/NVTextureSuite/Atlas_Tools/Texture_Atlas_Whitepaper.pdf (http://download.nvidia.com/developer/NVTextureSuite/Atlas_Tools/Texture_Atlas_Whitepaper.pdf)
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 11:11:58
Thanks for the link to the paper.

Especially the "Using Mip-Maps with Atlases"-part is interesting. They suggest to have the "sub-images" to be power-of-two too! Maybe this is also to avoid rounding problems (as except for "1x1" all dimensions would be "even".

Why isn't this technique used by the various texture packers around? They just optimize the placement to occupy as less space as possible.
I assume it is because they do not generate mipmaps - and therefor also the aspect of "larger textures in the atlas have define the maximum mipmap level" is not important.

This might be of interest for us too:
Quote
Most applications, however, use texture coordinates ranging from zero to one, inclusive, nonetheless. While such coordinates actually wrap, clamp, or mirror a texture by exactly one texel, the benefit of being texture-dimension independent outweighs the slight image-quality reduction. Because texture coordinates in the inclusive [0, 1] interval thus address an area larger than the actual texture, directly re-mapping these coordinates to atlas coordinates also accesses an area larger then the texture's assigned sub-rectangle. The Atlas Creation Tool [CreationTool2004] offers several solutions to this problem.

But wouldn't that - if it was done that way in Max2D/BlitzMax - be a little bug? I mean it would access a 512x512 texture as if it was 513x513 (or so ?) leading to calculation "offsets" (adding a bit from left/top of a texture). It slightly reads as if it was connected to the "x+width=wrongValue" thing we just wrote about some posts ago.
 
bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 11:30:22
I am not sure whether I was able to replicate it...


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"
Import "../../base.util.virtualgraphics.bmx"

SetGraphicsDriver GLMax2DDriver()
TVirtualGfx.getInstance().Init()
Local g:TGraphics = Graphics(1000, 750, 0, 60, 0 )
TVirtualGfx.getInstance().SetVirtualGraphics(800,600)

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()


What I do is to create a bigger application window and let it scale the 800x600 via "TVirtualGFX" (think the base of this was posted somewhen on the blitzmax.com website).
When having a window of "1200x900" (so scale of 1.5) I did not see any changes (except pixelation) but when running it with "1000x750" (so *1.25) I got a pink bar...

1000x750
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FV8usTdU.png&hash=55c19e7ec144e4fe00e03b6988a404ec5a6b602f)

1200x900
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2F1yLzw2W.png&hash=0a6b9e3335568c7e65dac147a2187d0a128da14d)

Would this be of use (regarding debugging) or did I mix things up?

In my game I use a similar approach and allow "windowed" but with a "screen resolution" (so you decide how big the window of the game gets). The Let's player used (if I remember correctly - seeing it once in his videos) that he has used a height of 900 - maybe this "scrambles" it on his computer ?


When running my "gui sample" in his resolution (had to search his videos ... when he went to the main menu to re-enable the music he found "annoying" at the beginning ;-)) I get a similar result: so 1600x900 is doing this here:


(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FO8bCK5d.png&hash=887a36c1fecfbd60018b71a77e3686a25a021f72)
Seems I finally got something to fix without relying on his computer.


bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 13:33:33
So I tried to adjust the "source rectangles" a bit.



DrawSubImageRect(parent.image,..
          x,..
          y,..
          area.GetW(),..
          area.GetH(),..
          area.GetX()+0.1,..
          area.GetY(),..
          area.GetW(),..
          area.GetH(),..
          offsetX,..
          offsetY,..
          0)


And it removed the black line on the left (of that "arrow left"-button) - but I only was able to do this for the "none-nine-patch-sprites". For nine-patch I tried the opposite - and subtracted 0.1 from "y2" (y+height-0.1). It worked there too.

So I assume it has to do with the "rounding" into another texel. Dunno how to avoid that, means, how to pass some kind of "integer" defining an exact pixel coordinate in the texture.


bye
Ron
Title: Re: Texture bleeding
Post by: col on October 19, 2017, 14:28:00
QuoteWhy isn't this technique used by the various texture packers around? They just optimize the placement to occupy as less space as possible.
A texture packer will all store the correct UVs though yes? as opposed to doing a calculation to get them. I would think that the new UV will be dead centre of the texel for edges of the smaller tile.

QuoteWould this be of use (regarding debugging) or did I mix things up?
Aside from seeing the artifact you would also see the uv numbers that are being sampled. More information is better ;)

Phew! It's much better that you can reproduce the issue. That alone can make the whole debug process so much easier. It can be a painstakingly slow process I know.
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 16:30:31
I found the culprit (partially, maybe ;-)...

max2d.mod/max2d.bmx: Function SetVirtualResolution( width#,height# )
So the virtual resolution function accepts floats... this, together with the "TVirtualGFX"-Class leads to the experienced inaccuracy.

I always assumed that the resolution-params are "integers" - so I never passed a "rounded" variant to them.

So this means I need to update DIGs "virtual graphics" to properly round down the x/y-offsets so it starts at "nice" coordinates when borders have to get added.
And I while doing, I replace these odd lines:

         ' Now go crazy with trial-and-error... ooh, it works! This tiny bit of code took FOREVER.
         Local pixels:Float = Float (GetInstance().vwidth) / (1.0 / GetInstance().vscale) ' Width after scaling
         Local half_scale:Float = (1.0 / GetInstance().vscale) / 2.0

with

         Local pixels:Int = Int(GetInstance().vwidth * GetInstance().vscale) ' Width after scaling
         Local half_scale:Float = 0.5 / GetInstance().vscale


PS: source of original "TVirtualGFX" is http://www.syntaxbomb.com/index.php?topic=2897.0 (http://www.syntaxbomb.com/index.php?topic=2897.0)

PPS: Even if this were not the source of the issue, then I would really really thank you as I would surely have stopped bug hunting already if there weren't your replies keeping me motivated.

PPPS: Will have to check formulas again, as for now it only fixes "X-axis" while the "stretched" ones are still showing that bottom-pixel. But for now it is family time until this evening is "computer time" again ;-)


bye
Ron
Title: Re: Texture bleeding
Post by: col on October 19, 2017, 16:45:35
Thats awesome! And a great find too.
I'm glad you kept at it as there was clearly a discrepancy in there somewhere, verified by the debug tool and with your own latest findings.

Fingers crossed that it is the problem 8)
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 19:48:21
Hmm, there is something ... interesting...

(https://abload.de/img/viewportrlxf1.gif)


First is the arrow-sprite which does no longer contain the "left side black border".
Second the upper widget: it is positioned at a "even" y-coordinate
Third the widget below: it is located at an "odd" y-coordinate
Fourth: the nine patch which moves on the right side... it shows how position adjusts the "scaled image result" which surely leads to the bottom borders of overlay (person) and widget

BUT ... what the really interesting thing is: the BOTTOM of the whole app. There is a black line... but why?

I use this code (hard coded values for this test... but calculated in the real app):
Code (blitzmax) Select

Local g:TGraphics = Graphics(800, 300, 0, 60, 0 )
SetVirtualResolution( 533, 200 )
SetViewport( 67, 0, 400, 200 )
SetOrigin( 67, 0 )

...
setClsColor ...
cls

But as you see it keeps a "1 pixel black border" at the bottom.


Ok, so this happens the same in this simple (standalone) sample:
Code (blitzmax) Select

SuperStrict

'keep it small
Framework BRL.standardIO
Import Brl.GlMax2D

SetGraphicsDriver GLMax2DDriver()
Local g:TGraphics = Graphics(800, 300, 0, 60, 0 )
SetVirtualResolution( 533, 200 )
SetViewport( 67, 0, 400, 200 )
SetOrigin( 67, 0 )

Repeat
   SetClsColor(255,230,200); Cls
   Flip 0
Until AppTerminate()
end


Is this black line happening for you too? If so, I assume this is an issue with Max2D and not my specific code.


The ApiTrace has this scissor-command there:
(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FdlBrY0E.png&hash=b45f4b28af6daa9cd30c394039aa91c4bca72a1c)
Editing this line (right click - edit) and change it to "100, 0, 600, 300" removes the black line in the following frames...

Digging around WHY this happens - leads me to "max2d.bmx : SetViewPort". There coordinates got always rounded _down_. Leading to the 299.

For now I replaced SetViewPort in max2d.bmx with the following code:
Code (blitzmax) Select

Function SetViewport( x,y,width,height )
   gc.viewport_x=x
   gc.viewport_y=y
   gc.viewport_w=width
   gc.viewport_h=height
   'round coordinates "correctly" to avoid values like 49.9997 to get rounded down
   'as the old floor() did
   Local x0=int( x / gc.vres_mousexscale  +0.5 )
   Local y0=int( y / gc.vres_mouseyscale  +0.5 )
   Local x1=int( (x+width) / gc.vres_mousexscale  +0.5 )
   Local y1=int( (y+height) / gc.vres_mouseyscale  +0.5 )
   _max2dDriver.SetViewport x0,y0,(x1-x0),(y1-y0)
End Function

And the black line is gone. The Error with scaled-up images is still existing. There might be similar issues left.


bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 21:00:58
Ok, now I hope to get your help, as you will surely have a bit more experience regarding matrix modes, viewports...

(https://www.syntaxbomb.com/proxy.php?request=http%3A%2F%2Fi.imgur.com%2FHoaMdH2.png&hash=daa339870d775f27234240f36cd32f96ea27f436)
As you can see the (uv) coordinates are exactly the same for left and right figure. Only difference is the glVertex2f-calls (the "draw positions"). Left one is rendered from "10,10 - 34,33" and the second one from "60,11 - 84,34". 77,10 is there because of the letterbox on the left (and right) side.

I assume that something like the projection matrix (excuse my missing knowledge here) leads to that issue.


To reproduce it on your computer:
- load this image: https://raw.githubusercontent.com/GWRon/Dig/master/samples/__res/gfx/gui.png (https://raw.githubusercontent.com/GWRon/Dig/master/samples/__res/gfx/gui.png)
- copy the following code:


Code (blitzmax) Select

SuperStrict

'keep it small
Framework BRL.standardIO
Import Brl.GlMax2D
Import Brl.PNGLoader

SetGraphicsDriver GLMax2DDriver()
Local g:TGraphics = Graphics(800, 300, 0, 60, 0 )
SetVirtualResolution( 533, 200 )
SetViewport( 67, 0, 400, 200 )
SetOrigin( 67, 0 )
SetAlpha ALPHABLEND

local img:TImage = LoadImage("gui.png", 0)

Repeat
    SetClsColor(255,230,200); Cls

    DrawSubImageRect(img,..
                     10,..  'x
                     10,..  'y
                     24,..      'width
                     23,..      'height
                     349,..     'atlas sprite x
                     1,..       'atlas sprite y
                     24,..      'atlas sprite w
                     23,..      'atlas sprite h
                     0,..       'offset x
                     0,..       'offset y
                     0)

    DrawSubImageRect(img,..
                     60,..  'x
                     11,..  'y
                     24,..      'width
                     23,..      'height
                     349,..     'atlas sprite x
                     1,..       'atlas sprite y
                     24,..      'atlas sprite w
                     23,..      'atlas sprite h
                     0,..       'offset x
                     0,..       'offset y
                     0)
    Flip 0
Until KeyHit(KEY_ESCAPE) or AppTerminate()
end





Hope you can help me to get rid of that nasty bug - again.




bye
Ron
Title: Re: Texture bleeding
Post by: Derron on October 19, 2017, 21:28:44
Ok, further digging:


glmax2d.bmx
Code (blitzmax) Select


   Method ResetGLContext( g:TGraphics )
      Local gw,gh,gd,gr,gf
      g.GetSettings gw,gh,gd,gr,gf
     
      state_blend=0
      state_boundtex=0
      state_texenabled=0
      glDisable GL_TEXTURE_2D
      glMatrixMode GL_PROJECTION
      glLoadIdentity
      glOrtho 0,gw,gh,0,-1,1
      glMatrixMode GL_MODELVIEW
      glLoadIdentity
      glViewport 0,0,gw,gh
   End Method

so it creates an orthographic view... for 800x600 it calls: glOrtho 0,800,600,0,-1,1. Isn't this saying: make an area of 801*601 pixels? Adjusting the command in QApiTrace (so it does "0,799,599,0,-1,1") removes my bars. But somehow I think it is still not perfectly correct and only "hiding" a bug.


Aside of that I found this:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd374282(v=vs.85).aspx (https://msdn.microsoft.com/en-us/library/windows/desktop/dd374282(v=vs.85).aspx)
and
https://www.opengl.org/archives/resources/faq/technical/transformations.htm (https://www.opengl.org/archives/resources/faq/technical/transformations.htm)  ("9.030 How do I draw 2D controls over my 3D rendering?")
and they suggested to use glTranslatef(0.375, 0.375, 0). So I added it to ResetGLContext():

glmax2d.bmx
Code (blitzmax) Select

   Method ResetGLContext( g:TGraphics )
      Local gw,gh,gd,gr,gf
      g.GetSettings gw,gh,gd,gr,gf
     
      state_blend=0
      state_boundtex=0
      state_texenabled=0
      glDisable GL_TEXTURE_2D
      glMatrixMode GL_PROJECTION
      glLoadIdentity
      glOrtho 0,gw,gh,0,-1,1
      glMatrixMode GL_MODELVIEW
      glLoadIdentity
      'make sure that primitives could be placed near the texel centers
      glTranslatef(0.375, 0.375, 0)
      glViewport 0,0,gw,gh
   End Method


Now all "lines" are gone - seems this fixed the bug for me. But I will wait for what you have to say about it.

I also undo'd my changes to the TVirtualGFX code - and still everything was rendered properly now.
I then undo'd my changes to max2d.bmx (the +0.5) and the line at the bottom of the window was back again.


So conclusion - for now - is, that only that glTranslate-call was needed to fix my issues (at least on my computer) and the max2d.bmx fix is "convenience" :-).

Happy? Nope... because adding this command makes all my fonts and sprites look "blurry" ...
So I moved that command from ResetGLContext() into the SetResolution()-function and the fonts/sprites kept being "crisp".


Now everything drawn is offset by 1 pixel (so top and left has 1px border of nothing). Seems there is still something to fix (and/or above thing isn't the right thing to fix the bug).


bye
Ron
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: col on October 19, 2017, 22:11:09
OMG There are scales working from scales that are adjusting to scales then applied to other scales :P

I know its may not be possible, but rendering to a texture that's 1:1 pixels with your original images sizes and then rendering that to a screen size quad may actually be a better solution?

I remember Grey Alien and I talked about it in the past and he is using 'render to texture' to solve alpha transparency issues. I'm not sure how the final image quality comes out - I can't imagine the final image to suffer any quality loss compared to the original.

And, yep I get a black line at the bottom of the display with your latest example.

QuoteglOrtho 0,800,600,0,-1,
From MS I read that as if it means that the 800 and 600 are the *dimensions* that the normalized device coordinates ( -1 to +1 ) will be scaled to and not neccessariy that coords go from 0 to 800 inclusive.

From OpenGL it does read as you suggest  ???
gluOrtho2D (http://gluortho2d)
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: Derron on October 19, 2017, 22:17:46
I hoped for a more enthusiastic reply :p


Think the "glTranslatef()"-thing creates other problems as "0,0" will no longer be 0,0 then. So I am not sure if that is the right direction for a fix.


Rendering to an image (to avoid that lines?) smells a bit like "avoidance". Aren't we coders? Coders to solve problems, not to avoid them (I know... time is money...).




bye
Ron
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: col on October 19, 2017, 22:29:22
QuoteI hoped for a more enthusiastic reply :p
I edited my last reply a tiny bit.

QuoteAren't we coders? Coders to solve problems
Haha, and rendering to a texture does exactly that yes?  :P
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: col on October 19, 2017, 22:36:26
As for solving a problem... hmm the problem was/is/maybe caused by so many scalings going on that it would quite difficult to get them 100% correct using the limited precision of floating point math. To solve that would be to remove it from the equation and start again. Kind of where I was heading with my suggestion of writing your own DrawSubImageRect, but it may even need to go further - as you've discovered.

I'm all in for high quality and striving for high standards, but working with heavy chains ( scales on scales etc ) is difficult for me to find attractive :P
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: Derron on October 19, 2017, 22:40:35
Hmm, rendering to a texture is less "cross platform" for now (SDL and NG) while this is an OGL-specific thing.
(as said on github: once r2t works properly I would be glad to use it in many situations as it speeds up rendering and eases a lot of pain).


@ scaling of scaling of ...
Yes it is a bit annoying, which is why I tend to accept my "whacky hacky fix" below to make it work. One might consider adding a "EnableGLCorrection()" function so one could enable that only when using scales/VirtualResolution<>RealResolution.
(On the other hand: the coordinates in the apitrace seem to look ok, so doesn't this say that there might be issues with the orthoview, matrix, viewports, ...whatever?)


For now I adjusted my code in glmax2d.bmx to this:
Code (blitzmax) Select

   Method ResetGLContext( g:TGraphics )
      Local gw,gh,gd,gr,gf
      g.GetSettings gw,gh,gd,gr,gf
     
      state_blend=0
      state_boundtex=0
      state_texenabled=0
      glDisable GL_TEXTURE_2D
      glMatrixMode GL_PROJECTION
      glLoadIdentity
      glOrtho 0,gw,gh,0,-1,1
      glMatrixMode GL_MODELVIEW
      glLoadIdentity
      'make sure that primitives on x=49.99 do land on x=50
      glTranslatef(0.01, 0.01, 0)
      glViewport 0,0,gw,gh
   End Method


As this seems to solve issues here this seems there is an rounding issue happening within my gpu-driver-ogl-combination. That "0.01" is so small that the "blurriness" of my sprites is neglictable (alpha-mixed colors vary by less than 1 percent). It's not the best thing but at least it "seems to do the job" (dunno if that is true on other computers too).


bye
Ron
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: col on October 19, 2017, 22:50:02
I think you'll remove the blurriness by changing the pixel sampling mode from LINEAR to NEAREST. I'm not sure the final image would look though.
Title: Re: Texture bleeding - Blitzmax bug with virtual resolution
Post by: Derron on October 19, 2017, 23:18:55
Isn't linear only used when "FILTEREDIMAGE" is used?

Code (blitzmax) Select

   If flags & FILTEREDIMAGE
      glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR
      If flags & MIPMAPPEDIMAGE
         glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR
      Else
         glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR
      EndIf
   Else
      glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST
      If flags & MIPMAPPEDIMAGE
         glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST
      Else
         glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST
      EndIf
   EndIf


So in my (test-)case it should always be correct except for certain fonts. I tried to see differences (removing the corresponding image flags) but it did not change something "visually". I then replaced the _LINEAR parts above with nearest (so ignoring image flags at all...) and text looked crisp again. This then made me find another little "bug" (= relying on default flags) - I create my bitmapfont-"all glyphs"-image without a given flag ("loadimage(pixmap)") which means it loads it with default flags. Passing a ", 0" lead to crisp fonts without the "_LINEAR" replacement in glmax2d.bmx.
BUT ... after doing so, the texts wont look good when scaled (...of course!) and this means they do not look nice when using virtual resolution. So that is no solution at all.


bye
Ron