-
-
Notifications
You must be signed in to change notification settings - Fork 376
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
Fix easter egg and refactor MilkdropNoise class #776
Fix easter egg and refactor MilkdropNoise class #776
Conversation
6535112
to
b0f63d3
Compare
src/libprojectM/TimeKeeper.cpp
Outdated
std::normal_distribution<double> gaussianDistribution{m_presetDuration, m_easterEgg}; | ||
if (m_easterEgg < 0.001) | ||
{ | ||
m_easterEgg = 1.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might make more sense to say something like m_easterEgg = max(m_easterEgg, 0.001)
. Having it snap to 1.0 only if it gets very small seems unexpected to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's always properly initialized, then I agree.
Setting it to 1 here will make sure the proper default value is used if someone uses 0 as the default value in an external app integration and passes it in after construction, e.g. in existing apps made by someone who didn't know about this limitation. Besides that, this "easter egg" isn't really noticeable in any case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option would be to just return m_presetDuration if m_easterEgg is zero, since that's what the normal distribution would theoretically do with stddev = 0. No action needed though since there are bigger fish to fry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall. I'll leave it up to you to decide whether you want to remove the extra random data copy or not.
auto MilkdropNoise::LowQuality() -> std::shared_ptr<Texture> | ||
{ | ||
GLuint texture{}; | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason this is in a separate block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly reduces the lifetime of the textureData
vector, which is only required until after the glTexImageXD
call. Not really a concern though as the method exits shortly after, if you find it less readable this way I can just remove the blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a big deal either way, just making sure it was intentional. It's consistent with other places in the code so I say leave it.
@@ -62,7 +151,7 @@ void MilkdropNoise::generate2D(int size, int zoomFactor, uint32_t** textureData) | |||
// smoothing | |||
if (zoomFactor > 1) | |||
{ | |||
dst = *textureData; | |||
dst = textureData.data(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is redundant, you already assign dst above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's not.
If you look closely, you'll notice that dst
is being incremented in the first loop. This assignment resets it to the start of the buffer. Without this line, projectM will just crash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, I missed the increment. You're right, my bad!
} | ||
|
||
return textureData; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is performing an additional (unnecessary) copy of textureData. I suggest either allocating textureData into a smart pointer and returning that or passing in the std::vector by reference instead to avoid the copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm well over 99% sure most modern compilers will always use copy elision here, as it's a rather simple case. So using smart pointers or a reference would increase code complexity, but not provide any advantages.
At least on my machines, I've checked this and .data()
returns the same memory address inside the function (after resize()
) and in the calling function as well in both release and debug builds, so no data has been copied to another memory location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad the compiler optimizes this away, but it's still allocating a vector on the stack, then returning that vector through a function call, which according to the spec will call the copy constructor. If you don't want to fix it that's fine (this is done during preset initialization so it doesn't really affect runtime performance) but it's not best practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you'd check this, you'll see that the compiler turns it into a move operation, and not even that in the std::move
sense - the compiler will simply make the variable it's being assigned to point to the stack object. Even if the vector itself would be copied (which is perfectly allowed as you said), it's just a heap pointer, the alloc size and the element count, so 24 bytes to copy on 64 bit systems. Not that much of a hassle.
{ | ||
auto textureData = generate2D(256, 1); | ||
|
||
glGenTextures(1, &texture); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This allocates a new texture each time this is called. That's fine if each of these is only called once by TextureManager, but if it's called twice it'll generate a new texture with different values. No action needed, just a note. In theory you could initialize a static field in this class and return it if it's been constructed, but I don't think the change is worth it since this should only be called once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the issue at hand here.
The noise is pseudo-random, so for each call I'd actually expect it to be different (or, rather even require it). Plus, this function is actually there to create a new texture upon each call (not reuse any existing one), because users might want different noise textures for each preset (currently, these are just generated once per projectM instance).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous implementation defined texture data and initialized it, and that texture data is referenced directly. The new implementation creates texture data each time it's called. It doesn't matter as long as this isn't called multiple times for a single preset (which it isn't) so it doesn't matter. No changes required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The series of events was changed, but technically, it does the exact same thing as before. Just to compare, here's how it worked previously each time TextureManager::Preload()
was called:
- Create an instance of MilkdropNoise.
- The MilkdropNoise constructor called each static noise generator function once and stored the resulting texture data as a raw pointer
- For each noise texture image, a GL texture was then created and the image data copied into it (via
glTexture2D
/glTexture3D
). - A projectM
Texture
instance was then created with the resulting GL texture ID for use in presets, stored as a shared pointer. - The MilkdropNoise instance was deleted at the end of the
Preload
function, including the image data.
Now the MilkdropNoise class is purely static, and texture creation was just moved into each noise function, like this:
- For each noise texture, the static generator function in Milkdrop noise is called, which then does:
- Generate the pixel data for the requested noise image.
- Create a GL texture and upload the image data into it.
- Delete the local pixel data.
- Create a
Texture
instance with the just-created texture ID and return it as a shared pointer.
- In
Preload()
, the returned shared_ptr instance is then added to the texture storage.
src/libprojectM/TimeKeeper.cpp
Outdated
std::normal_distribution<double> gaussianDistribution{m_presetDuration, m_easterEgg}; | ||
if (m_easterEgg < 0.001) | ||
{ | ||
m_easterEgg = 1.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option would be to just return m_presetDuration if m_easterEgg is zero, since that's what the normal distribution would theoretically do with stddev = 0. No action needed though since there are bigger fish to fry.
b0f63d3
to
1abd895
Compare
No need to create an instance of the class as all generators are static. Use std::vector instead of C-style pointers as pixel buffer.
Gaussian distribution requires the sigma value to be >0. If the value is too small, we just use the default 1.0 value.
1abd895
to
d6b6446
Compare
Two small things I've fixed/reworked for the 4.1 release:
Texture
instance.