glTexSubImage2D crash (another)

App for windows, Delphi. I can’t debug one crash - the glTexSubImage2D call. It’s called to refresh a subtexture in texture atlas. Subtexture bitmap extents are ATextureImage.Width x ATextureImage.Height. To avoid linear filtering artifacts, each subtexture is padded by AtlasGap texels at each of 4 sides. So real subtexture extents are FSubTexWidth x FSubTexHeight (see code). ATextureImage is a bitmap class, it has Bits property of type pointer. CheckGLError() calls glGetError() and if there is an error, raises exception. So first CheckGLError() passes, second raises exception (gl error code is GL_INVALID_VALUE) on single configuration - Mobile Intel® 4 Series Express Chipset Family but passes OK on every other platform tried so far.
Look at this:




  glBindTexture(GL_TEXTURE_2D, FAtlases[ AAtlasInx ].AtlasID);

  {FSubTexWidth/FSubTexHeight are enlarged to fit gaps}
  assert(ATextureImage.Width  = FSubTexWidth  - 2*AtlasGap);
  assert(ATextureImage.Height = FSubTexHeight - 2*AtlasGap);


  CheckGLError();   //  <-- this passes

  assert((c * FSubTexWidth   + AtlasGap) + ATextureImage.Width <= FAtlasWidth );
  assert((r * FSubTexHeight  + AtlasGap) + ATextureImage.Height <= FAtlasHeight);
  glTexSubImage2D(GL_TEXTURE_2D,0{level},
                  c * FSubTexWidth   + AtlasGap  {xoffset},
                  r * FSubTexHeight  + AtlasGap  {yoffset},
                  ATextureImage.Width,
                  ATextureImage.Height,
                  GL_BGRA_EXT,
                  GL_UNSIGNED_BYTE,
                  ATextureImage.Bits);
CheckGLError();   //  <--  raises exception, GL_INVALID_VALUE

Texture atlas is created a bit earlier(BytesPerPixel = 4):


...
 InitTextureAtlas(@FAtlases[AAtlasInx].AtlasID,
                           FAtlasWidth,
                           FAtlasHeight,
                           GL_RGBA {internal format},
                           GL_BGRA_EXT {pixel format}
                           );
...

procedure InitTextureAtlas(AAtlasID:PGLuint;
                           AAtlasWidth,AAtlasHeight:integer;
                           AInternalFormat:integer;
                           AFormat:GLuint);
var
  InitBuf:PByte;
  width:integer;
begin
  AAtlasWidth  := RoundUpToPowerOf2(AAtlasWidth);
  AAtlasHeight := RoundUpToPowerOf2(AAtlasHeight);

  glGenTextures(1, AAtlasID);
  glBindTexture(GL_TEXTURE_2D, AAtlasID^);
  CheckGLError();
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
  CheckGLError();


  { check capability with proxy texture }
  glTexImage2d(GL_PROXY_TEXTURE_2D,
                0{level},
                AInternalFormat{components},
                AAtlasWidth,
                AAtlasHeight,
                0 {border},
                AFormat,
                GL_UNSIGNED_BYTE, nil);
  glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, @width);
  if width = 0 then
  begin
    raise ESystemRequirements.Create('Unsufficient texture memory');
  end;
  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);


  GetMem(InitBuf, AAtlasWidth * AAtlasHeight * BytesPerPixel);
  try
    FillChar(InitBuf^,AAtlasWidth * AAtlasHeight * BytesPerPixel,$00);
    glTexImage2d(GL_TEXTURE_2D,
                  0{level},
                  AInternalFormat{components},
                  AAtlasWidth,
                  AAtlasHeight,
                  0 {border},
                  AFormat,
                  GL_UNSIGNED_BYTE, InitBuf);
  finally
   FreeMem(InitBuf);
  end;


  CheckGLError();
end;

To believe documentation, there are only next possible cases:
1)GL_INVALID_VALUE is generated if level is less than 0.

2)GL_INVALID_VALUE may be generated if level is greater than log 2 max, where max is the returned value of GL_MAX_TEXTURE_SIZE.

3)GL_INVALID_VALUE is generated if xoffset < - b , xoffset + width > w - b , yoffset < - b , or yoffset + height > h - b , where w is the GL_TEXTURE_WIDTH, h is the GL_TEXTURE_HEIGHT, and b is the border width of the texture image being modified. Note that w and h include twice the border width.

4)GL_INVALID_VALUE is generated if width or height is less than 0.

1,2 and 4 are excluded, 3 is checked by assertions. Again, I can reproduce it only on one machine. Any suggestions?

2)GL_INVALID_VALUE may be generated if level is greater than log 2 max, where max is the returned value of GL_MAX_TEXTURE_SIZE.

Why did you exclude point 2?
Max texture size change from machine to machine, and Intel graphic processor are not famous for their performance.
How big is your texture?

2)GL_INVALID_VALUE may be generated if level is greater than log 2 max, where max is the returned value of GL_MAX_TEXTURE_SIZE.

Why did you exclude point 2?

But proxy test passes OK. Also max texture size is checked during initialization (not shown) and atlas size is tuned to fit…