Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader isampler2D and usampler2D incorrect encoding #69880

Closed
hw762 opened this issue Dec 10, 2022 · 3 comments
Closed

Shader isampler2D and usampler2D incorrect encoding #69880

hw762 opened this issue Dec 10, 2022 · 3 comments

Comments

@hw762
Copy link

hw762 commented Dec 10, 2022

Godot version

3.5 stable, 4.0 beta8

System information

Linux Vulkan, GLES3

Issue description

Similar to this issue: https://stackoverflow.com/questions/67479596/cant-read-a-format-r8-uniform-sampler2d-texture-correctly-from-within-a-shader

isampler2D and usampler2D is supposed to bind uniforms in integer format, but the actual format is float.

Expected behavior:

If an input image has format RGB8, expect shader using uniform usampler2D to read each channel with range 0-255 integers.

Actual behavior:

Value read is encoded as normalized 32-bit float with range 0.0-1.0 but read as integers, producing nonsense values. This behavior is identical to sampler2D (with the unnecessary normalization and encoding), and incorrect decoding.

Hack to work around:

Use uintBitsToFloat then multiply by 255. This does not guarantee precision.

Steps to reproduce

Check the reproduction project, written for Godot 4.0 beta 8. Similar things happen to Godot 3.5 or possibly earlier.

  1. Open Test.tscn
  2. Notice Test.gd initializes texture to R8 format, filled 127
  3. Notice Test.gdshader reads isampler2D
  4. The "intended" use reads nonsese
  5. The "hacked" workaround reads 0.5

Minimal reproduction project

sampler_bug.zip

@clayjohn
Copy link
Member

What are you doing to create the texture? Samplers tell the shader how to read data, they don't tell the engine how the data is stored, you do that when you create the image.

@hw762
Copy link
Author

hw762 commented Dec 11, 2022

I do this, as in the reproduction project:

func _ready():
	var texture_data = PackedByteArray()
	texture_data.resize(1024 * 600)
	texture_data.fill(127)
	var test_image = Image.create_from_data(1024, 600, false, Image.FORMAT_R8, texture_data)
	var test_texture = ImageTexture.create_from_image(test_image)
	material.set_shader_parameter("tex", test_texture)

This means I am filling the image with the integer value 127. I expect to read 127 in the shader. However, the shader encodes the values in the following incorrect step:

  1. Input texture is cast to 4-channel 32-bit float and normalized to [0.0, 1.0] (i.e. 127 -> [0.5, 0, 0, 1])
  2. Texture is bound to shader
  3. Shader samples texture as float (i.e. [0.5, 0, 0, 1])
  4. Shader reads the sampled value, in float binary encoding but with integer type (i.e. like doing *(int *) ((float *)&x) in C).

Sorry, I don't find a way to express it better in English, but this is what the shader should do (one possibility):

  1. Cast input texture to 4-channel 32-bit integers
  2. Bind texture to shader
  3. Sample texture as integer
  4. Read sampled value, already in integer encoding and with integer type

The value should stay 127 for the whole process and should not even be converted to float in the first place

@Zylann
Copy link
Contributor

Zylann commented Dec 11, 2022

Duplicate of #57841 ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants