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

Add support for mipmaps #123

Closed
luiscubal opened this Issue Nov 11, 2011 · 28 comments

Comments

Projects
None yet
@luiscubal

luiscubal commented Nov 11, 2011

Currently, drawing a sprite with a low zoom(inferior to 1), so that the texture is "zoomed out", gives a very bad result, even if Smooth is set.
Although there are ways to force mipmaps to be created using SFML's Bind() and OpenGL commands, this is still worse than having proper support in SFML.

I propose the following solutions(any will do):

  1. Have a bool parameter in the Texture constructor telling SFML to generate mipmaps, and automatically enable them with Smooth=true(or a separate property useMipmapsIfSmooth)
  2. Have some function to enable mipmap support.
  3. Have mipmap support always enabled(perhaps not recommended since it will cause problems for images with Smooth=false)

Mipmaps seem to be available since OpenGL 1.4 (although there seem to have been some changes to how mipmaps are created in OpenGL 3.0), so I don't think this would cause problems to anyone. If compatibility is a problem, then some static method to detect mipmap support could be added(similar to how SFML handles shaders?)

As far as I know, adding mipmap support should be as simple as adding the following lines of code:

glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

See also: http://en.sfml-dev.org/forums/index.php?topic=6266.0

@ghost ghost assigned LaurentGomila Nov 11, 2011

@evanw

This comment has been minimized.

Show comment
Hide comment
@evanw

evanw Feb 26, 2012

We needed mipmapping in our game too since we want to support a wide range of screen resolutions. However, mipmaps are calculated incorrectly if they contain transparent values because the RGB components of the transparent colors are incorrectly averaged into the border colors for lower mipmap levels. This can be solved by converting textures to pre-multiplied alpha format and using the glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) blend mode instead of glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). So it would be a very good idea to add support for a pre-multiplied alpha blend mode when adding support for mipmapping.

evanw commented Feb 26, 2012

We needed mipmapping in our game too since we want to support a wide range of screen resolutions. However, mipmaps are calculated incorrectly if they contain transparent values because the RGB components of the transparent colors are incorrectly averaged into the border colors for lower mipmap levels. This can be solved by converting textures to pre-multiplied alpha format and using the glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) blend mode instead of glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). So it would be a very good idea to add support for a pre-multiplied alpha blend mode when adding support for mipmapping.

@Svensational

This comment has been minimized.

Show comment
Hide comment
@Svensational

Svensational Jun 11, 2013

I recently stumbled upon this issue. It really shouldn't be a big problem to implement it and I would appreciate it as the default for smooth=true.
Where I need it is while rendering text. While I just "patched" the glyph texture in SFML 1.x by binding it, setting the corresponding glTexParameter and forcing an update this attempt won't work with SFML 2.x's nice approach of loading glyphs ad hoc, which nondeterministically recreates the texture.
The issue was opened two years ago, could be fixed with little effort and bothers me way to much, so I just wanted to draw a little attention to it again ;)

Svensational commented Jun 11, 2013

I recently stumbled upon this issue. It really shouldn't be a big problem to implement it and I would appreciate it as the default for smooth=true.
Where I need it is while rendering text. While I just "patched" the glyph texture in SFML 1.x by binding it, setting the corresponding glTexParameter and forcing an update this attempt won't work with SFML 2.x's nice approach of loading glyphs ad hoc, which nondeterministically recreates the texture.
The issue was opened two years ago, could be fixed with little effort and bothers me way to much, so I just wanted to draw a little attention to it again ;)

@LaurentGomila

This comment has been minimized.

Show comment
Hide comment
@LaurentGomila

LaurentGomila Jun 12, 2013

Member

Scaled text always looks bad anyway, so you sould rather change the character size instead of scaling it.

Member

LaurentGomila commented Jun 12, 2013

Scaled text always looks bad anyway, so you sould rather change the character size instead of scaling it.

@Valloric

This comment has been minimized.

Show comment
Hide comment
@Valloric

Valloric Dec 1, 2013

Contributor

Any progress on this? Unless I'm missing something, it should be fairly simple to implement. I see "milestone 2.x" but that's obviously out of date.

Contributor

Valloric commented Dec 1, 2013

