diff --git a/examples/utilities/tools/render/PlotPerf.qml b/examples/utilities/tools/render/PlotPerf.qml index 00b7848936..c907b847a8 100644 --- a/examples/utilities/tools/render/PlotPerf.qml +++ b/examples/utilities/tools/render/PlotPerf.qml @@ -13,6 +13,7 @@ import QtQuick.Controls 1.4 Item { id: root + width: parent.width height: 100 property string title property var config @@ -34,12 +35,15 @@ Item { if (inputs.length > input_VALUE_OFFSET) { for (var i = input_VALUE_OFFSET; i < inputs.length; i++) { var varProps = inputs[i].split("-") - _values.push( { + _values.push( { value: varProps[1], valueMax: 1, + numSamplesConstantMax: 0, valueHistory: new Array(), label: varProps[0], - color: varProps[2] + color: varProps[2], + scale: (varProps.length > 3 ? varProps[3] : 1), + unit: (varProps.length > 4 ? varProps[4] : valueUnit) }) } } @@ -59,25 +63,40 @@ Item { var UPDATE_CANVAS_RATE = 20; tick++; - valueMax = 0 + + var currentValueMax = 0 for (var i = 0; i < _values.length; i++) { - var currentVal = stats.config[_values[i].value]; - if (_values[i].valueMax < currentVal) { - _values[i].valueMax = currentVal; - } + + var currentVal = stats.config[_values[i].value] * _values[i].scale; _values[i].valueHistory.push(currentVal) + _values[i].numSamplesConstantMax++; + if (_values[i].valueHistory.length > VALUE_HISTORY_SIZE) { var lostValue = _values[i].valueHistory.shift(); if (lostValue >= _values[i].valueMax) { _values[i].valueMax *= 0.99 + _values[i].numSamplesConstantMax = 0 } } - if (valueMax < _values[i].valueMax) { - valueMax = _values[i].valueMax + if (_values[i].valueMax < currentVal) { + _values[i].valueMax = currentVal; + _values[i].numSamplesConstantMax = 0 + } + + if (_values[i].numSamplesConstantMax > VALUE_HISTORY_SIZE) { + _values[i].numSamplesConstantMax = 0 + _values[i].valueMax *= 0.95 // lower slowly the current max if no new above max since a while + } + + if (currentValueMax < _values[i].valueMax) { + currentValueMax = _values[i].valueMax } } + if ((valueMax < currentValueMax) || (tick % VALUE_HISTORY_SIZE == 0)) { + valueMax = currentValueMax; + } if (tick % UPDATE_CANVAS_RATE == 0) { mycanvas.requestPaint() @@ -91,20 +110,23 @@ Item { onPaint: { var lineHeight = 12; - function displayValue(val) { - return (val / root.valueScale).toFixed(root.valueNumDigits) + " " + root.valueUnit + function displayValue(val, unit) { + return (val / root.valueScale).toFixed(root.valueNumDigits) + " " + unit } - function pixelFromVal(val) { + function pixelFromVal(val, valScale) { return lineHeight + (height - lineHeight) * (1 - (0.9) * val / valueMax); } + function valueFromPixel(pixY) { + return ((pixY - lineHeight) / (height - lineHeight) - 1) * valueMax / (-0.9); + } function plotValueHistory(ctx, valHistory, color) { var widthStep= width / (valHistory.length - 1); ctx.beginPath(); ctx.strokeStyle= color; // Green path ctx.lineWidth="2"; - ctx.moveTo(0, pixelFromVal(valHistory[i])); + ctx.moveTo(0, pixelFromVal(valHistory[0])); for (var i = 1; i < valHistory.length; i++) { ctx.lineTo(i * widthStep, pixelFromVal(valHistory[i])); @@ -116,27 +138,40 @@ Item { ctx.fillStyle = val.color; var bestValue = val.valueHistory[val.valueHistory.length -1]; ctx.textAlign = "right"; - ctx.fillText(displayValue(bestValue), width, height - num * lineHeight); + ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1.5); ctx.textAlign = "left"; - ctx.fillText(val.label, 0, height - num * lineHeight); + ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1.5); } function displayTitle(ctx, text, maxVal) { ctx.fillStyle = "grey"; ctx.textAlign = "right"; - ctx.fillText(displayValue(maxVal), width, lineHeight); + ctx.fillText(displayValue(valueFromPixel(lineHeight), root.valueUnit), width, lineHeight); ctx.fillStyle = "white"; ctx.textAlign = "left"; ctx.fillText(text, 0, lineHeight); } + function displayBackground(ctx) { + ctx.fillStyle = Qt.rgba(0, 0, 0, 0.6); + ctx.fillRect(0, 0, width, height); + + ctx.strokeStyle= "grey"; + ctx.lineWidth="2"; + + ctx.beginPath(); + ctx.moveTo(0, lineHeight + 1); + ctx.lineTo(width, lineHeight + 1); + ctx.moveTo(0, height); + ctx.lineTo(width, height); + ctx.stroke(); + } var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); - ctx.fillStyle = Qt.rgba(0, 0, 0, 0.4); - ctx.fillRect(0, 0, width, height); - ctx.font="12px Verdana"; + + displayBackground(ctx); for (var i = 0; i < _values.length; i++) { plotValueHistory(ctx, _values[i].valueHistory, _values[i].color) diff --git a/examples/utilities/tools/render/stats.qml b/examples/utilities/tools/render/stats.qml index 47c0f98568..9c0ac3e14d 100644 --- a/examples/utilities/tools/render/stats.qml +++ b/examples/utilities/tools/render/stats.qml @@ -12,40 +12,58 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 -Column { +Item { id: statsUI - width: 300 - spacing: 8 - Column { - spacing: 8 + anchors.fill:parent - property var config: Render.getConfig("Stats") + Column { id: stats + spacing: 8 + anchors.fill:parent + + property var config: Render.getConfig("Stats") + + function evalEvenHeight() { + // Why do we have to do that manually ? cannot seem to find a qml / anchor / layout mode that does that ? + return (height - spacing * (children.length - 1)) / children.length + } PlotPerf { title: "Num Buffers" - width:statsUI.width config: stats.config + height: parent.evalEvenHeight() parameters: "1::0:CPU-numBuffers-#00B4EF:GPU-numGPUBuffers-#1AC567" } PlotPerf { - title: "Memory Usage" - width:statsUI.width + title: "gpu::Buffer Memory" config: stats.config + height: parent.evalEvenHeight() parameters: "1048576:Mb:1:CPU-bufferSysmemUsage-#00B4EF:GPU-bufferVidmemUsage-#1AC567" } PlotPerf { title: "Num Textures" - width:statsUI.width config: stats.config - parameters: "1::0:CPU-numTextures-#00B4EF:GPU-numGPUTextures-#1AC567:Frame-numFrameTextures-#E2334D" + height: parent.evalEvenHeight() + parameters: "1::0:CPU-numTextures-#00B4EF:GPU-numGPUTextures-#1AC567:Frame-frameTextureCount-#E2334D" } PlotPerf { - title: "Memory Usage" - width:statsUI.width + title: "gpu::Texture Memory" config: stats.config + height: parent.evalEvenHeight() parameters: "1048576:Mb:1:CPU-textureSysmemUsage-#00B4EF:GPU-textureVidmemUsage-#1AC567" } + PlotPerf { + title: "Drawcalls" + config: stats.config + height: parent.evalEvenHeight() + parameters: "1::0:frame-frameDrawcallCount-#E2334D:rate-frameDrawcallRate-#1AC567-0.001-K/s" + } + PlotPerf { + title: "Triangles" + config: stats.config + height: parent.evalEvenHeight() + parameters: "1000:K:0:frame-frameTriangleCount-#E2334D:rate-frameTriangleRate-#1AC567-0.001-MT/s" + } } } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index c4ad35020a..c2cd1f239e 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -37,7 +37,7 @@ public: int _DSNumDrawcalls = 0; int _DSNumTriangles = 0; - + ContextStats() {} ContextStats(const ContextStats& stats) = default; }; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 2c25255a80..e847ad1a42 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -324,7 +324,10 @@ void GLBackend::do_draw(Batch& batch, size_t paramOffset) { uint32 numVertices = batch._params[paramOffset + 1]._uint; uint32 startVertex = batch._params[paramOffset + 0]._uint; glDrawArrays(mode, startVertex, numVertices); - (void) CHECK_GL_ERROR(); + _stats._DSNumTriangles += numVertices / 3; + _stats._DSNumDrawcalls++; + + (void)CHECK_GL_ERROR(); } void GLBackend::do_drawIndexed(Batch& batch, size_t paramOffset) { @@ -339,6 +342,9 @@ void GLBackend::do_drawIndexed(Batch& batch, size_t paramOffset) { GLvoid* indexBufferByteOffset = reinterpret_cast(startIndex * typeByteSize + _input._indexBufferOffset); glDrawElements(mode, numIndices, glType, indexBufferByteOffset); + _stats._DSNumTriangles += numIndices / 3; + _stats._DSNumDrawcalls++; + (void) CHECK_GL_ERROR(); } @@ -350,6 +356,9 @@ void GLBackend::do_drawInstanced(Batch& batch, size_t paramOffset) { uint32 startVertex = batch._params[paramOffset + 1]._uint; glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); + _stats._DSNumTriangles += (numInstances * numVertices) / 3; + _stats._DSNumDrawcalls += numInstances; + (void) CHECK_GL_ERROR(); } @@ -372,6 +381,9 @@ void GLBackend::do_drawIndexedInstanced(Batch& batch, size_t paramOffset) { glDrawElementsInstanced(mode, numIndices, glType, indexBufferByteOffset, numInstances); Q_UNUSED(startInstance); #endif + _stats._DSNumTriangles += (numInstances * numIndices) / 3; + _stats._DSNumDrawcalls += numInstances; + (void)CHECK_GL_ERROR(); } @@ -382,6 +394,7 @@ void GLBackend::do_multiDrawIndirect(Batch& batch, size_t paramOffset) { GLenum mode = _primitiveToGLmode[(Primitive)batch._params[paramOffset + 1]._uint]; glMultiDrawArraysIndirect(mode, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + _stats._DSNumDrawcalls += commandCount; #else // FIXME implement the slow path #endif @@ -396,6 +409,8 @@ void GLBackend::do_multiDrawIndexedIndirect(Batch& batch, size_t paramOffset) { GLenum indexType = _elementTypeToGLType[_input._indexBufferType]; glMultiDrawElementsIndirect(mode, indexType, reinterpret_cast(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride); + _stats._DSNumDrawcalls += commandCount; + #else // FIXME implement the slow path #endif diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index b4ee713e74..b0329faa3e 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -17,6 +17,7 @@ #include +#include "EngineStats.h" using namespace render; @@ -61,28 +62,3 @@ void Engine::run() { } -#include -void EngineStats::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - const size_t KILO_BYTES = 1024; - // Update the stats - auto config = std::static_pointer_cast(renderContext->jobConfig); - - config->numBuffers = gpu::Buffer::getCurrentNumBuffers(); - config->numGPUBuffers = gpu::Buffer::getCurrentNumGPUBuffers(); - config->bufferSysmemUsage = gpu::Buffer::getCurrentSystemMemoryUsage(); - config->bufferVidmemUsage = gpu::Buffer::getCurrentVideoMemoryUsage(); - - config->numTextures = gpu::Texture::getCurrentNumTextures(); - config->numGPUTextures = gpu::Texture::getCurrentNumGPUTextures(); - config->textureSysmemUsage = gpu::Texture::getCurrentSystemMemoryUsage(); - config->textureVidmemUsage = gpu::Texture::getCurrentVideoMemoryUsage(); - - gpu::ContextStats gpuStats(_gpuStats); - renderContext->args->_context->getStats(_gpuStats); - - config->numFrameTextures = _gpuStats._RSNumTextureBounded - gpuStats._RSNumTextureBounded; - - config->numFrameTriangles = _gpuStats._DSNumTriangles - gpuStats._DSNumTriangles; - - config->emitDirty(); -} diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 4fd29b5d66..d2bb42e5ff 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -13,7 +13,6 @@ #define hifi_render_Engine_h #include -#include #include "Context.h" #include "Task.h" @@ -49,57 +48,6 @@ namespace render { }; using EnginePointer = std::shared_ptr; - - // A simple job collecting global stats on the Engine / Scene / GPU - class EngineStatsConfig : public Job::Config{ - Q_OBJECT - - Q_PROPERTY(int numBuffers MEMBER numBuffers NOTIFY dirty) - Q_PROPERTY(int numGPUBuffers MEMBER numGPUBuffers NOTIFY dirty) - Q_PROPERTY(qint64 bufferSysmemUsage MEMBER bufferSysmemUsage NOTIFY dirty) - Q_PROPERTY(qint64 bufferVidmemUsage MEMBER bufferVidmemUsage NOTIFY dirty) - - Q_PROPERTY(int numTextures MEMBER numTextures NOTIFY dirty) - Q_PROPERTY(int numGPUTextures MEMBER numGPUTextures NOTIFY dirty) - Q_PROPERTY(qint64 textureSysmemUsage MEMBER textureSysmemUsage NOTIFY dirty) - Q_PROPERTY(qint64 textureVidmemUsage MEMBER textureVidmemUsage NOTIFY dirty) - Q_PROPERTY(int numFrameTextures MEMBER numFrameTextures NOTIFY dirty) - public: - EngineStatsConfig() : Job::Config(true) {} - - int numBuffers{ 0 }; - int numGPUBuffers{ 0 }; - qint64 bufferSysmemUsage{ 0 }; - qint64 bufferVidmemUsage{ 0 }; - - int numTextures{ 0 }; - int numGPUTextures{ 0 }; - qint64 textureSysmemUsage{ 0 }; - qint64 textureVidmemUsage{ 0 }; - - int numFrameTriangles{ 0 }; - int numFrameTextures{ 0 }; - - void emitDirty() { emit dirty(); } - - signals: - void dirty(); - }; - - class EngineStats { - public: - using Config = EngineStatsConfig; - using JobModel = Job::Model; - - EngineStats() {} - - gpu::ContextStats _gpuStats; - - void configure(const Config& configuration) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - }; - - } #endif // hifi_render_Engine_h diff --git a/libraries/render/src/render/EngineStats.cpp b/libraries/render/src/render/EngineStats.cpp new file mode 100644 index 0000000000..63a25229ea --- /dev/null +++ b/libraries/render/src/render/EngineStats.cpp @@ -0,0 +1,50 @@ +// +// EngineStats.cpp +// render/src/render +// +// Created by Sam Gateau on 3/27/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "EngineStats.h" + +#include + +using namespace render; + +void EngineStats::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + // Tick time + + quint64 msecsElapsed = _frameTimer.restart(); + double frequency = 1000.0 / msecsElapsed; + + + // Update the stats + auto config = std::static_pointer_cast(renderContext->jobConfig); + + config->numBuffers = gpu::Buffer::getCurrentNumBuffers(); + config->numGPUBuffers = gpu::Buffer::getCurrentNumGPUBuffers(); + config->bufferSysmemUsage = gpu::Buffer::getCurrentSystemMemoryUsage(); + config->bufferVidmemUsage = gpu::Buffer::getCurrentVideoMemoryUsage(); + + config->numTextures = gpu::Texture::getCurrentNumTextures(); + config->numGPUTextures = gpu::Texture::getCurrentNumGPUTextures(); + config->textureSysmemUsage = gpu::Texture::getCurrentSystemMemoryUsage(); + config->textureVidmemUsage = gpu::Texture::getCurrentVideoMemoryUsage(); + + gpu::ContextStats gpuStats(_gpuStats); + renderContext->args->_context->getStats(_gpuStats); + + config->frameDrawcallCount = _gpuStats._DSNumDrawcalls - gpuStats._DSNumDrawcalls; + config->frameDrawcallRate = config->frameDrawcallCount * frequency; + + config->frameTriangleCount = _gpuStats._DSNumTriangles - gpuStats._DSNumTriangles; + config->frameTriangleRate = config->frameTriangleCount * frequency; + + config->frameTextureCount = _gpuStats._RSNumTextureBounded - gpuStats._RSNumTextureBounded; + config->frameTextureRate = config->frameTextureCount * frequency; + + config->emitDirty(); +} diff --git a/libraries/render/src/render/EngineStats.h b/libraries/render/src/render/EngineStats.h new file mode 100644 index 0000000000..d9f7e7ac9a --- /dev/null +++ b/libraries/render/src/render/EngineStats.h @@ -0,0 +1,91 @@ +// +// EngineStats.h +// render/src/render +// +// Created by Sam Gateau on 3/27/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_EngineStats_h +#define hifi_render_EngineStats_h + +#include + +#include + +#include "Engine.h" + +namespace render { + + // A simple job collecting global stats on the Engine / Scene / GPU + class EngineStatsConfig : public Job::Config{ + Q_OBJECT + + Q_PROPERTY(int numBuffers MEMBER numBuffers NOTIFY dirty) + Q_PROPERTY(int numGPUBuffers MEMBER numGPUBuffers NOTIFY dirty) + Q_PROPERTY(qint64 bufferSysmemUsage MEMBER bufferSysmemUsage NOTIFY dirty) + Q_PROPERTY(qint64 bufferVidmemUsage MEMBER bufferVidmemUsage NOTIFY dirty) + + Q_PROPERTY(int numTextures MEMBER numTextures NOTIFY dirty) + Q_PROPERTY(int numGPUTextures MEMBER numGPUTextures NOTIFY dirty) + Q_PROPERTY(qint64 textureSysmemUsage MEMBER textureSysmemUsage NOTIFY dirty) + Q_PROPERTY(qint64 textureVidmemUsage MEMBER textureVidmemUsage NOTIFY dirty) + + Q_PROPERTY(int frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty) + Q_PROPERTY(int frameDrawcallRate MEMBER frameDrawcallRate NOTIFY dirty) + + Q_PROPERTY(int frameTriangleCount MEMBER frameTriangleCount NOTIFY dirty) + Q_PROPERTY(int frameTriangleRate MEMBER frameTriangleRate NOTIFY dirty) + + Q_PROPERTY(int frameTextureCount MEMBER frameTextureCount NOTIFY dirty) + Q_PROPERTY(int frameTextureRate MEMBER frameTextureRate NOTIFY dirty) + + + public: + EngineStatsConfig() : Job::Config(true) {} + + int numBuffers{ 0 }; + int numGPUBuffers{ 0 }; + qint64 bufferSysmemUsage{ 0 }; + qint64 bufferVidmemUsage{ 0 }; + + int numTextures{ 0 }; + int numGPUTextures{ 0 }; + qint64 textureSysmemUsage{ 0 }; + qint64 textureVidmemUsage{ 0 }; + + int frameDrawcallCount{ 0 }; + int frameDrawcallRate{ 0 }; + + int frameTriangleCount{ 0 }; + int frameTriangleRate{ 0 }; + + int frameTextureCount{ 0 }; + int frameTextureRate{ 0 }; + + void emitDirty() { emit dirty(); } + + signals: + void dirty(); + }; + + class EngineStats { + public: + using Config = EngineStatsConfig; + using JobModel = Job::Model; + + EngineStats() { _frameTimer.start(); } + + gpu::ContextStats _gpuStats; + + void configure(const Config& configuration) {} + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); + + QElapsedTimer _frameTimer; + }; +} + +#endif \ No newline at end of file