Skip to content

Commit

Permalink
Fixed|LogWidget: Rendering glitch related to atlas optimization
Browse files Browse the repository at this point in the history
As the entry atlas is used in a dynamic fashion, defragmentation is needed
to free up fragmented space. However, if defragmentation occurs during the
rendering of entries, the generated geometry becomes obsolete as the UV
coordinates of all entries will likely change. Therefore, LogWidget needs
to redo the visible range of entries if the atlas is optimized.
  • Loading branch information
skyjake committed Jun 5, 2013
1 parent 1584a52 commit bce1edf
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 16 deletions.
62 changes: 46 additions & 16 deletions doomsday/client/src/ui/widgets/logwidget.cpp
Expand Up @@ -39,7 +39,9 @@

using namespace de;

DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
DENG2_PIMPL(LogWidget),
DENG2_OBSERVES(Atlas, Reposition),
public Font::RichFormat::IStyle
{
typedef GLBufferT<Vertex2TexRgba> VertexBuf;

Expand Down Expand Up @@ -330,6 +332,7 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
VertexBuf *buf;
VertexBuf *bgBuf;
AtlasTexture *entryAtlas;
bool entryAtlasLayoutChanged;
Drawable contents;
Drawable background;
GLUniform uMvpMatrix;
Expand Down Expand Up @@ -359,6 +362,7 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
scrollBarWidth(0),
buf(0),
entryAtlas(0),
entryAtlasLayoutChanged(false),
uMvpMatrix ("uMvpMatrix", GLUniform::Mat4),
uTex ("uTex", GLUniform::Sampler2D),
uShadowColor("uColor", GLUniform::Vec4),
Expand Down Expand Up @@ -503,6 +507,7 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
entryAtlas = AtlasTexture::newWithRowAllocator(
Atlas::BackingStore | Atlas::AllowDefragment,
GLTexture::maximumSize().min(Atlas::Size(2048, 1024)));
entryAtlas->audienceForReposition += this;

Image solidWhitePixel = Image::solidColor(Image::Color(255, 255, 255, 255),
Image::Size(1, 1));
Expand Down Expand Up @@ -535,6 +540,14 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
background.clear();
}

void atlasContentRepositioned(Atlas &atlas)
{
if(entryAtlas == &atlas)
{
entryAtlasLayoutChanged = true;
}
}

duint contentWidth() const
{
return de::max(margin, self.rule().width().valuei() - 2 * margin);
Expand Down Expand Up @@ -719,29 +732,46 @@ DENG2_PIMPL(LogWidget), public Font::RichFormat::IStyle
//prune();

clampVisibleOffset(contentSize.y);

// Draw in reverse, as much as we need.
int yBottom = contentSize.y + visibleOffset;

maxScroll = maxVisibleOffset(contentSize.y);
visibleRange = Rangei(-1, -1);
maxScroll = maxVisibleOffset(contentSize.y);

VertexBuf::Builder verts;

// Copy all visible entries to the buffer.
for(int idx = cache.size() - 1; yBottom > -contentOffset && idx >= 0; --idx)
for(int attempt = 0; attempt < 2; ++attempt)
{
CacheEntry *entry = cache[idx];
yBottom -= entry->height();
// Draw in reverse, as much as we need.
int yBottom = contentSize.y + visibleOffset;
visibleRange = Rangei(-1, -1);
entryAtlasLayoutChanged = false;

if(yBottom + contentOffset < contentSize.y)
// Find the visible range and update all visible entries.
for(int idx = cache.size() - 1; yBottom > -contentOffset && idx >= 0; --idx)
{
// This entry is visible.
entry->make(verts, yBottom);
CacheEntry *entry = cache[idx];
yBottom -= entry->height();

if(visibleRange.end == -1) visibleRange.end = idx;
visibleRange.start = idx;
if(yBottom + contentOffset < contentSize.y)
{
// Rasterize and allocate if needed.
entry->make(verts, yBottom);

// Update the visible range.
if(visibleRange.end == -1)
{
visibleRange.end = idx;
}
visibleRange.start = idx;
}
}

if(entryAtlasLayoutChanged)
{
// Oops, the atlas was optimized during the loop and some items'
// positions are obsolete.
verts.clear();
continue;
}

break;
}

// Draw the scroll indicator.
Expand Down
6 changes: 6 additions & 0 deletions doomsday/libdeng2/include/de/core/range.h
Expand Up @@ -62,6 +62,12 @@ struct Range
inline bool operator > (Type const &value) const {
return start > value && end > value;
}
inline bool operator <= (Type const &value) const {
return !(*this > value);
}
inline bool operator >= (Type const &value) const {
return !(*this < value);
}
inline Range<Type> operator + (Type offset) const {
return Range<Type>(start + offset, end + offset);
}
Expand Down

0 comments on commit bce1edf

Please sign in to comment.