Any progress on this? Unless I'm missing something, it should be fairly simple to implement. I see "milestone 2.x" but that's obviously out of date.

@retep998

This comment has been minimized.

Show comment
Hide comment
@retep998

retep998 Dec 1, 2013

If you bothered to look at the related pull request, you'd see an implementation has been provided, and they are currently working on fixing it up and making sure it works perfectly before its merged in.

retep998 commented Dec 1, 2013

If you bothered to look at the related pull request, you'd see an implementation has been provided, and they are currently working on fixing it up and making sure it works perfectly before its merged in.

@Valloric

This comment has been minimized.

Show comment
Hide comment
@Valloric

Valloric Dec 1, 2013

Contributor

If you bothered to look at the related pull request

You are correct, I did miss the pull request. Thank you for pointing it out.

The brash tone you could have skipped.

Contributor

Valloric commented Dec 1, 2013

If you bothered to look at the related pull request

You are correct, I did miss the pull request. Thank you for pointing it out.

The brash tone you could have skipped.

@LaurentGomila

This comment has been minimized.

Show comment
Hide comment
@LaurentGomila

LaurentGomila Dec 1, 2013

Member

they are currently working on fixing it up and making sure it works perfectly before its merged in

I would not say that. Right now I'm testing the app to make sure it really makes a noticeable difference. If it's worth adding, then I'll have a deeper look at the implementation; chances are that it will require a significant amount of modifications :p

So don't expect it to be merged soon.

Member

LaurentGomila commented Dec 1, 2013

they are currently working on fixing it up and making sure it works perfectly before its merged in

I would not say that. Right now I'm testing the app to make sure it really makes a noticeable difference. If it's worth adding, then I'll have a deeper look at the implementation; chances are that it will require a significant amount of modifications :p

So don't expect it to be merged soon.

@Cleroth

This comment has been minimized.

Show comment
Hide comment
@Cleroth

Cleroth Apr 24, 2014

"A significant amount of modifications"? How so?

Cleroth commented Apr 24, 2014

"A significant amount of modifications"? How so?

@LaurentGomila

This comment has been minimized.

Show comment
Hide comment
@LaurentGomila

LaurentGomila Apr 24, 2014

Member

I don't remember, it was a long time ago.

Member

LaurentGomila commented Apr 24, 2014

I don't remember, it was a long time ago.

@Cleroth

This comment has been minimized.

Show comment
Hide comment
@Cleroth

Cleroth commented Apr 24, 2014

@TankOs

This comment has been minimized.

Show comment
Hide comment
@TankOs

TankOs Apr 24, 2014

Member

Just like texture filtering this seems like a required feature to me. Together with anisotropic filtering it can make a real difference.

Member

TankOs commented Apr 24, 2014

Just like texture filtering this seems like a required feature to me. Together with anisotropic filtering it can make a real difference.

@Cleroth

This comment has been minimized.

Show comment
Hide comment
@Cleroth

Cleroth Apr 24, 2014

Anisotropy does nothing at all for 2D.

Cleroth commented Apr 24, 2014

Anisotropy does nothing at all for 2D.

@TankOs

This comment has been minimized.

Show comment
Hide comment
@TankOs

TankOs Apr 24, 2014

Member

Keep in mind that SFML's textures are not only being used for 2D geometry/views, or only with the SFML graphics module.

Member

TankOs commented Apr 24, 2014

Keep in mind that SFML's textures are not only being used for 2D geometry/views, or only with the SFML graphics module.

@Cleroth

This comment has been minimized.

Show comment
Hide comment
@Cleroth

Cleroth Apr 24, 2014

Dunno, if you want to do 3D I'd probably highly suggest other systems since SFML is focused on 2D and is lacking features in 2D, so implementing stuff like that for 3D seems like a bit of a waste, specially since if you're doing 3D with SFML you're most likely wrapping it already and thus implementing your own anisotropy methods should be trivial.

Cleroth commented Apr 24, 2014

Dunno, if you want to do 3D I'd probably highly suggest other systems since SFML is focused on 2D and is lacking features in 2D, so implementing stuff like that for 3D seems like a bit of a waste, specially since if you're doing 3D with SFML you're most likely wrapping it already and thus implementing your own anisotropy methods should be trivial.

