diff --git a/interface/src/Util.cpp b/interface/src/Util.cpp index 9d7f5518d0..5399af8e75 100644 --- a/interface/src/Util.cpp +++ b/interface/src/Util.cpp @@ -197,6 +197,11 @@ static TextRenderer* textRenderer(int mono) { } } +void renderTextRenderer(int mono) { + textRenderer(mono)->executeDrawBatch(); + textRenderer(mono)->clearDrawBatch(); +} + int widthText(float scale, int mono, char const* string) { return textRenderer(mono)->computeWidth(string) * (scale / 0.10); } diff --git a/interface/src/Util.h b/interface/src/Util.h index 02cfd99f9a..acdbdb6f75 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -31,6 +31,8 @@ float widthChar(float scale, int mono, char ch); void drawText(int x, int y, float scale, float radians, int mono, char const* string, const float* color); +void renderTextRenderer(int mono); + void drawvec3(int x, int y, float scale, float radians, float thick, int mono, glm::vec3 vec, float r=1.0, float g=1.0, float b=1.0); diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index c4f6b328ef..5f00d84b90 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -100,12 +100,25 @@ int TextRenderer::draw(int x, int y, const char* str) { glTexCoord2f(ls, tt); glVertex2f(left, top); glEnd(); - +/* + const int NUM_COORDS_PER_GLYPH = 16; + float vertexBuffer[NUM_COORDS_PER_GLYPH] = { ls, bt, left, bottom, rs, bt, right, bottom, rs, tt, right, top, ls, tt, left, top }; + gpu::Buffer::Size offset = sizeof(vertexBuffer)*_numGlyphsBatched; + if ((offset + sizeof(vertexBuffer)) > _glyphsBuffer.getSize()) { + _glyphsBuffer.append(sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } else { + _glyphsBuffer.setSubData(offset, sizeof(vertexBuffer), (gpu::Buffer::Byte*) vertexBuffer); + } + _numGlyphsBatched++; +*/ x += glyph.width(); } glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + // executeDrawBatch(); + // clearDrawBatch(); + return maxHeight; } @@ -131,8 +144,10 @@ TextRenderer::TextRenderer(const Properties& properties) : _x(IMAGE_SIZE), _y(IMAGE_SIZE), _rowHeight(0), - _color(properties.color) { - + _color(properties.color), + _glyphsBuffer(), + _numGlyphsBatched(0) +{ _font.setKerning(false); } @@ -228,9 +243,248 @@ const Glyph& TextRenderer::getGlyph(char c) { return glyph; } +void TextRenderer::executeDrawBatch() { + if (_numGlyphsBatched<=0) { + return; + } + + glEnable(GL_TEXTURE_2D); + + GLuint textureID = 0; + glBindTexture(GL_TEXTURE_2D, textureID); + + gpu::backend::syncGPUObject(_glyphsBuffer); + GLuint vbo = _glyphsBuffer.getGLBufferObject(); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + const int NUM_POS_COORDS = 2; + const int NUM_TEX_COORDS = 2; + const int VERTEX_STRIDE = (NUM_POS_COORDS + NUM_TEX_COORDS) * sizeof(float); + const int VERTEX_TEXCOORD_OFFSET = NUM_POS_COORDS * sizeof(float); + glVertexPointer(2, GL_FLOAT, VERTEX_STRIDE, 0); + glTexCoordPointer(2, GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_TEXCOORD_OFFSET ); + + glDrawArrays(GL_QUADS, 0, _numGlyphsBatched * 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + +} + +void TextRenderer::clearDrawBatch() { + _numGlyphsBatched = 0; +} + QHash TextRenderer::_instances; Glyph::Glyph(int textureID, const QPoint& location, const QRect& bounds, int width) : _textureID(textureID), _location(location), _bounds(bounds), _width(width) { } +using namespace gpu; + +Buffer::Size Buffer::Sysmem::allocateMemory(Byte** dataAllocated, Size size) { + if ( !dataAllocated ) { + qWarning() << "Buffer::Sysmem::allocateMemory() : Must have a valid dataAllocated pointer."; + return NOT_ALLOCATED; + } + + // Try to allocate if needed + Size newSize = 0; + if (size > 0) { + // Try allocating as much as the required size + one block of memory + newSize = size; + (*dataAllocated) = new Byte[newSize]; + // Failed? + if (!(*dataAllocated)) { + qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; + return NOT_ALLOCATED; + } + } + + // Return what's actually allocated + return newSize; +} + +void Buffer::Sysmem::deallocateMemory(Byte* dataAllocated, Size size) { + if (dataAllocated) { + delete[] dataAllocated; + } +} + +Buffer::Sysmem::Sysmem() : + _data(NULL), + _size(0), + _stamp(0) +{ +} + +Buffer::Sysmem::Sysmem(Size size, const Byte* bytes) : + _data(NULL), + _size(0), + _stamp(0) +{ + if (size > 0) { + _size = allocateMemory(&_data, size); + if (_size >= size) { + if (bytes) { + memcpy(_data, bytes, size); + } + } + } +} + +Buffer::Sysmem::~Sysmem() { + deallocateMemory( _data, _size ); + _data = NULL; + _size = 0; +} + +Buffer::Size Buffer::Sysmem::allocate(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return 0; + } + newSize = allocated; + } + // Allocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::resize(Size size) { + if (size != _size) { + Byte* newData = 0; + Size newSize = 0; + if (size > 0) { + Size allocated = allocateMemory(&newData, size); + if (allocated == NOT_ALLOCATED) { + // early exit because allocation failed + return _size; + } + newSize = allocated; + // Restore back data from old buffer in the new one + if (_data) { + Size copySize = ((newSize < _size)? newSize: _size); + memcpy( newData, _data, copySize); + } + } + // Reallocation was successful, can delete previous data + deallocateMemory(_data, _size); + _data = newData; + _size = newSize; + _stamp++; + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setData( Size size, const Byte* bytes ) { + if (allocate(size) == size) { + if (bytes) { + memcpy( _data, bytes, _size ); + _stamp++; + } + } + return _size; +} + +Buffer::Size Buffer::Sysmem::setSubData( Size offset, Size size, const Byte* bytes) { + if (((offset + size) <= getSize()) && bytes) { + memcpy( _data + offset, bytes, size ); + _stamp++; + return size; + } + return 0; +} + +Buffer::Size Buffer::Sysmem::append(Size size, const Byte* bytes) { + if (size > 0) { + Size oldSize = getSize(); + Size totalSize = oldSize + size; + if (resize(totalSize) == totalSize) { + return setSubData(oldSize, size, bytes); + } + } + return 0; +} + +Buffer::Buffer() : + _sysmem(NULL), + _gpuObject(NULL) { + _sysmem = new Sysmem(); +} + +Buffer::~Buffer() { + if (_sysmem) { + delete _sysmem; + _sysmem = 0; + } + if (_gpuObject) { + delete _gpuObject; + _gpuObject = 0; + } +} + +Buffer::Size Buffer::resize(Size size) { + return editSysmem().resize(size); +} + +Buffer::Size Buffer::setData(Size size, const Byte* data) { + return editSysmem().setData(size, data); +} + +Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) { + return editSysmem().setSubData( offset, size, data); +} + +Buffer::Size Buffer::append(Size size, const Byte* data) { + return editSysmem().append( size, data); +} + +namespace gpu { +namespace backend { + +void syncGPUObject(const Buffer& buffer) { + BufferObject* object = buffer.getGPUObject(); + + if (object && (object->_stamp == buffer.getSysmem().getStamp())) { + return; + } + + // need to have a gpu object? + if (!object) { + object = new BufferObject(); + glGenBuffers(1, &object->_buffer); + buffer.setGPUObject(object); + } + + // Now let's update the content of the bo with the sysmem version + //if (object->_size < buffer.getSize()) { + glBindBuffer(GL_COPY_WRITE_BUFFER, object->_buffer); + glBufferData(GL_COPY_WRITE_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().read(), GL_STATIC_DRAW); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); + object->_stamp = buffer.getSysmem().getStamp(); + object->_size = buffer.getSysmem().getSize(); + //} +} + +}; +}; \ No newline at end of file diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index d3340462d5..ec1c9a088e 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -19,6 +19,8 @@ #include #include +#include + #include "InterfaceConfig.h" // a special "character" that renders as a solid block @@ -35,6 +37,138 @@ const char SOLID_BLOCK_CHAR = 127; class Glyph; +namespace gpu { +class Buffer; +typedef int Stamp; + +namespace backend { + + class BufferObject { + public: + Stamp _stamp; + GLuint _buffer; + GLuint _size; + + BufferObject() : + _stamp(0), + _buffer(0), + _size(0) + {} + }; + void syncGPUObject(const Buffer& buffer); +}; + +class Buffer { +public: + + typedef unsigned char Byte; + typedef unsigned int Size; + + static const Size MIN_ALLOCATION_BLOCK_SIZE = 256; + static const Size NOT_ALLOCATED = -1; + + Buffer(); + Buffer(const Buffer& buf ); + ~Buffer(); + + // The size in bytes of data stored in the buffer + inline Size getSize() const { return getSysmem().getSize(); } + inline const Byte* getData() const { return getSysmem().read(); } + + // Resize the buffer + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData(Size size, const Byte* data); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setSubData(Size offset, Size size, const Byte* data); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // this is a temporary hack so the current rendering code can access the underneath gl Buffer Object + // TODO: remove asap, when the backend is doing more of the gl features + inline GLuint getGLBufferObject() const { backend::syncGPUObject(*this); return getGPUObject()->_buffer; } + +protected: + + // Sysmem is the underneath cache for the data in ram. + class Sysmem { + public: + + Sysmem(); + Sysmem(Size size , const Byte* bytes); + ~Sysmem(); + + Size getSize() const { return _size; } + + // Allocate the byte array + // \param pSize The nb of bytes to allocate, if already exist, content is lost. + // \return The nb of bytes allocated, nothing if allready the appropriate size. + Size allocate(Size pSize); + + // Resize the byte array + // Keep previous data [0 to min(pSize, mSize)] + Size resize(Size pSize); + + // Assign data bytes and size (allocate for size, then copy bytes if exists) + Size setData( Size size, const Byte* bytes ); + + // Update Sub data, + // doesn't allocate and only copy size * bytes at the offset location + // only if all fits in the existing allocated buffer + Size setSubData( Size offset, Size size, const Byte* bytes); + + // Append new data at the end of the current buffer + // do a resize( size + getSIze) and copy the new data + // \return the number of bytes copied + Size append(Size size, const Byte* data); + + // Access the byte array. + // The edit version allow to map data. + inline const Byte* read() const { return _data; } + inline Byte* edit() { _stamp++; return _data; } + + template< typename T > + const T* read() const { return reinterpret_cast< T* > ( _data ); } + template< typename T > + T* edit() const { _stamp++; return reinterpret_cast< T* > ( _data ); } + + // Access the current version of the sysmem, used to compare if copies are in sync + inline Stamp getStamp() const { return _stamp; } + + static Size allocateMemory(Byte** memAllocated, Size size); + static void deallocateMemory(Byte* memDeallocated, Size size); + + private: + Sysmem(const Sysmem& sysmem) {} + Sysmem &operator=(const Sysmem &other) {return *this;} + + Byte* _data; + Size _size; + Stamp _stamp; + }; + + Sysmem* _sysmem; + + typedef backend::BufferObject GPUObject; + mutable backend::BufferObject* _gpuObject; + + inline const Sysmem& getSysmem() const { assert(_sysmem); return (*_sysmem); } + inline Sysmem& editSysmem() { assert(_sysmem); return (*_sysmem); } + + inline GPUObject* getGPUObject() const { return _gpuObject; } + inline void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + + friend void backend::syncGPUObject(const Buffer& buffer); +}; + +}; + class TextRenderer { public: @@ -64,6 +198,8 @@ public: int computeWidth(char ch); int computeWidth(const char* str); + void executeDrawBatch(); + void clearDrawBatch(); private: TextRenderer(const Properties& properties); @@ -100,6 +236,10 @@ private: // text color QColor _color; + // Graphics Buffer containing the current accumulated glyphs to render + gpu::Buffer _glyphsBuffer; + int _numGlyphsBatched; + static QHash _instances; };