Refactored GlyphTexture so that it utlizes standard osg::Texture2D/osg::Image combinations rather than locally implemented per glyph subloading.

This commit is contained in:
Robert Osfield 2017-03-21 13:10:45 +00:00
parent 6670a6e070
commit a74872c6bf
3 changed files with 17 additions and 306 deletions

View File

@ -264,8 +264,6 @@ public:
void addGlyph(Glyph* glyph,int posX, int posY); void addGlyph(Glyph* glyph,int posX, int posY);
virtual void apply(osg::State& state) const;
/** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/ /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/
virtual void setThreadSafeRefUnref(bool threadSafe); virtual void setThreadSafeRefUnref(bool threadSafe);

View File

@ -32,13 +32,10 @@
using namespace osgText; using namespace osgText;
using namespace std; using namespace std;
static osg::ApplicationUsageProxy Font_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_TEXT_INCREMENTAL_SUBLOADING <type>","ON | OFF");
#define FIXED_FUNCTION defined(OSG_GL_FIXED_FUNCTION_AVAILABLE) #define FIXED_FUNCTION defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)
#define SHADERS_GL3 (defined(OSG_GL3_AVAILABLE)) #define SHADERS_GL3 (defined(OSG_GL3_AVAILABLE))
#define SHADERS_GL2 !FIXED_FUNCTION && !SHADERS_GL3 #define SHADERS_GL2 !FIXED_FUNCTION && !SHADERS_GL3
#if SHADERS_GL3 #if SHADERS_GL3
static const char* gl3_TextVertexShader = { static const char* gl3_TextVertexShader = {
"#version 330 core\n" "#version 330 core\n"

View File

@ -103,11 +103,9 @@ void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
{ {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex); OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
if (!_image.valid()) createImage();
_glyphs.push_back(glyph); _glyphs.push_back(glyph);
for(unsigned int i=0;i<_glyphsToSubload.size();++i)
{
_glyphsToSubload[i].push_back(glyph);
}
// set up the details of where to place glyph's image in the texture. // set up the details of where to place glyph's image in the texture.
glyph->setTexture(this); glyph->setTexture(this);
@ -117,294 +115,9 @@ void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
static_cast<float>(posY)/static_cast<float>(getTextureHeight()) ) ); static_cast<float>(posY)/static_cast<float>(getTextureHeight()) ) );
glyph->setMaxTexCoord( osg::Vec2( static_cast<float>(posX+glyph->s())/static_cast<float>(getTextureWidth()), glyph->setMaxTexCoord( osg::Vec2( static_cast<float>(posX+glyph->s())/static_cast<float>(getTextureWidth()),
static_cast<float>(posY+glyph->t())/static_cast<float>(getTextureHeight()) ) ); static_cast<float>(posY+glyph->t())/static_cast<float>(getTextureHeight()) ) );
}
void GlyphTexture::apply(osg::State& state) const
{
// get the contextID (user defined ID of 0 upwards) for the
// current OpenGL context.
const unsigned int contextID = state.getContextID();
if (contextID>=_glyphsToSubload.size())
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
// graphics context is beyond the number of glyphsToSubloads, so
// we must now copy the glyph list across, this is a potential
// threading issue though is multiple applies are happening the
// same time on this object - to avoid this condition number of
// graphics contexts should be set before create text.
for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i)
{
GlyphPtrList& glyphPtrs = _glyphsToSubload[i];
for(GlyphRefList::const_iterator itr=_glyphs.begin();
itr!=_glyphs.end();
++itr)
{
glyphPtrs.push_back(itr->get());
}
}
}
const osg::GLExtensions* extensions = state.get<osg::GLExtensions>();
bool generateMipMapSupported = extensions->isGenerateMipMapSupported;
// get the texture object for the current contextID.
TextureObject* textureObject = getTextureObject(contextID);
bool newTextureObject = (textureObject == 0);
#if defined(OSG_GLES2_AVAILABLE)
bool requiresGenerateMipmapCall = false;
// need to look to see generate mip map call is required.
switch(_min_filter)
{
case NEAREST_MIPMAP_NEAREST:
case NEAREST_MIPMAP_LINEAR:
case LINEAR_MIPMAP_NEAREST:
case LINEAR_MIPMAP_LINEAR:
requiresGenerateMipmapCall = generateMipMapSupported;
break;
default:
break;
}
#endif
if (newTextureObject)
{
GLint maxTextureSize = 256;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (maxTextureSize < getTextureWidth() || maxTextureSize < getTextureHeight())
{
OSG_WARN<<"Warning: osgText::Font texture size of ("<<getTextureWidth()<<", "<<getTextureHeight()<<") too large, unable to create font texture."<<std::endl;
OSG_WARN<<" Maximum supported by hardward by native OpenGL implementation is ("<<maxTextureSize<<","<<maxTextureSize<<")."<<std::endl;
OSG_WARN<<" Please set OSG_MAX_TEXTURE_SIZE lenvironment variable to "<<maxTextureSize<<" and re-run application."<<std::endl;
return;
}
// being bound for the first time, need to allocate the texture
textureObject = osg::Texture::generateAndAssignTextureObject(
contextID, GL_TEXTURE_2D, 1, OSGTEXT_GLYPH_INTERNALFORMAT, getTextureWidth(), getTextureHeight(), 1, 0);
textureObject->bind();
applyTexParameters(GL_TEXTURE_2D,state);
// need to look at generate mip map extension if mip mapping required.
switch(_min_filter)
{
case NEAREST_MIPMAP_NEAREST:
case NEAREST_MIPMAP_LINEAR:
case LINEAR_MIPMAP_NEAREST:
case LINEAR_MIPMAP_LINEAR:
if (generateMipMapSupported)
{
#if !defined(OSG_GLES2_AVAILABLE)
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
#endif
}
else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR);
break;
default:
// not mip mapping so no problems.
break;
}
unsigned int imageDataSize = getTextureHeight()*getTextureWidth();
unsigned char* imageData = new unsigned char[imageDataSize];
for(unsigned int i=0; i<imageDataSize; ++i)
{
imageData[i] = 0;
}
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
glPixelStorei(GL_UNPACK_ROW_LENGTH,getTextureWidth());
#endif
// allocate the texture memory.
glTexImage2D(GL_TEXTURE_2D, 0, OSGTEXT_GLYPH_INTERNALFORMAT,
getTextureWidth(), getTextureHeight(), 0,
OSGTEXT_GLYPH_FORMAT,
GL_UNSIGNED_BYTE,
imageData );
delete [] imageData;
}
else
{
// reuse texture by binding.
textureObject->bind();
if (getTextureParameterDirty(contextID))
{
applyTexParameters(GL_TEXTURE_2D,state);
}
}
static const GLubyte* s_renderer = 0;
static bool s_subloadAllGlyphsTogether = false;
if (!s_renderer)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
s_renderer = glGetString(GL_RENDERER);
OSG_INFO<<"glGetString(GL_RENDERER)=="<<s_renderer<<std::endl;
if (s_renderer && strstr((const char*)s_renderer,"IMPACT")!=0)
{
// we're running on an Octane, so need to work around its
// subloading bugs by loading all at once.
s_subloadAllGlyphsTogether = true;
}
if (s_renderer &&
((strstr((const char*)s_renderer,"Radeon")!=0) ||
(strstr((const char*)s_renderer,"RADEON")!=0) ||
(strstr((const char*)s_renderer,"ALL-IN-WONDER")!=0)))
{
// we're running on an ATI, so need to work around its
// subloading bugs by loading all at once.
s_subloadAllGlyphsTogether = true;
}
if (s_renderer && strstr((const char*)s_renderer,"Sun")!=0)
{
// we're running on an solaris x server, so need to work around its
// subloading bugs by loading all at once.
s_subloadAllGlyphsTogether = true;
}
const char* str = getenv("OSG_TEXT_INCREMENTAL_SUBLOADING");
if (str)
{
s_subloadAllGlyphsTogether = strcmp(str,"OFF")==0 || strcmp(str,"Off")==0 || strcmp(str,"off")==0;
}
}
// now subload the glyphs that are outstanding for this graphics context.
GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID];
if (!glyphsWereSubloading.empty() || newTextureObject)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
bool subloadAllGlyphsTogether = s_subloadAllGlyphsTogether;
#if defined(OSG_GLES2_AVAILABLE)
if (requiresGenerateMipmapCall) subloadAllGlyphsTogether = true;
#endif
if (!subloadAllGlyphsTogether)
{
if (newTextureObject)
{
for(GlyphRefList::const_iterator itr=_glyphs.begin();
itr!=_glyphs.end();
++itr)
{
(*itr)->subload();
}
}
else // just subload the new entries.
{
// default way of subloading as required.
//std::cout<<"subloading"<<std::endl;
for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin();
itr!=glyphsWereSubloading.end();
++itr)
{
(*itr)->subload();
}
}
// clear the list since we have now subloaded them.
glyphsWereSubloading.clear();
}
else
{
OSG_INFO<<"osgText::Font loading all glyphs as a single subload."<<std::endl;
// Octane has bugs in OGL driver which mean that subloads smaller
// than 32x32 produce errors, and also cannot handle general alignment,
// so to get round this copy all glyphs into a temporary image and
// then subload the whole lot in one go.
int tsize = getTextureHeight() * getTextureWidth();
unsigned char *local_data = new unsigned char[tsize];
memset( local_data, 0L, tsize);
for(GlyphRefList::const_iterator itr=_glyphs.begin();
itr!=_glyphs.end();
++itr)
{
//(*itr)->subload();
// Rather than subloading to graphics, we'll write the values
// of the glyphs into some intermediate data and subload the
// whole thing at the end
for( int t = 0; t < (*itr)->t(); t++ )
{
for( int s = 0; s < (*itr)->s(); s++ )
{
int sindex = (t*(*itr)->s()+s);
int dindex =
((((*itr)->getTexturePositionY()+t) * getTextureWidth()) +
((*itr)->getTexturePositionX()+s));
const unsigned char *sptr = &(*itr)->data()[sindex];
unsigned char *dptr = &local_data[dindex];
(*dptr) = (*sptr);
}
}
}
// clear the list since we have now subloaded them.
glyphsWereSubloading.clear();
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE)
glPixelStorei(GL_UNPACK_ROW_LENGTH,getTextureWidth());
#endif
// Subload the image once
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
getTextureWidth(),
getTextureHeight(),
OSGTEXT_GLYPH_FORMAT, GL_UNSIGNED_BYTE, local_data);
#if defined(OSG_GLES2_AVAILABLE)
if (requiresGenerateMipmapCall) glGenerateMipmap(GL_TEXTURE_2D);
#endif
delete [] local_data;
}
}
else
{
// OSG_INFO << "no need to subload "<<std::endl;
}
// if (generateMipMapTurnedOn)
// {
// glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
// }
_image->copySubImage(glyph->getTexturePositionX(), glyph->getTexturePositionY(), 0, glyph);
_image->dirty();
} }
void GlyphTexture::setThreadSafeRefUnref(bool threadSafe) void GlyphTexture::setThreadSafeRefUnref(bool threadSafe)
@ -432,19 +145,22 @@ void GlyphTexture::resizeGLObjectBuffers(unsigned int maxSize)
osg::Image* GlyphTexture::createImage() osg::Image* GlyphTexture::createImage()
{ {
osg::ref_ptr<osg::Image> image = new osg::Image; if (!_image)
image->allocateImage(getTextureWidth(), getTextureHeight(), 1, OSGTEXT_GLYPH_FORMAT, GL_UNSIGNED_BYTE);
memset(image->data(), 0, image->getTotalSizeInBytes());
for(GlyphRefList::iterator itr = _glyphs.begin();
itr != _glyphs.end();
++itr)
{ {
Glyph* glyph = itr->get(); _image = new osg::Image;
image->copySubImage(glyph->getTexturePositionX(), glyph->getTexturePositionY(), 0, glyph); _image->allocateImage(getTextureWidth(), getTextureHeight(), 1, OSGTEXT_GLYPH_FORMAT, GL_UNSIGNED_BYTE);
memset(_image->data(), 0, _image->getTotalSizeInBytes());
for(GlyphRefList::iterator itr = _glyphs.begin();
itr != _glyphs.end();
++itr)
{
Glyph* glyph = itr->get();
_image->copySubImage(glyph->getTexturePositionX(), glyph->getTexturePositionY(), 0, glyph);
}
} }
return image.release(); return _image.get();
} }
// all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK. // all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK.