Canvas Renderer

Canvas Renderer

202k Downloads

WTF (Resource Reload)

grondag opened this issue · 2 comments

commented

BOUNTY: This issue currently has a $50 USD bounty for someone who can identify the root cause and a proper fix.

UPDATE: I'm almost certain PR #96 will close this issue and claim the bounty. I won't be able to confirm that until later today, but if you're still in the hunt I recommend stopping.

In addition to the monetary reward, the person fixing this bug will receive:

  • Bragging rights
  • Credit in the source code
  • A favor from Grondag

A favor means you can ask me - once - to do something for you, like add a feature to a mod or help with a coding problem you have. If I feel the request is a reasonable exchange for your help in fixing this defect, and is also legal, ethical, safe and within my practical means then I will try to do the thing. If I can't agree do to the favor then I will suggest alternatives. If we can't agree on that request I still owe you a favor.

Here is the issue...

Canvas terrain shaders receive normalized (0-1) texture coordinates because that is better for shader authors and will allow the coordinates to be reused on different maps (normal, rough, metal, height, etc.). The block textures (sprites) are still packed on a sprite atlas texture so the shader needs a way to translate those normalized coordinates to sprite atlas coordinates.

This is done via an information texture that is accessed in the vertex shader.

Here is the primary class that creates and manages the sprite information texture: https://github.com/grondag/canvas/blob/one/src/main/java/grondag/canvas/texture/SpriteInfoTexture.java

Here is the shader code that uses the texture to look up sprite atlas coordinates: https://github.com/grondag/canvas/blob/256e84d47c56f25055bad56333f1c3d1e11102c1/src/main/resources/assets/canvas/shaders/internal/vanilla/vanilla.vert#L44

After resource reload, the sprite IDs used in the terrain shader become scrambled, apparently shifting 5 positions towards positive numbers. So, for example, if white concrete is assigned a sprite ID of 760, after resource reload it will still be assigned the same spriteID but will render with the sprite having ID 765, which may be a clock, torch, dirt, etc.

Once the IDs become scrambled, they remain that way. The scrambling appears to be deterministic - the same on subsequent runs. (This was eyeballed, not tested directly.)

All of the following potential causes have been investigated by the author and none appear to be the culprit:

  • Difference in the terrain shader code - identical before and after resource reload
  • Difference in the sprite / numeric ID mapping - identical before and after resource reload
  • Difference in vertex data sent to shader - identical before and after resource reload
  • Difference in information texture data - identical before and after resource reload (verified by zeroing out the buffer before population, preserving buffer content from first run, and comparing after resource reload)
  • Difference in sampler uniform - it's a static value and the shader and uniform are recreated on resource reload

I also suspected some kind of strange race condition triggered by differences in loading sequence or off-thread activity after resource reload vs initial load. For this, I added lifecycle tracing for certain objects and made sprite map/unmap logic more consistent. I also inserted temporary tracing throughout and around SpriteInfoTexture - all indicated the same sequence of activity and all activity happened on the main render thread. However, a problem of this nature can't be completed excluded.

Some kind of stray GL state seems plausible as a cause, but many GL state elements affecting texture don't apply in shaders. Even so, inserting this code when SpriteInfoTexture is created and running it again before every shader invocation does not help:

	GlStateManager.activeTexture(TextureData.SPRITE_INFO);
	GlStateManager.bindTexture(glId);
	GlStateManager.enableTexture();
	RenderSystem.matrixMode(GL21.GL_TEXTURE);
	RenderSystem.loadIdentity();
	RenderSystem.matrixMode(GL21.GL_MODELVIEW);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_MAX_LEVEL, 0);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_MIN_LOD, 0);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_MAX_LOD, 0);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_LOD_BIAS, 0.0F);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_MIN_FILTER, GL21.GL_NEAREST);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_MAG_FILTER, GL21.GL_NEAREST);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_WRAP_S, GL21.GL_REPEAT);
	GlStateManager.texParameter(GL21.GL_TEXTURE_2D, GL21.GL_TEXTURE_WRAP_T, GL21.GL_REPEAT);
	GlStateManager.activeTexture(TextureData.MC_SPRITE_ATLAS);

Astonishingly, adding this code to the vertex shader, uncommenting after resource reload and hitting F3+A to reload shaders causes the game to render with correct textures on my machine:

#define WTF

...

#ifdef WTF
    vec4 spriteBounds = texture2DLod(frxs_spriteInfo, vec2(0, (in_material.x - 5) / _CV_SPRITE_INFO_TEXTURE_SIZE), 0);
#else
    vec4 spriteBounds = texture2DLod(frxs_spriteInfo, vec2(0, in_material.x / _CV_SPRITE_INFO_TEXTURE_SIZE), 0);
#endif

(I do not consider the above hack an acceptable fix.)

I'm currently out of ideas and patience for this, so I'm leaving it here and pretending it doesn't exist for a while.

commented

have you tried turning it off and on again

commented

have you looked into vanilla's use-after-free issues that cause minecraft to not work properly with mesa?