Thanks for the heads up, I tried adding glEnable(GL_TEXTURE_2D); after my context was set up and it did not fix the problems I’m having.

I can appreciate that, but my question is more general than my code. Is mipmapping of floating point textures well supported on modern ATI hardware? This article on the wiki states that it is not available for x300 and above, but this article is quite old. I just want to know if the article is still correct.

But I’m happy to share my code and I’ll paste the relevant sections of it below, but with all of the boilerplate I don’t think I can make a minimal example. Full sources are available here.

Implementation

I initialise the floating point buffer like so.

```
{
std::tr1::shared_ptr<magnet::GL::Texture2D>
colorTexture(new magnet::GL::Texture2D);
colorTexture->init(_camera.getWidth(), _camera.getHeight(), GL_RGB16F);
colorTexture->parameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
colorTexture->parameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
colorTexture->genMipmaps(); //Ensure the mipmap chain is built/available for writing into
_luminanceBuffer.init();
_luminanceBuffer.attachTexture(colorTexture, 0);
}
```

I then fill it with luminance data using the following shader

```
void main()
{
vec4 color = texture(colorTex, screenCoord).rgba;
float L = dot(color.rgb, vec3(0.265068, 0.67023428, 0.06409157));
//Prevent negative logarithms
L_out = vec4(log(max(10.0e-8, L)), L, L, 1.0);
}
```

I generate mipmaps using this code

```
//Now we need to generate the mipmaps containing the scene
//average, minimum and maximum luminances
magnet::GL::Texture2D& tex = *_luminanceBuffer.getColorTexture();
GLsizei currentWidth = tex.getWidth();
GLsizei currentHeight = tex.getHeight();
GLint numLevels = tex.calcMipmapLevels();
//Ensure the luminance buffer is both attached and its color
//texture bound
_luminanceBuffer.attach();
tex.bind(0);
//Attach the mipmapping shader
_luminanceMipMapShader.attach();
_luminanceMipMapShader["inputTex"] = 0;
for (int i=1; i < numLevels; ++i)
{
GLsizei oldWidth = currentWidth;
GLsizei oldHeight = currentHeight;
//Halve the size of the textures, ensuring they never drop below 1
currentWidth /= 2; currentWidth += !currentWidth;
currentHeight /= 2; currentHeight += !currentHeight;
_glContext->setViewport(0, 0, currentWidth, currentHeight);
tex.parameter(GL_TEXTURE_BASE_LEVEL, i - 1);
tex.parameter(GL_TEXTURE_MAX_LEVEL, i - 1);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
tex.getGLType(), tex.getGLHandle(), i);
//Now generate the mipmap level using a shader
std::tr1::array<GLfloat, 2> oldInvDimensions = {{1.0 / oldWidth,
1.0 / oldHeight}};
_luminanceMipMapShader["oldInvDimensions"] = oldInvDimensions;
std::tr1::array<GLint,2> oldDimensions = {{oldWidth, oldHeight}};
_luminanceMipMapShader["oldDimensions"] = oldDimensions;
_luminanceMipMapShader.invoke();
}
//Rebind mipmap 0 to the framebuffer
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
tex.getGLType(), tex.getGLHandle(), 0);
_glContext->setViewport(0, 0, tex.getWidth(), tex.getHeight());
tex.parameter(GL_TEXTURE_BASE_LEVEL, 0);
tex.parameter(GL_TEXTURE_MAX_LEVEL, numLevels - 1);
_luminanceMipMapShader.detach();
_luminanceBuffer.detach();
```

And this shader

```
layout (location = 0) out vec4 L_out;
uniform sampler2D inputTex;
uniform ivec2 oldDimensions;
uniform vec2 oldInvDimensions;
uniform float downscale = 2.0;
vec3 data = vec3(0.0);
float divider = 0.0;
void combine(in vec4 sample)
{
//If this is the first sample, just copy the min max values.
if (divider == 0)
{
data.r = 0.0;
data.g = sample.g;
data.b = sample.b;
}
//Store the value for averaging
data.r += sample.r;
divider += 1.0;
//Store the maximum value
data.g = max(sample.g, data.g);
//Store the maximum value
data.b = min(sample.b, data.b);
}
vec4 output_frag()
{
return vec4(data.r / divider, data.g, data.b, 1.0);
}
void main()
{
vec2 oldPixelOrigin = (downscale * gl_FragCoord.xy - vec2(0.5, 0.5)) * oldInvDimensions;
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(0,0)));
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(0,1)));
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(1,0)));
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(1,1)));
//Now determine if we need to add extra samples in case of
//non-power of two textures
bool extraXSamples = (2 * (int(gl_FragCoord.x) + 1) == oldDimensions.x - 1);
bool extraYSamples = (2 * (int(gl_FragCoord.y) + 1) == oldDimensions.y - 1);
if (extraXSamples)
{
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(2,0)));
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(2,1)));
}
if (extraYSamples)
{
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(0,2)));
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(1,2)));
}
if (extraXSamples && extraYSamples)
combine(textureOffset(inputTex, oldPixelOrigin, ivec2(2,2)));
L_out = output_frag();
}
```