@TankOs

This comment has been minimized.

Show comment
Hide comment
@TankOs

TankOs Apr 25, 2014

Member

People use sf::Texture for loading and using textures only as well.

Member

TankOs commented Apr 25, 2014

People use sf::Texture for loading and using textures only as well.

@LaurentGomila LaurentGomila removed their assignment May 19, 2014

@eXpl0it3r eXpl0it3r removed this from the 2.x milestone Nov 13, 2014

@luiscubal luiscubal removed this from the 2.x milestone Nov 13, 2014

@eXpl0it3r eXpl0it3r removed the s:unassigned label Jan 8, 2015

@binary1248 binary1248 added this to the 2.4 milestone Mar 29, 2015

@binary1248 binary1248 self-assigned this Mar 29, 2015

@binary1248

This comment has been minimized.

Show comment
Hide comment
@binary1248

binary1248 May 11, 2015

Member

So... time to resurrect this issue.

Right now, there are 2 routes we can go down to implement mipmap support:

  • Use GL_SGIS_generate_mipmap
  • Use GL_EXT_framebuffer_object

(I'm not even considering the GLU option, since... it's GLU and is not hardware accelerated at all)

One possible implementation of the first route exists as #498. Mipmap generation relies on setting a texture parameter which instructs the GL to automatically generate mipmaps every time the texture is modified after that parameter is set. This can and will be a problem if the user intends to update the texture frequently or in multiple smaller segments. In order to preserve performance in this case, they would have to disable mipmaps, update the texture as they need to and re-enable mipmaps before their last update. If you ask me, this sounds very troublesome and almost unusable. In fact, because this makes the GL generate mipmaps every time the texture is modified, it doesn't even work on textures that are used as framebuffer object images, another disadvantage. The only real advantage I can think of with this variant is that it is supported in OpenGL since version 1.4 or on systems that support GL_SGIS_generate_mipmap.

The second route makes mipmap generation explicit so the user will have full control over when they are allocated and generated. This means that once they are done modifying the texture in any way they need to, they can finally call a method to generate the mipmap based on the current texture data. It's that simple really. Because it's that simple, it is also supported on textures that are used as framebuffer object images, which is the reason why it was introduced in the corresponding extension. The user would just draw to a RenderTexture as usual, and once they are done drawing everything, they can manually generate the mipmap for the new texture data. The only disadvantage (if you can even call it that) is that this route relies on a more recent extension (GL_EXT_framebuffer_object) which is already used by the RenderTexture implementation when it is supported.

So, there you have it, we will have to choose between implicit/automatic mipmap generation or explicit mipmap generation, the latter being more powerful for users who know when and how to use it. Seeing as mipmaps will probably only be interesting to more advanced users anyway, I favour the second option. Bear in mind that when thinking of support for OpenGL ES 2.0+ (which is required for shader support), there really only is the second option.

Member

binary1248 commented May 11, 2015

So... time to resurrect this issue.

Right now, there are 2 routes we can go down to implement mipmap support:

  • Use GL_SGIS_generate_mipmap
  • Use GL_EXT_framebuffer_object

(I'm not even considering the GLU option, since... it's GLU and is not hardware accelerated at all)

One possible implementation of the first route exists as #498. Mipmap generation relies on setting a texture parameter which instructs the GL to automatically generate mipmaps every time the texture is modified after that parameter is set. This can and will be a problem if the user intends to update the texture frequently or in multiple smaller segments. In order to preserve performance in this case, they would have to disable mipmaps, update the texture as they need to and re-enable mipmaps before their last update. If you ask me, this sounds very troublesome and almost unusable. In fact, because this makes the GL generate mipmaps every time the texture is modified, it doesn't even work on textures that are used as framebuffer object images, another disadvantage. The only real advantage I can think of with this variant is that it is supported in OpenGL since version 1.4 or on systems that support GL_SGIS_generate_mipmap.

