Jump to content

View more

Image of the Day

雑魚は多めにして、爽快感重視にしつつ・・・(´・ω・`)
早いとこ、ベースを作って、完成にもっていかないとね。
タイトルもまだ迷ってるだよなぁ。 
#indiedev  #indiegame #screenshotsaturday https://t.co/IwVbswGrhe
IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.


Sign up now

Very strange problem with Texture Units

4: Adsense
  • You cannot reply to this topic
11 replies to this topic

#1 CirdanValen   Members   

371
Like
1Likes
Like

Posted 19 March 2017 - 06:12 PM

I have the following code:

    BindTextureUnit(&editor->devTexture, 0);
    BindTextureUnit(&editor->renderBuffer, 1);
    BindTextureUnit(&editor->lightBuffer, 2);
    BindTextureUnit(&editor->uiSprites, 3);
    GLenum err = glGetError();    

Which consists of:

_INTERNAL void
BindTextureUnit(Texture* texture, u32 unit)
{
    glActiveTexture(GL_TEXTURE0 + unit);
    glBindTexture(GL_TEXTURE_2D, texture->handle);
    texture->unit = unit;
}

This section before the game loop is the only place where I bind textures to texture units. Rest of the time I set the sampler in shaders. Also note that the GL Error is set to 0 through all of this.

The first three textures work as expected, no problems. I recently added the fourth texture "uiSprites". When I render anything with that texture, it comes out white. Now supposedly my GPU can handle 192 texture units, so I'm not running out...but for some reason the texture units above 2 don't work properly.

Here is what I've found in my investigations

- I know for sure that the textures are being loaded correctly. If I put uiSprites into the first texture unit, it renders correctly. Similarly, if I put devTexture into the 3rd unit, it renders white.

- If I comment out the first line, where devTexture is being bound, then I bind uiSprites to unit 0 AFTER binding the renderBuffer and lightBuffer...the texture is rendered as white. This means that the only time binding to texture unit 0 works is if I do it before the other two.

 

renderBuffer and lightBuffer are both Framebuffers and work correctly the whole time while testing this. I'm wracking my brain trying to figure out why a) the 3rd texture unit is white b) why is this order dependent?

 

 

 

 

 



#2 Promit   Senior Moderators   

13098
Like
1Likes
Like

Posted 19 March 2017 - 06:35 PM

RenderDoc may give you some insight into what exactly the pipeline is seeing and where. Personally, I'm suspicious that you're not setting the sampler assignments correctly for the shader, since you still need to assign samplers to units. 


SlimDX | Shark Eaters for iOS | Ventspace Blog | Twitter | Proud supporter of diversity and inclusiveness in game development

#3 CirdanValen   Members   

371
Like
1Likes
Like

Posted 19 March 2017 - 06:59 PM

Basically, I store the assigned texture unit in the Texture struct as seen above. When I go to set the uniform in the texture, I use that texture.unit. 

_INTERNAL void
UseTexture(RenderContext* context, Texture texture, u32 loc)
{
    if(context->cmdBufferSize > 0)
    {
        ASSERT(!"InvalidPath");
    }
    glUniform1i(loc, texture.unit);
}

The render part looks like this

glClearColor(0.0f, 0.0f, 0.0f, 1.f);
BeginRenderPass(&editor->renderContext, editor->spriteShader, SPRITE_SHADER_UNIFORM_TRANSFORM_MAT, viewMatrix, editor->renderBuffer);
{
    UseTexture(&editor->renderContext, editor->devTexture, SPRITE_SHADER_UNIFORM_TEXTURE);
    PushVertexBuffer(&editor->renderContext, editor->floorVbo, 6, RenderMode_Triangles);  
}
EndRenderPass(&editor->renderContext); 

So all I do to test this is change editor->devTexture to editor->uiSprite.   devTexture works, uiSprite doesn't. The same shader is used, so the sampler location doesn't change.

 

Renderdoc doesn't work on my application for some reason (Doesn't support OpenGL 4.5?). But i can use GLintercept to post the GL calls log if needed.


Edited by CirdanValen, 19 March 2017 - 07:01 PM.


#4 CirdanValen   Members   

371
Like
1Likes
Like

Posted 19 March 2017 - 08:59 PM

Quick update: I went through and did glBindTexture(GL_TEXTURE_2D, 0) after creating the frame buffers. Now instead of being white, the texture comes out as black. Same order dependency as before. Calling glGetError() at the end of the game loop still returns 0.



#5 mzbear   Members   

218
Like
1Likes
Like

Posted 19 March 2017 - 09:16 PM

random guess from the dark: it's trying to mipmap a texture that doesn't have mipmaps, i.e. it has nothing to do with texture units and all to do with how you're using the texture bound to the unit.



#6 DragonJoker   Members   

186
Like
0Likes
Like

Posted 20 March 2017 - 04:33 AM

Are you using OpenGL sampler objects, or traditional glTexParameter ?


If you can't find anything, look for something else.


#7 CirdanValen   Members   

371
Like
0Likes
Like

Posted 20 March 2017 - 07:39 AM

regular tex parameters

 

_INTERNAL Texture
CreateTexture(Bitmap bitmap, b32 convertToLinear) 
{
    Texture result = {};
    result.handle = UINT_MAX;
    result.width = bitmap.width;
    result.height = bitmap.height;




    glGenTextures(1, &result.handle);
    glBindTexture(GL_TEXTURE_2D, result.handle);


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);


GLenum colorType = (convertToLinear) ? GL_SRGB8_ALPHA8 : GL_RGBA;


    glTexImage2D(GL_TEXTURE_2D, 0, colorType, bitmap.width, bitmap.height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, bitmap.pixels);


    glBindTexture(GL_TEXTURE_2D, 0);


    return result;
}


#8 CirdanValen   Members   

371
Like
0Likes
Like

Posted 20 March 2017 - 10:50 PM

EDIT: here's the GL command call log http://pastebin.com/0xxVqDQq

Couple more points to add:

Another strange discovery I found is that if I comment out the last texture bind, by doing this:

    BindTextureUnit(&editor->devTexture, 0);
    BindTextureUnit(&editor->renderBuffer, 1);
    BindTextureUnit(&editor->lightBuffer, 2);
    //BindTextureUnit(&editor->uiSprites, 3);

The first texture, devTexture at unit 0, renders as black. If I uncomment it, it works fine.

 

Secondly, I have a previous revision of this code that works with all four texture units bound. The difference between this code base and the previous one, is this one I have converted to 3D rendering with depth testing. This is how I build the frame buffers with depth texture:

_INTERNAL RenderBuffer 
CreateRenderBuffer(u32 width, u32 height, b32 hasDepthBuffer = true) 
{
    RenderBuffer result = {};


    result.width = width;
    result.height = height;


    glGenFramebuffers(1, &result.fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, result.fbo);


    glGenTextures(1, &result.texture);
    glBindTexture(GL_TEXTURE_2D, result.texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);


    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, result.texture, 0);


    
    if(hasDepthBuffer)
    {
        glGenTextures(1, &result.depth);
        glBindTexture(GL_TEXTURE_2D, result.depth);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);        


        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0,
                     GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);    


        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, result.depth, 0);        
    } 


    GLenum drawBuffers[1] = {
       GL_COLOR_ATTACHMENT0
    };    


    glDrawBuffers(1, drawBuffers);
    
    GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    ASSERT(fboStatus == GL_FRAMEBUFFER_COMPLETE);
    
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);


    return result;
}

Could the two be somehow related? I've double checked the generated texture IDs and none of them are overlapping. No gl errors even after multiple frames and the frambuffers don't trigger the assert.


Edited by CirdanValen, 20 March 2017 - 10:58 PM.


#9 DragonJoker   Members   

186
Like
0Likes
Like

Posted 21 March 2017 - 05:39 AM

Hi, do you sample your depth texture with a samplerShadow ?
This is usually what I use the compare func for, and except for that, I don't use it.


If you can't find anything, look for something else.


#10 CirdanValen   Members   

371
Like
0Likes
Like

Posted 21 March 2017 - 07:54 AM

I don't touch the depth texture at all after creating it, aside from clearing it every frame.



#11 mhagain   Members   

13155
Like
2Likes
Like

Posted 21 March 2017 - 08:54 AM

You're over-writing your texture bindings; this is a side-effect of OpenGL's bind-to-modify behaviour and happens because in non-DSA OpenGL state used for drawing is the same as state used for creating/modifying objects.

At line 180 you have the following:

glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D,5)
glActiveTexture(GL_TEXTURE1)
glBindTexture(GL_TEXTURE_2D,1)
glActiveTexture(GL_TEXTURE2)
glBindTexture(GL_TEXTURE_2D,3)
glActiveTexture(GL_TEXTURE3)

glBindTexture(GL_TEXTURE_2D,6)

Then at line 249 you have the following:

glBindTexture(GL_TEXTURE_2D,1)
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1904,1041,0,GL_RGBA,GL_UNSIGNED_BYTE,0000000000000000)
glBindTexture(GL_TEXTURE_2D,2)
glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,1904,1041,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,0000000000000000)
glBindTexture(GL_TEXTURE_2D,0)
glBindTexture(GL_TEXTURE_2D,3)
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,1904,1041,0,GL_RGBA,GL_UNSIGNED_BYTE,0000000000000000)
glBindTexture(GL_TEXTURE_2D,4)
glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,1904,1041,0,GL_DEPTH_COMPONENT,GL_UNSIGNED_BYTE,0000000000000000)

glBindTexture(GL_TEXTURE_2D,0)

These calls will over-write the binding for GL_TEXTURE3 and when you come to draw texture '0' (which is actually a valid texture object in OpenGL) will be bound to it.

If you hadn't been un-binding (which IMO is a bad practice) you would have probably caught this sooner because you would have clearly seen the wrong texture being used.

To resolve, either re-bind '6' to GL_TEXTURE3 before drawing; or reserve a texture unit for updates and make that active before doing any updates; or use direct state access.  I personally recommend a combination of direct state access (assuming your target market will stand it) and bind-before-use, getting rid of all unbinding.


It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#12 CirdanValen   Members   

371
Like
1Likes
Like

Posted 21 March 2017 - 06:21 PM

Oh wow. I can't believe I missed that, tho I'm still surprised this problem occurred in this code base and not my previous one. I normally don't unbind anything, since the binds get overwritten anyway, I'll just have to be more careful about texture units.

Still, what was happening is when the window is resized...I recreate the frame buffers (which are what those glTexImage2D calls are after binding to the texture units). So after the initializing the framebuffers for the first time, they immediately get recreated again because Windows sends a resized event when the app first starts up. I'll admit, this should be changed.

I guess I didn't realize that calling glBindTexture while a different texture unit was  active, would change the texture bound to that unit. It makes sense in retrospect. I'll figure out a way to prevent this from happening in the future.

Thanks!


Edited by CirdanValen, 21 March 2017 - 06:26 PM.