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

COMP: as per memononen's suggestions, tweak findTexture #452

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

Chaircrusher
Copy link

This is based closely on the suggestion you gave to me, but tweaked to work a little better.

  1. The salt is shifted once to the high word. Right-shifting & left-shifting was messing up on ints.
  2. I make the salt by caling rand; it appears that you were taking it from uninitialized memory, which isn't a reliable way to get unique salt for each texture.
  3. I made sure the salt was nonzero, because if the salt was zero and the ID was zero, it makes findTexture look like it failed.

This solves the 'stale texture ID' problem:

  1. If it's a valid ID, the salt matches.
  2. If it's a deleted ID, the salt in the array is zero, and the salt in the ID doesn't match.
  3. If it's a texture that's been deleted and re-used, the salt won't match.

This version has slightly more overhead than the original, (mostly on allocTexture when it matters least), but has better overall performance due to skipping a sequential search for textures by ID.


for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == 0) {
tex = &gl->textures[i];
idx = i;
break;
}
}
Copy link
Contributor

@cforfang cforfang Jan 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tex == NULL below this line should probably be idx == -1 now?

EDIT:
Couldn't find a way to comment on the exact line, but it's the if below the first loop.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, below should be:

if (idx == -1) {

@@ -363,38 +364,54 @@ static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)
gl->textures = textures;
gl->ctextures = ctextures;
}
tex = &gl->textures[gl->ntextures++];
idx = gl->ntextures++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using rand() for the salt below, if you're here you know you're initializing a new entry for which you can set salt = 0.

@@ -147,6 +147,7 @@ struct GLNVGtexture {
int width, height;
int type;
int flags;
unsigned int salt;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't salt already stored as part of the id?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... thinking about it. The goal is to make the ID == the index in the array, so storing the ID in the Texture structure is no longer necessary.

The salt is what need to be stored. The ID returned to the application is the ID + the salt.

@cforfang
Copy link
Contributor

Right-shifting & left-shifting was messing up on ints.

I didn't think this was a problem, but then I also don't use bitfields much :) What problems did you encounter?

it appears that you were taking it from uninitialized memory, which isn't a reliable way to get unique salt for each texture

This seems like a bug in @memononen comment. I think you only need to identify when a new entry is allocated and set the salt to 0 then. If you add that I think the original code (which increments the salt on delete) should work.

Also, from skimming the code it's not clear to me why a separate salt field is needed. Is it not sufficent to just store it as part of id?

@Chaircrusher
Copy link
Author

I didn't think this was a problem, but then I also don't use bitfields much :) What problems did you
encounter?

The problems was using ints. When you right-shift a signed integer, it propogates the sign bits left. This means that for some numbers X << 16 != (X << 16) >> 16.

I solved the problem by just making the salt a random integer left-shifted into the high two bytes.

This seems like a bug in @memononen comment. I think you only need to identify when a new entry is allocated and set the salt to 0 then. If you add that I think the original code (which increments the salt on delete) should work.

Two things: 1) the existing code thinks an ID of zero means allocTexture failed, which means ID can never be zero. 2) The salt could be an incrementing counter, but it should not start at zero, because the index can be zero, so 0 + 0 violates assumption #1

Also, from skimming the code it's not clear to me why a separate salt field is needed. Is it not sufficent to just store it as part of id?

As I said above, the ID is no longer needed as part of the Texture struct.

I'll submit a new version of this branch that A) takes out the id from the Texture struct and B) uses monotonically increasing salt values.

Copy link
Owner

@memononen memononen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bitshifts should work just fine, I think casting to unsigned int is a good idea here. Maybe int does some sign expansion. Let's keep the API with int, though.

There was an oversight in my code, the salt should be initialized to 1. That way valid IDs are always > 0, and 0 can be invalid id. I'm pretty sure that is not used in the code base, but it is part of the common salted index pattern.


for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == 0) {
tex = &gl->textures[i];
idx = i;
break;
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct, below should be:

if (idx == -1) {

src/nanovg_gl.h Outdated
GLNVGtexture* tex = NULL;
int i;
GLNVGtexture* tex = NULL;
int i, idx = -1, salt;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int i, idx = -1, salt = 1;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The salt as a local variable in allocTexture means that it isn't preserved across calls. I made it a member of GLNVGcontext as textureSalt.


for (i = 0; i < gl->ntextures; i++) {
if (gl->textures[i].id == 0) {
tex = &gl->textures[i];
idx = i;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idx = i;
salt = gl->textures[i].salt; // Carry salt from previous item

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

src/nanovg_gl.h Outdated
}

tex = &gl->textures[idx];
while(!(salt = (unsigned int)rand()))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove rand() stuff.

src/nanovg_gl.h Outdated
tex->id = ++gl->textureId;
tex->salt = salt;
// salt value is in high 2 bytes, ID in low two bytes
tex->salt <<= 16;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove
tex->salt <<= 16;

src/nanovg_gl.h Outdated
tex->salt = salt;
// salt value is in high 2 bytes, ID in low two bytes
tex->salt <<= 16;
tex->id = (idx & 0xffff) | tex->salt;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the bitshift problem was operator precedence, try:

tex->id = (idx & 0xffff) | (tex->salt << 16);

src/nanovg_gl.h Outdated
glDeleteTextures(1, &tex->tex);
memset(tex, 0, sizeof(*tex));
// salt always > 0, so deleted texture will have illegal salt
tex->salt = 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Salt must be increment here, not set to zero.

src/nanovg_gl.h Outdated
}
return 0;
}
int idx = id & 0xffff, salt = ((unsigned int)id) & 0xffff0000;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bitshift should work here too no problem.

@Chaircrusher
Copy link
Author

working on a new version. will update within the hour.

@cforfang
Copy link
Contributor

The problems was using ints. When you right-shift a signed integer, it propogates the sign bits left. This means that for some numbers X << 16 != (X << 16) >> 16.

But is that a problem in this case? I know left shifts of signed integers is undefined, but I wasn't sure if right-shifts for the purpose of extracting two numbers like here is OK.

@Chaircrusher
Copy link
Author

I checked in a new version. I think that it is simple and correct. I've verified that it works properly in the application I was testing in (vcvRack). If you want to do it differently be my guest.

tex->id = ++gl->textureId;
// pre-increment so salt begins at one. When GLNVGcontext is
// allocated initially it is set to 0 via memset.
tex->salt = ++gl->textureSalt;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for using a salt per texture entry is that they are less likely to wrap around and keep unique. I'd prefer to use that version.

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

Successfully merging this pull request may close these issues.

3 participants