The second route makes mipmap generation explicit so the user will have full control over when they are allocated and generated. This means that once they are done modifying the texture in any way they need to, they can finally call a method to generate the mipmap based on the current texture data. It's that simple really. Because it's that simple, it is also supported on textures that are used as framebuffer object images, which is the reason why it was introduced in the corresponding extension. The user would just draw to a RenderTexture as usual, and once they are done drawing everything, they can manually generate the mipmap for the new texture data. The only disadvantage (if you can even call it that) is that this route relies on a more recent extension (GL_EXT_framebuffer_object) which is already used by the RenderTexture implementation when it is supported.

So, there you have it, we will have to choose between implicit/automatic mipmap generation or explicit mipmap generation, the latter being more powerful for users who know when and how to use it. Seeing as mipmaps will probably only be interesting to more advanced users anyway, I favour the second option. Bear in mind that when thinking of support for OpenGL ES 2.0+ (which is required for shader support), there really only is the second option.

@MarioLiebisch

This comment has been minimized.

Show comment
Hide comment
@MarioLiebisch

MarioLiebisch May 11, 2015

Member

Never checked what GLU does. Does it really generate the mipmaps in software? Or is it some kind of fallback?

Member

MarioLiebisch commented May 11, 2015

Never checked what GLU does. Does it really generate the mipmaps in software? Or is it some kind of fallback?

@binary1248

This comment has been minimized.

Show comment
Hide comment
@binary1248

binary1248 May 11, 2015

Member

It really does everything in software. Back when GLU was written, there was no such thing as "hardware acceleration" like there is nowadays. All the hardware did was really basic rendering/rasterization, manage framebuffer memory and drive the output to the monitor essentially.

In this case, GLU basically takes the texture data you pass to it (in CPU memory), generates the mipmap chain in memory and uploads all of that stuff to the GPU once it's done. You can still generate your own mipmaps in software and upload yourself nowadays if you really wanted to, but as you can imagine nobody ever does it any more since it is all properly hardware accelerated these days.

Member

binary1248 commented May 11, 2015

It really does everything in software. Back when GLU was written, there was no such thing as "hardware acceleration" like there is nowadays. All the hardware did was really basic rendering/rasterization, manage framebuffer memory and drive the output to the monitor essentially.

In this case, GLU basically takes the texture data you pass to it (in CPU memory), generates the mipmap chain in memory and uploads all of that stuff to the GPU once it's done. You can still generate your own mipmaps in software and upload yourself nowadays if you really wanted to, but as you can imagine nobody ever does it any more since it is all properly hardware accelerated these days.

@TankOs

This comment has been minimized.

Show comment
Hide comment
@TankOs

TankOs May 11, 2015

Member

I guess the second option is the only sane one, then.

Member

TankOs commented May 11, 2015

I guess the second option is the only sane one, then.

@Cleroth

This comment has been minimized.

Show comment
Hide comment
@Cleroth

Cleroth May 11, 2015

Software mipmapping is still useful when you want a better downsizing algorithm. As far as I'm aware the only algorithms that are hardware-accelerated are either linear or bicubic sampling (in RGB...), neither of which is very good at downsizing. Lanczos would be one option.

Point being, if you want high-quality mipmaps, software is the way to go. But like you said, nobody really bothers to do high-quality mipmaps yet. It may be coming into fashion though, as people become more aware of their existence.

Cleroth commented May 11, 2015

Software mipmapping is still useful when you want a better downsizing algorithm. As far as I'm aware the only algorithms that are hardware-accelerated are either linear or bicubic sampling (in RGB...), neither of which is very good at downsizing. Lanczos would be one option.

Point being, if you want high-quality mipmaps, software is the way to go. But like you said, nobody really bothers to do high-quality mipmaps yet. It may be coming into fashion though, as people become more aware of their existence.

@TankOs

This comment has been minimized.

Show comment
Hide comment
@TankOs

TankOs May 13, 2015

Member

I'm no graphics expert, so bear with me, but isn't a Lanczos filter also possible through a fragment shader?

Member

TankOs commented May 13, 2015

I'm no graphics expert, so bear with me, but isn't a Lanczos filter also possible through a fragment shader?

@MarioLiebisch

This comment has been minimized.

Show comment
Hide comment
@MarioLiebisch

MarioLiebisch May 13, 2015

Member

You can implement pretty much any filter in a shader, but it'll cause runtime costs rather than some static one-time cost when generating mipmaps.

Member

