Donnerstag, 12. November 2015

Bindless texture with LWJGL

The classic bound texture paradigma is so 1990. But getting bindless textures to work is not that easy, especially when you're using something other than C or C++. For all the LWJGL or Java users out there, here are some notes that may help you removing the classic texture pipeline from your engine. I think you can read about the technique in the internet, so I'll keep it mostly short.

First of all, you need a handle (some kind of pointer again, but don't think of it as a pointer, yucks) for a given texture.

 long handle = ARBBindlessTexture.glGetTextureHandleARB(textureID);  

Afterwards, the handle has to be made resident. I think that's something you need for the combination with partially resident textures.

 ARBBindlessTexture.glMakeTextureHandleResidentARB(handle);  

This was the easy part. Now, you have to use the handle in our shaders somehow. The easiest way would be to use it as a uniform.

 ARBBindlessTexture.glUniformHandleui64ARB(location, handle);  

Inside your shader, you have to use the handle. But what datatype should one use? Maybe I missed something elementary, but  there's only one proper way, namely use a datatype made available through an nvidia extension. Since bindless textures are available through an extension as well, here are both calls that you (probably) need:

 #extension GL_NV_gpu_shader5 : enable  
 #extension GL_ARB_bindless_texture : enable  

And now, you can use tha datatype uint64_t. So your uniform would be a uint64_t.

That would work. But most probable, you want to have your data in a uniform or storage buffer, probably together with some other data and datatypes. So here's what I did.

Use a DoubleBuffer (Java native buffer, available via BufferUtils.createDoubleBuffer(int size)) for your data. Since doubles are twice the size of a float, which is 4 byte, we have 8 bytes per texture handle, which is 64 bits, which is the same as a uint64's size, so that's enough. Now one has to take the generated handle's bits and put them into the buffer (for example a ssbo) as they are. This can be done like so:

 GL15.glBufferSubData(GL43.GL_SHADER_STORAGE_BUFFER, offset * primitiveByteSize, values);  
-
Where primitiveByteSize is 8 in this case. Since we use the underlying buffer as a DoubleBuffer, we have to provide a double value for the handle (or use it as a byte buffer, but nevertheless we need the correct bits or bytes). You can convert a Java long to and from a double like this:

 Double.longBitsToDouble(longValue)  
 Double.doubleToLongBits(doubleValue)  

Afterwars, the shader can take the value as a said uint64_t and cast it to a sampler. Sounds ugly, maybe it is.

 color = texture(sampler2D(uint64_t(material.handleDiffuse)), UV);  

That is the whole story, took me a while to figure it out.

Keine Kommentare:

Kommentar veröffentlichen