Mercurial > hg > svgui
changeset 1090:c8c747783110 spectrogram-minor-refactor
Cut over to using the renderer (though it's very incomplete) and fix some cache problems and pointer lifetime misunderstandings
author | Chris Cannam |
---|---|
date | Tue, 05 Jul 2016 17:48:26 +0100 |
parents | c8683d94442a |
children | ac10a087e045 |
files | layer/Colour3DPlotRenderer.cpp layer/Colour3DPlotRenderer.h layer/LayerGeometryProvider.h layer/ScrollableImageCache.cpp layer/ScrollableImageCache.h layer/SpectrogramLayer.cpp |
diffstat | 6 files changed, 105 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/layer/Colour3DPlotRenderer.cpp Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/Colour3DPlotRenderer.cpp Tue Jul 05 17:48:26 2016 +0100 @@ -28,25 +28,22 @@ using namespace std; Colour3DPlotRenderer::RenderResult -Colour3DPlotRenderer::render(QPainter &paint, QRect rect) +Colour3DPlotRenderer::render(LayerGeometryProvider *v, QPainter &paint, QRect rect) { - return render(paint, rect, false); + return render(v, paint, rect, false); } Colour3DPlotRenderer::RenderResult -Colour3DPlotRenderer::renderTimeConstrained(QPainter &paint, QRect rect) +Colour3DPlotRenderer::renderTimeConstrained(LayerGeometryProvider *v, + QPainter &paint, QRect rect) { - return render(paint, rect, true); + return render(v, paint, rect, true); } Colour3DPlotRenderer::RenderResult -Colour3DPlotRenderer::render(QPainter &paint, QRect rect, bool timeConstrained) +Colour3DPlotRenderer::render(LayerGeometryProvider *v, + QPainter &paint, QRect rect, bool timeConstrained) { - LayerGeometryProvider *v = m_sources.geometryProvider; - if (!v) { - throw std::logic_error("no LayerGeometryProvider provided"); - } - sv_frame_t startFrame = v->getStartFrame(); int x0 = v->getXForViewX(rect.x()); @@ -57,22 +54,35 @@ m_cache.resize(v->getPaintSize()); m_cache.setZoomLevel(v->getZoomLevel()); + cerr << "cache start " << m_cache.getStartFrame() + << " view start " << startFrame + << " valid left " << m_cache.getValidLeft() + << " valid right " << m_cache.getValidRight() + << " x0 " << x0 + << " x1 " << x1 + << endl; + + if (m_cache.isValid()) { // some part of the cache is valid if (v->getXForFrame(m_cache.getStartFrame()) == v->getXForFrame(startFrame) && m_cache.getValidLeft() <= x0 && m_cache.getValidRight() >= x1) { - + + cerr << "cache hit" << endl; + // cache is valid for the complete requested area paint.drawImage(rect, m_cache.getImage(), rect); return { rect, {} }; } else { + cerr << "cache partial hit" << endl; + // cache doesn't begin at the right frame or doesn't // contain the complete view, but might be scrollable or // partially usable - m_cache.scrollTo(startFrame); + m_cache.scrollTo(v, startFrame); // if we are not time-constrained, then we want to paint // the whole area in one go; we don't return a partial @@ -87,6 +97,9 @@ } } } + } else { + // cache completely invalid + m_cache.setStartFrame(startFrame); } bool rightToLeft = false; @@ -104,6 +117,8 @@ } if (m_cache.isValid()) { + cerr << "cache somewhat valid" << endl; + // When rendering only a part of the cache, we need to make // sure that the part we're rendering is adjacent to (or // overlapping) a valid area of cache, if we have one. The @@ -125,7 +140,7 @@ rightToLeft = isLeftOfValidArea; } - renderToCache(x0, x1 - x0, rightToLeft, timeConstrained); + renderToCache(v, x0, x1 - x0, rightToLeft, timeConstrained); QRect pr = rect & m_cache.getValidArea(); paint.drawImage(pr.x(), pr.y(), m_cache.getImage(), @@ -161,7 +176,8 @@ } void -Colour3DPlotRenderer::renderToCache(int x0, int repaintWidth, +Colour3DPlotRenderer::renderToCache(LayerGeometryProvider *v, + int x0, int repaintWidth, bool rightToLeft, bool timeConstrained) { // Draw to the draw buffer, and then scale-copy from there. @@ -171,8 +187,6 @@ throw std::logic_error("no source model provided, or model not ready"); } - LayerGeometryProvider *v = m_sources.geometryProvider; // already checked - // The draw buffer contains a fragment at either our pixel // resolution (if there is more than one time-bin per pixel) or // time-bin resolution (if a time-bin spans more than one pixel). @@ -251,7 +265,7 @@ } else { for (int x = 0; x < drawWidth; ++x) { - sv_frame_t f0 = v->getFrameForX(x); + sv_frame_t f0 = v->getFrameForX(x0 + x); double s0 = double(f0 - model->getStartFrame()) / binResolution; binforx[x] = int(s0 + 0.0001); } @@ -269,8 +283,7 @@ } for (int y = 0; y < h; ++y) { - binfory[y] = - m_sources.verticalBinLayer->getBinForY(m_sources.geometryProvider, y); + binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); } int attainedWidth = renderDrawBuffer(repaintWidth, @@ -335,7 +348,6 @@ } } - int Colour3DPlotRenderer::renderDrawBuffer(int w, int h, const vector<int> &binforx, @@ -416,6 +428,10 @@ // distribute/interpolate ColumnOp::Column fullColumn = sourceModel->getColumn(sx); + + cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size() + << ", minbin " << minbin << ", maxbin " << maxbin << endl; + ColumnOp::Column column = vector<float>(fullColumn.data() + minbin, fullColumn.data() + maxbin + 1);
--- a/layer/Colour3DPlotRenderer.h Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/Colour3DPlotRenderer.h Tue Jul 05 17:48:26 2016 +0100 @@ -47,11 +47,9 @@ }; struct Sources { - Sources() : geometryProvider(0), verticalBinLayer(0), - source(0), peaks(0), fft(0) { } + Sources() : verticalBinLayer(0), source(0), peaks(0), fft(0) { } // These must all outlive this class - LayerGeometryProvider *geometryProvider; // always const VerticalBinLayer *verticalBinLayer; // always DenseThreeDimensionalModel *source; // always Dense3DModelPeakCache *peaks; // optionally @@ -98,14 +96,21 @@ /** * Render the requested area using the given painter, obtaining - * geometry (e.g. start frame) from the stored + * geometry (e.g. start frame) from the given * LayerGeometryProvider. * - * The whole rect will be rendered and the returned QRect will be - * equal to the passed QRect. (See renderTimeConstrained for an - * alternative that may render only part of the rect in cases - * where obtaining source data is slow and retaining - * responsiveness is important.) + * The whole of the supplied rect will be rendered and the + * returned QRect will be equal to the supplied QRect. (See + * renderTimeConstrained for an alternative that may render only + * part of the rect in cases where obtaining source data is slow + * and retaining responsiveness is important.) + * + * Note that Colour3DPlotRenderer retains internal cache state + * related to the size and position of the supplied + * LayerGeometryProvider. Although it is valid to call render() + * successively on the same Colour3DPlotRenderer with different + * LayerGeometryProviders, it will be much faster to use a + * dedicated Colour3DPlotRenderer for each LayerGeometryProvider. * * If the model to render from is not ready, this will throw a * std::logic_error exception. The model must be ready and the @@ -113,7 +118,7 @@ * that the LayerGeometryProvider returns valid results; it is the * caller's responsibility to ensure these. */ - RenderResult render(QPainter &paint, QRect rect); + RenderResult render(LayerGeometryProvider *v, QPainter &paint, QRect rect); /** * Render the requested area using the given painter, obtaining @@ -127,13 +132,21 @@ * rendered. Note that we always render the full requested height, * it's only width that is time-constrained. * + * Note that Colour3DPlotRenderer retains internal cache state + * related to the size and position of the supplied + * LayerGeometryProvider. Although it is valid to call render() + * successively on the same Colour3DPlotRenderer with different + * LayerGeometryProviders, it will be much faster to use a + * dedicated Colour3DPlotRenderer for each LayerGeometryProvider. + * * If the model to render from is not ready, this will throw a * std::logic_error exception. The model must be ready and the * layer requesting the render must not be dormant in its view, so * that the LayerGeometryProvider returns valid results; it is the * caller's responsibility to ensure these. */ - RenderResult renderTimeConstrained(QPainter &paint, QRect rect); + RenderResult renderTimeConstrained(LayerGeometryProvider *v, + QPainter &paint, QRect rect); private: Sources m_sources; @@ -154,8 +167,9 @@ // then repainting from cache to the requested painter. ScrollableImageCache m_cache; - RenderResult render(QPainter &paint, QRect rect, bool timeConstrained); - void renderToCache(int x0, int repaintWidth, + RenderResult render(LayerGeometryProvider *v, + QPainter &paint, QRect rect, bool timeConstrained); + void renderToCache(LayerGeometryProvider *v, int x0, int repaintWidth, bool rightToLeft, bool timeConstrained); int renderDrawBuffer(int w, int h, const std::vector<int> &binforx,
--- a/layer/LayerGeometryProvider.h Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/LayerGeometryProvider.h Tue Jul 05 17:48:26 2016 +0100 @@ -25,6 +25,22 @@ class View; class Layer; +/** + * Interface for classes that provide geometry information (such as + * size, start frame, and a large number of other properties) about + * the disposition of a layer. The main implementor of this interface + * is the View class, but other implementations may be used in + * different circumstances, e.g. as a proxy to handle hi-dpi + * coordinate mapping. + * + * Note it is expected that some implementations of this may be + * disposable, created on-the-fly for a single use. Code that receives + * a LayerGeometryProvider pointer as an argument to something should + * not, in general, store that pointer as it may be invalidated before + * the next use. Use getId() to instead obtain a persistent identifier + * for a LayerGeometryProvider, for example to establish whether the + * same one is being provided in two separate calls. + */ class LayerGeometryProvider { protected:
--- a/layer/ScrollableImageCache.cpp Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/ScrollableImageCache.cpp Tue Jul 05 17:48:26 2016 +0100 @@ -17,20 +17,18 @@ #include <iostream> using namespace std; -//#define DEBUG_SCROLLABLE_IMAGE_CACHE 1 +#define DEBUG_SCROLLABLE_IMAGE_CACHE 1 void -ScrollableImageCache::scrollTo(sv_frame_t newStartFrame) +ScrollableImageCache::scrollTo(LayerGeometryProvider *v, sv_frame_t newStartFrame) { - if (!m_v) throw std::logic_error("ScrollableImageCache: not associated with a LayerGeometryProvider"); - if (m_startFrame == newStartFrame) { // haven't moved return; } - int dx = (m_v->getXForFrame(m_startFrame) - - m_v->getXForFrame(newStartFrame)); + int dx = (v->getXForFrame(m_startFrame) - + v->getXForFrame(newStartFrame)); #ifdef DEBUG_SCROLLABLE_IMAGE_CACHE cerr << "ScrollableImageCache::scrollTo: start frame " << m_startFrame
--- a/layer/ScrollableImageCache.h Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/ScrollableImageCache.h Tue Jul 05 17:48:26 2016 +0100 @@ -37,8 +37,7 @@ class ScrollableImageCache { public: - ScrollableImageCache(const LayerGeometryProvider *v = 0) : - m_v(v), + ScrollableImageCache() : m_left(0), m_width(0), m_startFrame(0), @@ -112,11 +111,12 @@ } /** - * Set the new start frame for the cache, if possible also moving - * along any existing valid data within the cache so that it - * continues to be valid for the new start frame. + * Set the new start frame for the cache, according to the + * geometry of the supplied LayerGeometryProvider, if possible + * also moving along any existing valid data within the cache so + * that it continues to be valid for the new start frame. */ - void scrollTo(sv_frame_t newStartFrame); + void scrollTo(LayerGeometryProvider *v, sv_frame_t newStartFrame); /** * Take a left coordinate and width describing a region, and @@ -142,7 +142,6 @@ int imageWidth); private: - const LayerGeometryProvider *m_v; QImage m_image; int m_left; // of valid region int m_width; // of valid region
--- a/layer/SpectrogramLayer.cpp Tue Jul 05 12:20:56 2016 +0100 +++ b/layer/SpectrogramLayer.cpp Tue Jul 05 17:48:26 2016 +0100 @@ -52,6 +52,7 @@ #include <alloca.h> #endif +#define DEBUG_SPECTROGRAM 1 #define DEBUG_SPECTROGRAM_REPAINT 1 using namespace std; @@ -1158,7 +1159,7 @@ } double -SpectrogramLayer::getYForBin(LayerGeometryProvider *, double bin) const { +SpectrogramLayer::getYForBin(LayerGeometryProvider *, double) const { //!!! not implemented throw std::logic_error("not implemented"); } @@ -1525,7 +1526,7 @@ SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const { if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) { - m_imageCaches[view->getId()] = ScrollableImageCache(view); + m_imageCaches[view->getId()] = ScrollableImageCache(); } return m_imageCaches.at(view->getId()); } @@ -1535,7 +1536,11 @@ { Colour3DPlotRenderer *renderer = getRenderer(v); - + //!!! not time-constrained for now + Colour3DPlotRenderer::RenderResult result = renderer->render(v, paint, rect); + + //!!! do + (void)result; } Colour3DPlotRenderer * @@ -1544,11 +1549,10 @@ if (m_renderers.find(v->getId()) == m_renderers.end()) { Colour3DPlotRenderer::Sources sources; - sources.geometryProvider = v; sources.verticalBinLayer = this; - sources.source = m_fftModel; - sources.peaks = m_peakCache; - sources.fft = m_fftModel; + sources.fft = getFFTModel(); + sources.source = sources.fft; + sources.peaks = getPeakCache(); ::ColourScale::Parameters cparams; //!!! todo @@ -1588,8 +1592,8 @@ SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; } -// paintAlternative(v, paint, rect); -// return; + paintAlternative(v, paint, rect); + return; //!!! @@ -1671,7 +1675,7 @@ cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl; #endif - cache.scrollTo(startFrame); + cache.scrollTo(v, startFrame); #ifdef DEBUG_SPECTROGRAM_REPAINT cerr << "SpectrogramLayer: after scrolling, cache valid from " @@ -2595,7 +2599,7 @@ } int -SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const +SpectrogramLayer::getCompletion(LayerGeometryProvider *) const { if (!m_fftModel) return 100; int completion = m_fftModel->getCompletion(); @@ -2606,9 +2610,8 @@ } QString -SpectrogramLayer::getError(LayerGeometryProvider *v) const +SpectrogramLayer::getError(LayerGeometryProvider *) const { - const View *view = v->getView(); if (!m_fftModel) return ""; return m_fftModel->getError(); }