MarioLiebisch commented May 13, 2015

You can implement pretty much any filter in a shader, but it'll cause runtime costs rather than some static one-time cost when generating mipmaps.

@binary1248

This comment has been minimized.

Show comment
Hide comment
@binary1248

binary1248 May 13, 2015

Member

Fragment shaders are executed per fragment, as their name implies. Even if one were to ignore the constant cost of having to add an extra pass every frame, this still doesn't fix the problem that mipmapping is meant to fix. Mipmapping applies solely to the texture space before it even gets pulled into the rendering process through a sampler. Some might even use mipmapping not for the potentially improved aesthetics but more for the improved performance that GPUs can deliver because less texture data has to be processed in total at lower LODs. Fragment shaders are used for post-processing effects, stuff that applies to the actual scene and not to specific data sources for that scene, although I'm pretty sure hell-bent people will still find a way to do this as well, as usual...

Member

binary1248 commented May 13, 2015

Fragment shaders are executed per fragment, as their name implies. Even if one were to ignore the constant cost of having to add an extra pass every frame, this still doesn't fix the problem that mipmapping is meant to fix. Mipmapping applies solely to the texture space before it even gets pulled into the rendering process through a sampler. Some might even use mipmapping not for the potentially improved aesthetics but more for the improved performance that GPUs can deliver because less texture data has to be processed in total at lower LODs. Fragment shaders are used for post-processing effects, stuff that applies to the actual scene and not to specific data sources for that scene, although I'm pretty sure hell-bent people will still find a way to do this as well, as usual...

@wintertime

This comment has been minimized.

Show comment
Hide comment
@wintertime

wintertime May 26, 2015

Contributor

One possible implementation of the first route exists as #498.

Actually, there have been commits for automatically choosing between ARB_FBO, EXT_FBO and SGIS_MIPMAP, depending on whats available: https://github.com/wintertime/SFML/commits/mipmaps
Its just, after all this time, its probably bitrotted and you need to adapt or reimplement it.

Contributor

wintertime commented May 26, 2015

One possible implementation of the first route exists as #498.

Actually, there have been commits for automatically choosing between ARB_FBO, EXT_FBO and SGIS_MIPMAP, depending on whats available: https://github.com/wintertime/SFML/commits/mipmaps
Its just, after all this time, its probably bitrotted and you need to adapt or reimplement it.

@binary1248

This comment has been minimized.

Show comment
Hide comment
@binary1248

binary1248 May 26, 2015

Member

I glanced over those other commits and I still don't get why you chose to emulate GL_SGIS_generate_mipmap behaviour even when glGenerateMipmap is available. The whole reason why glGenerateMipmap was necessary is so that the user can choose themselves when the mipmaps actually get generated. Making it happen automatically as with the older extension and not giving the user any control over it whatsoever eliminates all the advantages glGenerateMipmap is supposed to have.

If SFML is going to support mipmap generation, it will have to be explicit, as part of the public API. The users who don't know/care about mipmaps can just ignore it and those who do know/care about mipmaps will know when to generate their them manually anyway (if mipmaps are even beneficial in their scenario).

Member

binary1248 commented May 26, 2015

I glanced over those other commits and I still don't get why you chose to emulate GL_SGIS_generate_mipmap behaviour even when glGenerateMipmap is available. The whole reason why glGenerateMipmap was necessary is so that the user can choose themselves when the mipmaps actually get generated. Making it happen automatically as with the older extension and not giving the user any control over it whatsoever eliminates all the advantages glGenerateMipmap is supposed to have.

If SFML is going to support mipmap generation, it will have to be explicit, as part of the public API. The users who don't know/care about mipmaps can just ignore it and those who do know/care about mipmaps will know when to generate their them manually anyway (if mipmaps are even beneficial in their scenario).

@wintertime

This comment has been minimized.

Show comment
Hide comment
@wintertime

wintertime May 27, 2015

Contributor

The reason had been that SFML code had to have support for ancient OpenGL 1.x and therefore the inferior method using the texture parameter had to be supported and that is the only way this method can be used.
Its also the least intrusive method, allowing full backwards compatibility (which was mandated for SFML code, as RenderTexture was also maintaining the fallback path without EXT_FBO) - people can just ignore the method to activate mipmapping and would have the same texture only doing ugly nearest filtering they always had.
I did not watch SFML development anymore, have you finally ditched support for ancient OpenGL meanwhile?

