root/OpenSceneGraph/trunk/src/osg/TextureRectangle.cpp @ 12912

Revision 12912, 19.2 kB (checked in by robert, 3 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osg/GLExtensions>
15#include <osg/TextureRectangle>
16#include <osg/State>
17#include <osg/GLU>
18#include <osg/Notify>
19
20#include <osg/Timer>
21
22#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE
23#define GL_UNPACK_CLIENT_STORAGE_APPLE    0x85B2
24#endif
25
26#ifndef GL_APPLE_vertex_array_range
27#define GL_VERTEX_ARRAY_RANGE_APPLE       0x851D
28#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E
29#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F
30#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521
31#define GL_STORAGE_CACHED_APPLE           0x85BE
32#define GL_STORAGE_SHARED_APPLE           0x85BF
33#endif
34
35// #define DO_TIMING
36
37using namespace osg;
38
39TextureRectangle::TextureRectangle():
40    _textureWidth(0),
41    _textureHeight(0)
42{
43    setWrap(WRAP_S, CLAMP);
44    setWrap(WRAP_T, CLAMP);
45
46    setFilter(MIN_FILTER, LINEAR);
47    setFilter(MAG_FILTER, LINEAR);
48}
49
50TextureRectangle::TextureRectangle(Image* image):
51            _textureWidth(0),
52            _textureHeight(0)
53{
54    setWrap(WRAP_S, CLAMP);
55    setWrap(WRAP_T, CLAMP);
56
57    setFilter(MIN_FILTER, LINEAR);
58    setFilter(MAG_FILTER, LINEAR);
59   
60    setImage(image);
61}
62
63TextureRectangle::TextureRectangle(const TextureRectangle& text,const CopyOp& copyop):
64    Texture(text,copyop),
65    _image(copyop(text._image.get())),
66    _textureWidth(text._textureWidth),
67    _textureHeight(text._textureHeight),
68    _subloadCallback(text._subloadCallback)
69{
70}
71
72TextureRectangle::~TextureRectangle()
73{
74}
75
76int TextureRectangle::compare(const StateAttribute& sa) const
77{
78    // check the types are equal and then create the rhs variable
79    // used by the COMPARE_StateAttribute_Parameter macros below.
80    COMPARE_StateAttribute_Types(TextureRectangle,sa)
81
82    if (_image!=rhs._image) // smart pointer comparison.
83    {
84        if (_image.valid())
85        {
86            if (rhs._image.valid())
87            {
88                int result = _image->compare(*rhs._image);
89                if (result!=0) return result;
90            }
91            else
92            {
93                return 1; // valid lhs._image is greater than null.
94            }
95        }
96        else if (rhs._image.valid())
97        {
98            return -1; // valid rhs._image is greater than null.
99        }
100    }
101
102    if (!_image && !rhs._image)
103    {
104        // no image attached to either Texture2D
105        // but could these textures already be downloaded?
106        // check the _textureObjectBuffer to see if they have been
107        // downloaded
108
109        int result = compareTextureObjects(rhs);
110        if (result!=0) return result;
111    }
112
113    int result = compareTexture(rhs);
114    if (result!=0) return result;
115
116    // compare each parameter in turn against the rhs.
117    COMPARE_StateAttribute_Parameter(_textureWidth)
118    COMPARE_StateAttribute_Parameter(_textureHeight)
119    COMPARE_StateAttribute_Parameter(_subloadCallback)
120
121    return 0; // passed all the above comparison macros, must be equal.
122}
123
124void TextureRectangle::setImage(Image* image)
125{
126    if (_image == image) return;
127
128    if (_image.valid() && _image->requiresUpdateCall())
129    {
130        setUpdateCallback(0);
131        setDataVariance(osg::Object::STATIC);
132    }
133
134    // delete old texture objects.
135    dirtyTextureObject();
136
137    _image = image;
138
139    if (_image.valid() && _image->requiresUpdateCall())
140    {
141        setUpdateCallback(new Image::UpdateCallback());
142        setDataVariance(osg::Object::DYNAMIC);
143    }
144}
145
146
147void TextureRectangle::apply(State& state) const
148{
149    static bool s_rectangleSupported =
150            OSG_GL3_FEATURES ||
151            isGLExtensionSupported(state.getContextID(),"GL_ARB_texture_rectangle") ||
152            isGLExtensionSupported(state.getContextID(),"GL_EXT_texture_rectangle") ||
153            isGLExtensionSupported(state.getContextID(),"GL_NV_texture_rectangle");
154
155    if (!s_rectangleSupported)
156    {
157        OSG_WARN<<"Warning: TextureRectangle::apply(..) failed, texture rectangle is not support by your OpenGL drivers."<<std::endl;
158        return;
159    }
160
161    // get the contextID (user defined ID of 0 upwards) for the
162    // current OpenGL context.
163    const unsigned int contextID = state.getContextID();
164
165    Texture::TextureObjectManager* tom = Texture::getTextureObjectManager(contextID).get();
166    ElapsedTime elapsedTime(&(tom->getApplyTime()));
167    tom->getNumberApplied()++;
168
169
170    // get the texture object for the current contextID.
171    TextureObject* textureObject = getTextureObject(contextID);
172
173    if (textureObject)
174    {
175        if (_image.valid() && getModifiedCount(contextID) != _image->getModifiedCount())
176        {
177            // compute the internal texture format, this set the _internalFormat to an appropriate value.
178            computeInternalFormat();
179
180            GLsizei new_width, new_height, new_numMipmapLevels;
181
182            // compute the dimensions of the texture.
183            computeRequiredTextureDimensions(state, *_image, new_width, new_height, new_numMipmapLevels);
184
185            if (!textureObject->match(GL_TEXTURE_RECTANGLE, new_numMipmapLevels, _internalFormat, new_width, new_height, 1, _borderWidth))
186            {
187                Texture::releaseTextureObject(contextID, _textureObjectBuffer[contextID].get());
188                _textureObjectBuffer[contextID] = 0;
189                textureObject = 0;
190            }
191        }
192    }
193
194    if (textureObject)
195    {
196        textureObject->bind();
197
198        if (getTextureParameterDirty(state.getContextID()))
199            applyTexParameters(GL_TEXTURE_RECTANGLE, state);
200
201        if (_subloadCallback.valid())
202        {
203            _subloadCallback->subload(*this, state);
204        }
205        else if (_image.valid() && getModifiedCount(contextID) != _image->getModifiedCount())
206        {
207            applyTexImage_subload(GL_TEXTURE_RECTANGLE, _image.get(), state, _textureWidth, _textureHeight, _internalFormat);
208 
209            // update the modified count to show that it is upto date.
210            getModifiedCount(contextID) = _image->getModifiedCount();
211        }
212    }
213    else if (_subloadCallback.valid())
214    {
215        // we don't have a applyTexImage1D_subload yet so can't reuse.. so just generate a new texture object.       
216        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_RECTANGLE);
217
218        textureObject->bind();
219
220        applyTexParameters(GL_TEXTURE_RECTANGLE, state);
221
222        _subloadCallback->load(*this, state);
223
224        textureObject->setAllocated(1,_internalFormat,_textureWidth,_textureHeight,1,0);
225
226        // in theory the following line is redundant, but in practice
227        // have found that the first frame drawn doesn't apply the textures
228        // unless a second bind is called?!!
229        // perhaps it is the first glBind which is not required...
230        //glBindTexture(GL_TEXTURE_RECTANGLE, handle);
231    }
232    else if (_image.valid() && _image->data())
233    {
234
235
236        // keep the image around at least till we go out of scope.
237        osg::ref_ptr<osg::Image> image = _image;
238
239        // compute the internal texture format, this set the _internalFormat to an appropriate value.
240        computeInternalFormat();
241
242        _textureWidth = image->s();
243        _textureHeight = image->t();
244
245        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
246                this, contextID,GL_TEXTURE_RECTANGLE,1,_internalFormat,_textureWidth,_textureHeight,1,0);
247
248        textureObject->bind();
249
250        applyTexParameters(GL_TEXTURE_RECTANGLE, state);
251
252        if (textureObject->isAllocated())
253        {
254            applyTexImage_subload(GL_TEXTURE_RECTANGLE, _image.get(), state, _textureWidth, _textureHeight, _internalFormat);
255        }
256        else
257        {
258            applyTexImage_load(GL_TEXTURE_RECTANGLE, _image.get(), state, _textureWidth, _textureHeight);
259            textureObject->setAllocated(true);
260        }
261
262        // unref image data?
263        if (isSafeToUnrefImageData(state) && _image->getDataVariance()==STATIC)
264        {
265            TextureRectangle* non_const_this = const_cast<TextureRectangle*>(this);
266            non_const_this->_image = NULL;
267        }
268    }
269    else if ( (_textureWidth!=0) && (_textureHeight!=0) && (_internalFormat!=0) )
270    {
271        _textureObjectBuffer[contextID] = textureObject = generateTextureObject(
272                this, contextID,GL_TEXTURE_RECTANGLE,0,_internalFormat,_textureWidth,_textureHeight,1,0);
273       
274        textureObject->bind();
275
276        applyTexParameters(GL_TEXTURE_RECTANGLE,state);
277
278        // no image present, but dimensions at set so lets create the texture
279        glTexImage2D( GL_TEXTURE_RECTANGLE, 0, _internalFormat,
280                     _textureWidth, _textureHeight, _borderWidth,
281                     _sourceFormat ? _sourceFormat : _internalFormat,
282                     _sourceType ? _sourceType : GL_UNSIGNED_BYTE,
283                     0);               
284                     
285        if (_readPBuffer.valid())
286        {
287            _readPBuffer->bindPBufferToTexture(GL_FRONT);
288        }
289       
290    }
291    else
292    {
293        glBindTexture(GL_TEXTURE_RECTANGLE, 0);
294    }
295}
296
297void TextureRectangle::applyTexImage_load(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight) const
298{
299    // if we don't have a valid image we can't create a texture!
300    if (!image || !image->data())
301        return;
302
303    // get the contextID (user defined ID of 0 upwards) for the
304    // current OpenGL context.
305    const unsigned int contextID = state.getContextID();
306    const Extensions* extensions = getExtensions(contextID,true);
307
308    // update the modified count to show that it is upto date.
309    getModifiedCount(contextID) = image->getModifiedCount();
310
311    // compute the internal texture format, sets _internalFormat.
312    computeInternalFormat();
313
314    glPixelStorei(GL_UNPACK_ALIGNMENT, image->getPacking());
315    glPixelStorei(GL_UNPACK_ROW_LENGTH,image->getRowLength());
316
317    bool useClientStorage = extensions->isClientStorageSupported() && getClientStorageHint();
318    if (useClientStorage)
319    {
320        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_TRUE);
321
322        #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
323            glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_PRIORITY,0.0f);
324        #endif
325
326        #ifdef GL_TEXTURE_STORAGE_HINT_APPLE
327            glTexParameteri(target, GL_TEXTURE_STORAGE_HINT_APPLE , GL_STORAGE_CACHED_APPLE);
328        #endif
329    }
330
331    const unsigned char* dataPtr = image->data();
332    GLBufferObject* pbo = image->getOrCreateGLBufferObject(contextID);
333    if (pbo)
334    {
335        state.bindPixelBufferObject(pbo);
336        dataPtr = reinterpret_cast<unsigned char*>(pbo->getOffset(image->getBufferIndex()));
337    }
338
339    if(isCompressedInternalFormat(_internalFormat) && extensions->isCompressedTexImage2DSupported())
340    {
341        extensions->glCompressedTexImage2D(target, 0, _internalFormat,
342          image->s(), image->t(), 0,
343          image->getImageSizeInBytes(),
344          dataPtr);
345    }
346    else
347    {
348        glTexImage2D(target, 0, _internalFormat,
349          image->s(), image->t(), 0,
350          (GLenum)image->getPixelFormat(),
351          (GLenum)image->getDataType(),
352          dataPtr );
353    }
354   
355
356    if (pbo)
357    {
358        state.unbindPixelBufferObject();
359    }
360
361    inwidth = image->s();
362    inheight = image->t();
363
364    if (useClientStorage)
365    {
366        glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE,GL_FALSE);
367    }
368}
369
370void TextureRectangle::applyTexImage_subload(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight, GLint& inInternalFormat) const
371{
372    // if we don't have a valid image we can't create a texture!
373    if (!image || !image->data())
374        return;
375
376    if (image->s()!=inwidth || image->t()!=inheight || image->getInternalTextureFormat()!=inInternalFormat)
377    {
378        applyTexImage_load(target, image, state, inwidth, inheight);
379        return;
380    }
381
382
383    // get the contextID (user defined ID of 0 upwards) for the
384    // current OpenGL context.
385    const unsigned int contextID = state.getContextID();
386    const Extensions* extensions = getExtensions(contextID,true);
387
388
389    // update the modified count to show that it is upto date.
390    getModifiedCount(contextID) = image->getModifiedCount();
391
392    // compute the internal texture format, sets _internalFormat.
393    computeInternalFormat();
394
395    glPixelStorei(GL_UNPACK_ALIGNMENT, image->getPacking());
396    unsigned int rowLength = image->getRowLength();
397   
398
399#ifdef DO_TIMING
400    osg::Timer_t start_tick = osg::Timer::instance()->tick();
401    OSG_NOTICE<<"TextureRectangle::apply pixelFormat = "<<std::hex<<image->getPixelFormat()<<std::dec<<std::endl;
402#endif
403    const unsigned char* dataPtr = image->data();
404    GLBufferObject* pbo = image->getOrCreateGLBufferObject(contextID);
405    if (pbo)
406    {
407        state.bindPixelBufferObject(pbo);
408        dataPtr = reinterpret_cast<unsigned char*>(pbo->getOffset(image->getBufferIndex()));
409        rowLength = 0;
410#ifdef DO_TIMING
411        OSG_NOTICE<<"after PBO "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
412#endif
413    }
414
415    glPixelStorei(GL_UNPACK_ROW_LENGTH,rowLength);
416
417    if(isCompressedInternalFormat(_internalFormat) && extensions->isCompressedTexSubImage2DSupported())
418    {
419        extensions->glCompressedTexSubImage2D(target, 0,
420          0,0,
421          image->s(), image->t(),
422          (GLenum)image->getPixelFormat(),
423          (GLenum)image->getDataType(),
424          dataPtr);
425    }
426    else
427    {
428        glTexSubImage2D(target, 0,
429          0,0,
430          image->s(), image->t(),
431          (GLenum)image->getPixelFormat(),
432          (GLenum)image->getDataType(),
433          dataPtr);
434    }
435
436    if (pbo)
437    {
438        state.unbindPixelBufferObject();
439    }
440
441#ifdef DO_TIMING
442    OSG_NOTICE<<"glTexSubImage2D "<<osg::Timer::instance()->delta_m(start_tick,osg::Timer::instance()->tick())<<"ms"<<std::endl;
443#endif
444}
445
446void TextureRectangle::computeInternalFormat() const
447{
448    if (_image.valid()) computeInternalFormatWithImage(*_image);
449    else computeInternalFormatType();
450}
451
452void TextureRectangle::copyTexImage2D(State& state, int x, int y, int width, int height )
453{
454    const unsigned int contextID = state.getContextID();
455   
456    if (_internalFormat==0) _internalFormat=GL_RGBA;
457
458    // get the globj for the current contextID.
459    TextureObject* textureObject = getTextureObject(contextID);
460   
461    if (textureObject)
462    {
463        if (width==(int)_textureWidth && height==(int)_textureHeight)
464        {
465            // we have a valid texture object which is the right size
466            // so lets play clever and use copyTexSubImage2D instead.
467            // this allows use to reuse the texture object and avoid
468            // expensive memory allocations.
469            copyTexSubImage2D(state,0 ,0, x, y, width, height);
470            return;
471        }
472        // the relevent texture object is not of the right size so
473        // needs to been deleted   
474        // remove previously bound textures.
475        dirtyTextureObject();
476        // note, dirtyTextureObject() dirties all the texture objects for
477        // this texture, is this right?  Perhaps we should dirty just the
478        // one for this context.  Note sure yet will leave till later.
479        // RO July 2001.
480    }
481   
482   
483    // remove any previously assigned images as these are nolonger valid.
484    _image = NULL;
485
486    // switch off mip-mapping.
487    //
488    _textureObjectBuffer[contextID] = textureObject = generateTextureObject(this, contextID,GL_TEXTURE_RECTANGLE);
489
490    textureObject->bind();
491   
492    applyTexParameters(GL_TEXTURE_RECTANGLE,state);
493
494
495/*    bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
496    bool hardwareMipMapOn = false;
497    if (needHardwareMipMap)
498    {
499        const Extensions* extensions = getExtensions(contextID,true);
500        bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
501
502        hardwareMipMapOn = _useHardwareMipMapGeneration && generateMipMapSupported;
503       
504        if (!hardwareMipMapOn)
505        {
506            // have to swtich off mip mapping
507            OSG_NOTICE<<"Warning: Texture2D::copyTexImage2D(,,,,) switch of mip mapping as hardware support not available."<<std::endl;
508            _min_filter = LINEAR;
509        }
510    }
511*/   
512//    if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
513
514    glCopyTexImage2D( GL_TEXTURE_RECTANGLE, 0, _internalFormat, x, y, width, height, 0 );
515
516//    if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
517
518
519    _textureWidth = width;
520    _textureHeight = height;
521//    _numMipmapLevels = 1;
522   
523    textureObject->setAllocated(1,_internalFormat,_textureWidth,_textureHeight,1,0);
524
525
526    // inform state that this texture is the current one bound.
527    state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
528}
529
530void TextureRectangle::copyTexSubImage2D(State& state, int xoffset, int yoffset, int x, int y, int width, int height )
531{
532    const unsigned int contextID = state.getContextID();
533
534    if (_internalFormat==0) _internalFormat=GL_RGBA;
535
536    // get the texture object for the current contextID.
537    TextureObject* textureObject = getTextureObject(contextID);
538   
539    if (textureObject)
540    {
541        // we have a valid image
542        textureObject->bind();
543       
544        applyTexParameters(GL_TEXTURE_RECTANGLE,state);
545
546/*        bool needHardwareMipMap = (_min_filter != LINEAR && _min_filter != NEAREST);
547        bool hardwareMipMapOn = false;
548        if (needHardwareMipMap)
549        {
550            const Extensions* extensions = getExtensions(contextID,true);
551            bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
552
553            hardwareMipMapOn = _useHardwareMipMapGeneration && generateMipMapSupported;
554
555            if (!hardwareMipMapOn)
556            {
557                // have to swtich off mip mapping
558                OSG_NOTICE<<"Warning: Texture2D::copyTexImage2D(,,,,) switch of mip mapping as hardware support not available."<<std::endl;
559                _min_filter = LINEAR;
560            }
561        }
562*/
563//        if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
564
565        glCopyTexSubImage2D( GL_TEXTURE_RECTANGLE, 0, xoffset, yoffset, x, y, width, height);
566
567//        if (hardwareMipMapOn) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
568
569        // inform state that this texture is the current one bound.
570        state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), this);
571
572    }
573    else
574    {
575        // no texture object already exsits for this context so need to
576        // create it upfront - simply call copyTexImage2D.
577        copyTexImage2D(state,x,y,width,height);
578    }
579}
580
581void TextureRectangle::allocateMipmap(State&) const
582{
583    OSG_NOTICE<<"Warning: TextureRectangle::allocateMipmap(State&) called eroneously, GL_TEXTURE_RECTANGLE does not support mipmapping."<<std::endl;
584}
Note: See TracBrowser for help on using the browser.