| 50 | } |
| 51 | |
| 52 | GLuint ThumbnailCache::texture_for(const std::string& name, |
| 53 | std::uint64_t revision, |
| 54 | oid::Stage* stage) { |
| 55 | const auto it = entries_.find(name); |
| 56 | |
| 57 | if (const bool up_to_date = |
| 58 | it != entries_.end() && it->second.revision == revision; |
| 59 | up_to_date) { |
| 60 | return it->second.failed ? 0 : it->second.tex; |
| 61 | } |
| 62 | |
| 63 | // Entry missing (first time this name is seen) or stale (a re-plot |
| 64 | // bumped the buffer's revision). Budgeted to at most one icon render |
| 65 | // per frame across all callers (see class doc comment): if the budget |
| 66 | // is already spent this frame, or the Stage isn't ready yet, fall back |
| 67 | // to whatever texture is already cached -- a stale thumbnail reads |
| 68 | // better than the row flashing blank on every re-plot -- rather than |
| 69 | // forcing a render right now. |
| 70 | if (rendered_this_frame_ || stage == nullptr) { |
| 71 | return (it != entries_.end() && !it->second.failed) ? it->second.tex |
| 72 | : 0; |
| 73 | } |
| 74 | |
| 75 | rendered_this_frame_ = true; |
| 76 | |
| 77 | // Reuse the cached texture object across re-renders (same fixed |
| 78 | // render_w_ x render_h_ size every time, for the life of this instance); |
| 79 | // only allocate a new one the first time this name is seen. |
| 80 | GLuint tex = (it != entries_.end()) ? it->second.tex : 0; |
| 81 | if (tex == 0) { |
| 82 | canvas_.glGenTextures(1, &tex); |
| 83 | canvas_.glBindTexture(GL_TEXTURE_2D, tex); |
| 84 | canvas_.glTexParameteri( |
| 85 | GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 86 | canvas_.glTexParameteri( |
| 87 | GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 88 | canvas_.glTexParameteri( |
| 89 | GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 90 | canvas_.glTexParameteri( |
| 91 | GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 92 | } |
| 93 | |
| 94 | if (!canvas_.render_buffer_icon(*stage, render_w_, render_h_)) { |
| 95 | // A previously-good icon exists for this name (same fixed |
| 96 | // render_w_ x render_h_ texture object, just holding an older |
| 97 | // frame's pixels): keep serving it rather than overwriting the |
| 98 | // entry with failed=true, which would blank the row -- a stale |
| 99 | // thumbnail reads better than the row flashing blank (class doc |
| 100 | // comment above). Deliberately leave entries_[name] untouched |
| 101 | // (still at its old revision, not `revision`) rather than |
| 102 | // insert_or_assign-ing a "failed" record at the new revision: that |
| 103 | // keeps up_to_date false on the next texture_for() call for this |
| 104 | // name, so the render is retried next frame (budget permitting) |
| 105 | // instead of being stuck until another revision bump ever comes. |
| 106 | // No texture is allocated/leaked here: `tex` above is the reused |
| 107 | // existing object, not a fresh glGenTextures() result. |
| 108 | if (it != entries_.end() && !it->second.failed && it->second.tex != 0) { |
| 109 | return it->second.tex; |
nothing calls this directly
no test coverage detected