Btw., most people would only load most textures from file once, then use it. If the PR had required them to explicitly call mipmap generation it would have had even less chance of being merged and might have been closed with "thats not what the S in SFML says, our beginner users would never know if/when this new redundant call is necessary".

Contributor

wintertime commented May 27, 2015

The reason had been that SFML code had to have support for ancient OpenGL 1.x and therefore the inferior method using the texture parameter had to be supported and that is the only way this method can be used.
Its also the least intrusive method, allowing full backwards compatibility (which was mandated for SFML code, as RenderTexture was also maintaining the fallback path without EXT_FBO) - people can just ignore the method to activate mipmapping and would have the same texture only doing ugly nearest filtering they always had.
I did not watch SFML development anymore, have you finally ditched support for ancient OpenGL meanwhile?

Btw., most people would only load most textures from file once, then use it. If the PR had required them to explicitly call mipmap generation it would have had even less chance of being merged and might have been closed with "thats not what the S in SFML says, our beginner users would never know if/when this new redundant call is necessary".

@binary1248

This comment has been minimized.

Show comment
Hide comment
@binary1248

binary1248 May 27, 2015

Member

SFML hasn't ditched support for ancient OpenGL. That also doesn't mean that support for mipmap generation has to be mandatory.

As you said yourself, if mipmap generation isn't supported in the way SFML needs it to be, then the user will just have to stick with textures without mipmaps. It doesn't break anything in any way and doesn't cut out users with really old hardware. In fact, most users who could use it probably wouldn't anyway since they wouldn't benefit from it in the first place.

Mipmaps only really provide a benefit if you minify your textures, meaning that a texel is going to be smaller than a logical pixel on the final framebuffer. Providing a sub-optimal implementation that automatically creates mipmaps whenever a texture gets updated will make mipmap generation less appealing to users who have the required hardware and who know/understand that they will benefit from mipmaps (e.g. a lot of zoomed out sprites, OpenGL interoperation, etc.), and that is the target audience of mipmap support in SFML.

SFML hasn't, and probably won't for a while, ditched old hardware. It's just time that the library started to support optional functionality that users with newer hardware might want to take advantage of. sf::Shader already did it long ago, I don't see why it can't be done in other areas as well.

Member

binary1248 commented May 27, 2015

SFML hasn't ditched support for ancient OpenGL. That also doesn't mean that support for mipmap generation has to be mandatory.

As you said yourself, if mipmap generation isn't supported in the way SFML needs it to be, then the user will just have to stick with textures without mipmaps. It doesn't break anything in any way and doesn't cut out users with really old hardware. In fact, most users who could use it probably wouldn't anyway since they wouldn't benefit from it in the first place.

Mipmaps only really provide a benefit if you minify your textures, meaning that a texel is going to be smaller than a logical pixel on the final framebuffer. Providing a sub-optimal implementation that automatically creates mipmaps whenever a texture gets updated will make mipmap generation less appealing to users who have the required hardware and who know/understand that they will benefit from mipmaps (e.g. a lot of zoomed out sprites, OpenGL interoperation, etc.), and that is the target audience of mipmap support in SFML.

SFML hasn't, and probably won't for a while, ditched old hardware. It's just time that the library started to support optional functionality that users with newer hardware might want to take advantage of. sf::Shader already did it long ago, I don't see why it can't be done in other areas as well.

binary1248 added a commit that referenced this issue Oct 2, 2015

binary1248 added a commit that referenced this issue Oct 2, 2015

binary1248 added a commit that referenced this issue Feb 21, 2016

@eXpl0it3r eXpl0it3r added the s:accepted label May 4, 2016

binary1248 added a commit that referenced this issue May 4, 2016

@eXpl0it3r

This comment has been minimized.

Show comment
Hide comment
@eXpl0it3r

eXpl0it3r May 6, 2016

Member

Merged in 259811d

Member

eXpl0it3r commented May 6, 2016

Merged in 259811d

@eXpl0it3r eXpl0it3r closed this May 6, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment