Merge pull request #7481 from samcake/lemon

Monitoring Tool for rendering engine
This commit is contained in:
Brad Hefta-Gaub 2016-03-28 12:20:07 -07:00
commit b26533bc14
18 changed files with 788 additions and 82 deletions

View file

@ -0,0 +1,186 @@
//
// PlotPerf.qml
// examples/utilities/tools/render
//
// Created by Sam Gateau on 3//2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
Item {
id: root
width: parent.width
height: 100
property string title
property var config
property string parameters
// THis is my hack to get the name of the first property and assign it to a trigger var in order to get
// a signal called whenever the value changed
property var trigger: config[parameters.split(":")[3].split("-")[0]]
property var inputs: parameters.split(":")
property var valueScale: +inputs[0]
property var valueUnit: inputs[1]
property var valueNumDigits: inputs[2]
property var input_VALUE_OFFSET: 3
property var valueMax : 1
property var _values : new Array()
property var tick : 0
function createValues() {
if (inputs.length > input_VALUE_OFFSET) {
for (var i = input_VALUE_OFFSET; i < inputs.length; i++) {
var varProps = inputs[i].split("-")
_values.push( {
value: varProps[0],
valueMax: 1,
numSamplesConstantMax: 0,
valueHistory: new Array(),
label: varProps[1],
color: varProps[2],
scale: (varProps.length > 3 ? varProps[3] : 1),
unit: (varProps.length > 4 ? varProps[4] : valueUnit)
})
}
}
print("in creator" + JSON.stringify(_values));
}
Component.onCompleted: {
createValues();
print(JSON.stringify(_values));
}
function pullFreshValues() {
//print("pullFreshValues");
var VALUE_HISTORY_SIZE = 100;
var UPDATE_CANVAS_RATE = 20;
tick++;
var currentValueMax = 0
for (var i = 0; i < _values.length; i++) {
var currentVal = 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 (_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()
}
}
onTriggerChanged: pullFreshValues()
Canvas {
id: mycanvas
anchors.fill:parent
onPaint: {
var lineHeight = 12;
function displayValue(val, unit) {
return (val / root.valueScale).toFixed(root.valueNumDigits) + " " + unit
}
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[0]));
for (var i = 1; i < valHistory.length; i++) {
ctx.lineTo(i * widthStep, pixelFromVal(valHistory[i]));
}
ctx.stroke();
}
function displayValueLegend(ctx, val, num) {
ctx.fillStyle = val.color;
var bestValue = val.valueHistory[val.valueHistory.length -1];
ctx.textAlign = "right";
ctx.fillText(displayValue(bestValue, val.unit), width, (num + 2) * lineHeight * 1.5);
ctx.textAlign = "left";
ctx.fillText(val.label, 0, (num + 2) * lineHeight * 1.5);
}
function displayTitle(ctx, text, maxVal) {
ctx.fillStyle = "grey";
ctx.textAlign = "right";
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.font="12px Verdana";
displayBackground(ctx);
for (var i = 0; i < _values.length; i++) {
plotValueHistory(ctx, _values[i].valueHistory, _values[i].color)
displayValueLegend(ctx, _values[i], i)
}
displayTitle(ctx, title, valueMax)
}
}
}

View file

@ -0,0 +1,21 @@
//
// renderStats.js
// examples/utilities/tools/render
//
// Sam Gateau, created on 3/22/2016.
// 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
//
// Set up the qml ui
var qml = Script.resolvePath('stats.qml');
var window = new OverlayWindow({
title: 'Render Stats',
source: qml,
width: 300,
height: 200
});
window.setPosition(500, 50);
window.closed.connect(function() { Script.stop(); });

View file

@ -0,0 +1,69 @@
//
// stats.qml
// examples/utilities/tools/render
//
// Created by Zach Pomerantz on 2/8/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
Item {
id: statsUI
anchors.fill:parent
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"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:bufferCPUCount-CPU-#00B4EF:bufferGPUCount-GPU-#1AC567"
}
PlotPerf {
title: "gpu::Buffer Memory"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1048576:Mb:1:bufferCPUMemoryUsage-CPU-#00B4EF:bufferGPUMemoryUsage-GPU-#1AC567"
}
PlotPerf {
title: "Num Textures"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:textureCPUCount-CPU-#00B4EF:textureGPUCount-GPU-#1AC567:frameTextureCount-Frame-#E2334D"
}
PlotPerf {
title: "gpu::Texture Memory"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1048576:Mb:1:textureCPUMemoryUsage-CPU-#00B4EF:textureGPUMemoryUsage-GPU-#1AC567"
}
PlotPerf {
title: "Drawcalls"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1::0:frameDrawcallCount-frame-#E2334D:frameDrawcallRate-rate-#1AC567-0.001-K/s"
}
PlotPerf {
title: "Triangles"
config: stats.config
height: parent.evalEvenHeight()
parameters: "1000:K:0:frameTriangleCount-frame-#E2334D:frameTriangleRate-rate-#1AC567-0.001-MT/s"
}
}
}

View file

@ -74,6 +74,11 @@ void Context::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, cons
_backend->downloadFramebuffer(srcFramebuffer, region, destImage);
}
void Context::getStats(ContextStats& stats) const {
_backend->getStats(stats);
}
const Backend::TransformCamera& Backend::TransformCamera::recomputeDerived(const Transform& xformView) const {
_projectionInverse = glm::inverse(_projection);
@ -102,3 +107,68 @@ Backend::TransformCamera Backend::TransformCamera::getEyeCamera(int eye, const S
return result;
}
// Counters for Buffer and Texture usage in GPU/Context
std::atomic<uint32_t> Context::_bufferGPUCount{ 0 };
std::atomic<Buffer::Size> Context::_bufferGPUMemoryUsage{ 0 };
std::atomic<uint32_t> Context::_textureGPUCount{ 0 };
std::atomic<Texture::Size> Context::_textureGPUMemoryUsage{ 0 };
void Context::incrementBufferGPUCount() {
_bufferGPUCount++;
}
void Context::decrementBufferGPUCount() {
_bufferGPUCount--;
}
void Context::updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
}
if (newObjectSize > prevObjectSize) {
_bufferGPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize);
} else {
_bufferGPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize);
}
}
void Context::incrementTextureGPUCount() {
_textureGPUCount++;
}
void Context::decrementTextureGPUCount() {
_textureGPUCount--;
}
void Context::updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
}
if (newObjectSize > prevObjectSize) {
_textureGPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize);
} else {
_textureGPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize);
}
}
uint32_t Context::getBufferGPUCount() {
return _bufferGPUCount.load();
}
Context::Size Context::getBufferGPUMemoryUsage() {
return _bufferGPUMemoryUsage.load();
}
uint32_t Context::getTextureGPUCount() {
return _textureGPUCount.load();
}
Context::Size Context::getTextureGPUMemoryUsage() {
return _textureGPUMemoryUsage.load();
}
void Backend::incrementBufferGPUCount() { Context::incrementBufferGPUCount(); }
void Backend::decrementBufferGPUCount() { Context::decrementBufferGPUCount(); }
void Backend::updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateBufferGPUMemoryUsage(prevObjectSize, newObjectSize); }
void Backend::incrementTextureGPUCount() { Context::incrementTextureGPUCount(); }
void Backend::decrementTextureGPUCount() { Context::decrementTextureGPUCount(); }
void Backend::updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize) { Context::updateTextureGPUMemoryUsage(prevObjectSize, newObjectSize); }

View file

@ -27,6 +27,21 @@ class QImage;
namespace gpu {
struct ContextStats {
public:
int _ISNumFormatChanges = 0;
int _ISNumInputBufferChanges = 0;
int _ISNumIndexBufferChanges = 0;
int _RSNumTextureBounded = 0;
int _DSNumDrawcalls = 0;
int _DSNumTriangles = 0;
ContextStats() {}
ContextStats(const ContextStats& stats) = default;
};
struct StereoState {
bool _enable{ false };
bool _skybox{ false };
@ -100,13 +115,27 @@ public:
return reinterpret_cast<T*>(object.gpuObject.getGPUObject());
}
void getStats(ContextStats& stats) const { stats = _stats; }
// These should only be accessed by Backend implementation to repport the buffer and texture allocations,
// they are NOT public calls
static void incrementBufferGPUCount();
static void decrementBufferGPUCount();
static void updateBufferGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize);
static void incrementTextureGPUCount();
static void decrementTextureGPUCount();
static void updateTextureGPUMemoryUsage(Resource::Size prevObjectSize, Resource::Size newObjectSize);
protected:
StereoState _stereo;
ContextStats _stats;
};
class Context {
public:
using Size = Resource::Size;
typedef Backend* (*CreateBackend)();
typedef bool (*MakeProgram)(Shader& shader, const Shader::BindingSet& bindings);
@ -125,6 +154,7 @@ public:
~Context();
void render(Batch& batch);
void enableStereo(bool enable = true);
bool isStereo();
void setStereoProjections(const mat4 eyeProjections[2]);
@ -137,6 +167,16 @@ public:
// It s here for convenience to easily capture a snapshot
void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage);
// Repporting stats of the context
void getStats(ContextStats& stats) const;
static uint32_t getBufferGPUCount();
static Size getBufferGPUMemoryUsage();
static uint32_t getTextureGPUCount();
static Size getTextureGPUMemoryUsage();
protected:
Context(const Context& context);
@ -153,6 +193,23 @@ protected:
static std::once_flag _initialized;
friend class Shader;
// These should only be accessed by the Backend, they are NOT public calls
static void incrementBufferGPUCount();
static void decrementBufferGPUCount();
static void updateBufferGPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
static void incrementTextureGPUCount();
static void decrementTextureGPUCount();
static void updateTextureGPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
// Buffer and Texture Counters
static std::atomic<uint32_t> _bufferGPUCount;
static std::atomic<Size> _bufferGPUMemoryUsage;
static std::atomic<uint32_t> _textureGPUCount;
static std::atomic<Size> _textureGPUMemoryUsage;
friend class Backend;
};
typedef std::shared_ptr<Context> ContextPointer;

View file

@ -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<GLvoid*>(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<GLvoid*>(_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<GLvoid*>(_input._indirectBufferOffset), commandCount, (GLsizei)_input._indirectBufferStride);
_stats._DSNumDrawcalls += commandCount;
#else
// FIXME implement the slow path
#endif

View file

@ -67,6 +67,8 @@ public:
GLBuffer();
~GLBuffer();
void setSize(GLuint size);
};
static GLBuffer* syncGPUObject(const Buffer& buffer);
static GLuint getBufferID(const Buffer& buffer);
@ -77,10 +79,15 @@ public:
Stamp _contentStamp;
GLuint _texture;
GLenum _target;
GLuint _size;
GLTexture();
~GLTexture();
void setSize(GLuint size);
GLuint size() const { return _size; }
private:
GLuint _size;
};
static GLTexture* syncGPUObject(const Texture& texture);
static GLuint getTextureID(const TexturePointer& texture, bool sync = true);
@ -230,26 +237,11 @@ public:
void do_setStateBlend(State::BlendFunction blendFunction);
void do_setStateColorWriteMask(uint32 mask);
// Repporting stats of the context
class Stats {
public:
int _ISNumFormatChanges = 0;
int _ISNumInputBufferChanges = 0;
int _ISNumIndexBufferChanges = 0;
Stats() {}
Stats(const Stats& stats) = default;
};
void getStats(Stats& stats) const { stats = _stats; }
protected:
void renderPassTransfer(Batch& batch);
void renderPassDraw(Batch& batch);
Stats _stats;
// Draw Stage
void do_draw(Batch& batch, size_t paramOffset);
void do_drawIndexed(Batch& batch, size_t paramOffset);

View file

@ -16,12 +16,21 @@ GLBackend::GLBuffer::GLBuffer() :
_stamp(0),
_buffer(0),
_size(0)
{}
{
Backend::incrementBufferGPUCount();
}
GLBackend::GLBuffer::~GLBuffer() {
if (_buffer != 0) {
glDeleteBuffers(1, &_buffer);
}
Backend::updateBufferGPUMemoryUsage(_size, 0);
Backend::decrementBufferGPUCount();
}
void GLBackend::GLBuffer::setSize(GLuint size) {
Backend::updateBufferGPUMemoryUsage(_size, size);
_size = size;
}
GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) {
@ -46,7 +55,7 @@ GLBackend::GLBuffer* GLBackend::syncGPUObject(const Buffer& buffer) {
glBufferData(GL_ARRAY_BUFFER, buffer.getSysmem().getSize(), buffer.getSysmem().readData(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
object->_stamp = buffer.getSysmem().getStamp();
object->_size = (GLuint)buffer.getSysmem().getSize();
object->setSize((GLuint)buffer.getSysmem().getSize());
//}
(void) CHECK_GL_ERROR();

View file

@ -251,6 +251,9 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
return;
}
// One more True texture bound
_stats._RSNumTextureBounded++;
// Always make sure the GLObject is in sync
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
if (object) {

View file

@ -19,12 +19,21 @@ GLBackend::GLTexture::GLTexture() :
_texture(0),
_target(GL_TEXTURE_2D),
_size(0)
{}
{
Backend::incrementTextureGPUCount();
}
GLBackend::GLTexture::~GLTexture() {
if (_texture != 0) {
glDeleteTextures(1, &_texture);
}
Backend::updateTextureGPUMemoryUsage(_size, 0);
Backend::decrementTextureGPUCount();
}
void GLBackend::GLTexture::setSize(GLuint size) {
Backend::updateTextureGPUMemoryUsage(_size, size);
_size = size;
}
class GLTexelFormat {
@ -427,8 +436,8 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
if (needUpdate) {
if (texture.isStoredMipFaceAvailable(0)) {
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
const GLvoid* bytes = mip->_sysmem.read<Byte>();
Element srcFormat = mip->_format;
const GLvoid* bytes = mip->readData();
Element srcFormat = mip->getFormat();
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
@ -458,8 +467,8 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
if (texture.isStoredMipFaceAvailable(0)) {
Texture::PixelsPointer mip = texture.accessStoredMipFace(0);
bytes = mip->_sysmem.read<Byte>();
srcFormat = mip->_format;
bytes = mip->readData();
srcFormat = mip->getFormat();
object->_contentStamp = texture.getDataStamp();
}
@ -483,7 +492,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
object->_storageStamp = texture.getStamp();
object->_contentStamp = texture.getDataStamp();
object->_size = (GLuint)texture.getSize();
object->setSize((GLuint)texture.getSize());
}
glBindTexture(GL_TEXTURE_2D, boundTex);
@ -507,11 +516,11 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
for (int f = 0; f < NUM_FACES; f++) {
if (texture.isStoredMipFaceAvailable(0, f)) {
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
Element srcFormat = mipFace->_format;
Element srcFormat = mipFace->getFormat();
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
glTexSubImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->_sysmem.read<Byte>()));
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
// At this point the mip pixels have been loaded, we can notify
texture.notifyMipFaceGPULoaded(0, f);
@ -536,11 +545,11 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
for (int f = 0; f < NUM_FACES; f++) {
if (texture.isStoredMipFaceAvailable(0, f)) {
Texture::PixelsPointer mipFace = texture.accessStoredMipFace(0, f);
Element srcFormat = mipFace->_format;
Element srcFormat = mipFace->getFormat();
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
glTexImage2D(FACE_LAYOUT[f], 0, texelFormat.internalFormat, texture.getWidth(), texture.getWidth(), 0,
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->_sysmem.read<Byte>()));
texelFormat.format, texelFormat.type, (GLvoid*) (mipFace->readData()));
// At this point the mip pixels have been loaded, we can notify
texture.notifyMipFaceGPULoaded(0, f);
@ -561,7 +570,7 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
object->_storageStamp = texture.getStamp();
object->_contentStamp = texture.getDataStamp();
object->_size = (GLuint)texture.getSize();
object->setSize((GLuint)texture.getSize());
}
glBindTexture(GL_TEXTURE_CUBE_MAP, boundTex);

View file

@ -16,6 +16,8 @@
#include <NumericalConstants.h>
#include <QDebug>
#include "Context.h"
using namespace gpu;
class AllocationDebugger {
@ -232,19 +234,55 @@ Resource::Size Resource::Sysmem::append(Size size, const Byte* bytes) {
return 0;
}
std::atomic<uint32_t> Buffer::_bufferCPUCount{ 0 };
std::atomic<Buffer::Size> Buffer::_bufferCPUMemoryUsage{ 0 };
void Buffer::updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
}
if (prevObjectSize > newObjectSize) {
_bufferCPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize);
} else {
_bufferCPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize);
}
}
uint32_t Buffer::getBufferCPUCount() {
return _bufferCPUCount.load();
}
Buffer::Size Buffer::getBufferCPUMemoryUsage() {
return _bufferCPUMemoryUsage.load();
}
uint32_t Buffer::getBufferGPUCount() {
return Context::getBufferGPUCount();
}
Buffer::Size Buffer::getBufferGPUMemoryUsage() {
return Context::getBufferGPUMemoryUsage();
}
Buffer::Buffer() :
Resource(),
_sysmem(new Sysmem()) {
_bufferCPUCount++;
}
Buffer::Buffer(Size size, const Byte* bytes) :
Resource(),
_sysmem(new Sysmem(size, bytes)) {
_bufferCPUCount++;
Buffer::updateBufferCPUMemoryUsage(0, _sysmem->getSize());
}
Buffer::Buffer(const Buffer& buf) :
Resource(),
_sysmem(new Sysmem(buf.getSysmem())) {
_bufferCPUCount++;
Buffer::updateBufferCPUMemoryUsage(0, _sysmem->getSize());
}
Buffer& Buffer::operator=(const Buffer& buf) {
@ -253,18 +291,27 @@ Buffer& Buffer::operator=(const Buffer& buf) {
}
Buffer::~Buffer() {
_bufferCPUCount--;
if (_sysmem) {
Buffer::updateBufferCPUMemoryUsage(_sysmem->getSize(), 0);
delete _sysmem;
_sysmem = NULL;
}
}
Buffer::Size Buffer::resize(Size size) {
return editSysmem().resize(size);
auto prevSize = editSysmem().getSize();
auto newSize = editSysmem().resize(size);
Buffer::updateBufferCPUMemoryUsage(prevSize, newSize);
return newSize;
}
Buffer::Size Buffer::setData(Size size, const Byte* data) {
return editSysmem().setData(size, data);
auto prevSize = editSysmem().getSize();
auto newSize = editSysmem().setData(size, data);
Buffer::updateBufferCPUMemoryUsage(prevSize, newSize);
return newSize;
}
Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) {
@ -272,6 +319,9 @@ Buffer::Size Buffer::setSubData(Size offset, Size size, const Byte* data) {
}
Buffer::Size Buffer::append(Size size, const Byte* data) {
return editSysmem().append( size, data);
auto prevSize = editSysmem().getSize();
auto newSize = editSysmem().append( size, data);
Buffer::updateBufferCPUMemoryUsage(prevSize, newSize);
return newSize;
}

View file

@ -16,6 +16,7 @@
#include "Format.h"
#include <vector>
#include <atomic>
#include <memory>
#ifdef _DEBUG
@ -109,7 +110,15 @@ protected:
};
class Buffer : public Resource {
static std::atomic<uint32_t> _bufferCPUCount;
static std::atomic<Size> _bufferCPUMemoryUsage;
static void updateBufferCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
public:
static uint32_t getBufferCPUCount();
static Size getBufferCPUMemoryUsage();
static uint32_t getBufferGPUCount();
static Size getBufferGPUMemoryUsage();
Buffer();
Buffer(Size size, const Byte* bytes);

View file

@ -12,20 +12,77 @@
#include "Texture.h"
#include <glm/gtc/constants.hpp>
#include <QDebug>
#include "GPULogging.h"
#include "Context.h"
using namespace gpu;
std::atomic<uint32_t> Texture::_textureCPUCount{ 0 };
std::atomic<Texture::Size> Texture::_textureCPUMemoryUsage{ 0 };
void Texture::updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize) {
if (prevObjectSize == newObjectSize) {
return;
}
if (prevObjectSize > newObjectSize) {
_textureCPUMemoryUsage.fetch_sub(prevObjectSize - newObjectSize);
} else {
_textureCPUMemoryUsage.fetch_add(newObjectSize - prevObjectSize);
}
}
uint32_t Texture::getTextureCPUCount() {
return _textureCPUCount.load();
}
Texture::Size Texture::getTextureCPUMemoryUsage() {
return _textureCPUMemoryUsage.load();
}
uint32_t Texture::getTextureGPUCount() {
return Context::getTextureGPUCount();
}
Texture::Size Texture::getTextureGPUMemoryUsage() {
return Context::getTextureGPUMemoryUsage();
}
uint8 Texture::NUM_FACES_PER_TYPE[NUM_TYPES] = {1, 1, 1, 6};
Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) :
_sysmem(size, bytes),
_format(format),
_sysmem(size, bytes),
_isGPULoaded(false) {
Texture::updateTextureCPUMemoryUsage(0, _sysmem.getSize());
}
Texture::Pixels::~Pixels() {
Texture::updateTextureCPUMemoryUsage(_sysmem.getSize(), 0);
}
Texture::Size Texture::Pixels::resize(Size pSize) {
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.resize(pSize);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
return newSize;
}
Texture::Size Texture::Pixels::setData(const Element& format, Size size, const Byte* bytes ) {
_format = format;
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.setData(size, bytes);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
_isGPULoaded = false;
return newSize;
}
void Texture::Pixels::notifyGPULoaded() {
_isGPULoaded = true;
auto prevSize = _sysmem.getSize();
auto newSize = _sysmem.resize(0);
Texture::updateTextureCPUMemoryUsage(prevSize, newSize);
}
void Texture::Storage::assignTexture(Texture* texture) {
@ -59,15 +116,15 @@ const Texture::PixelsPointer Texture::Storage::getMipFace(uint16 level, uint8 fa
void Texture::Storage::notifyMipFaceGPULoaded(uint16 level, uint8 face) const {
PixelsPointer mipFace = getMipFace(level, face);
if (mipFace && (_type != TEX_CUBE)) {
mipFace->_isGPULoaded = true;
mipFace->_sysmem.resize(0);
// Free the mips
if (mipFace) {
mipFace->notifyGPULoaded();
}
}
bool Texture::Storage::isMipAvailable(uint16 level, uint8 face) const {
PixelsPointer mipFace = getMipFace(level, face);
return (mipFace && mipFace->_sysmem.getSize());
return (mipFace && mipFace->getSize());
}
bool Texture::Storage::allocateMip(uint16 level) {
@ -103,9 +160,7 @@ bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size s
auto faceBytes = bytes;
Size allocated = 0;
for (auto& face : mip) {
face->_format = format;
allocated += face->_sysmem.setData(sizePerFace, faceBytes);
face->_isGPULoaded = false;
allocated += face->setData(format, sizePerFace, faceBytes);
faceBytes += sizePerFace;
}
@ -122,9 +177,7 @@ bool Texture::Storage::assignMipFaceData(uint16 level, const Element& format, Si
Size allocated = 0;
if (face < mip.size()) {
auto mipFace = mip[face];
mipFace->_format = format;
allocated += mipFace->_sysmem.setData(size, bytes);
mipFace->_isGPULoaded = false;
allocated += mipFace->setData(format, size, bytes);
bumpStamp();
}
@ -171,10 +224,12 @@ Texture* Texture::createFromStorage(Storage* storage) {
Texture::Texture():
Resource()
{
_textureCPUCount++;
}
Texture::~Texture()
{
_textureCPUCount--;
}
Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) {
@ -292,7 +347,7 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co
}
}
// THen check that the mem buffer passed make sense with its format
// THen check that the mem texture passed make sense with its format
Size expectedSize = evalStoredMipSize(level, format);
if (size == expectedSize) {
_storage->assignMipData(level, format, size, bytes);
@ -323,7 +378,7 @@ bool Texture::assignStoredMipFace(uint16 level, const Element& format, Size size
}
}
// THen check that the mem buffer passed make sense with its format
// THen check that the mem texture passed make sense with its format
Size expectedSize = evalStoredMipFaceSize(level, format);
if (size == expectedSize) {
_storage->assignMipFaceData(level, format, size, bytes, face);
@ -364,7 +419,7 @@ uint16 Texture::autoGenerateMips(uint16 maxMip) {
uint16 Texture::getStoredMipWidth(uint16 level) const {
PixelsPointer mipFace = accessStoredMipFace(level);
if (mipFace && mipFace->_sysmem.getSize()) {
if (mipFace && mipFace->getSize()) {
return evalMipWidth(level);
}
return 0;
@ -372,7 +427,7 @@ uint16 Texture::getStoredMipWidth(uint16 level) const {
uint16 Texture::getStoredMipHeight(uint16 level) const {
PixelsPointer mip = accessStoredMipFace(level);
if (mip && mip->_sysmem.getSize()) {
if (mip && mip->getSize()) {
return evalMipHeight(level);
}
return 0;
@ -380,7 +435,7 @@ uint16 Texture::getStoredMipHeight(uint16 level) const {
uint16 Texture::getStoredMipDepth(uint16 level) const {
PixelsPointer mipFace = accessStoredMipFace(level);
if (mipFace && mipFace->_sysmem.getSize()) {
if (mipFace && mipFace->getSize()) {
return evalMipDepth(level);
}
return 0;
@ -388,7 +443,7 @@ uint16 Texture::getStoredMipDepth(uint16 level) const {
uint32 Texture::getStoredMipNumTexels(uint16 level) const {
PixelsPointer mipFace = accessStoredMipFace(level);
if (mipFace && mipFace->_sysmem.getSize()) {
if (mipFace && mipFace->getSize()) {
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level);
}
return 0;
@ -396,7 +451,7 @@ uint32 Texture::getStoredMipNumTexels(uint16 level) const {
uint32 Texture::getStoredMipSize(uint16 level) const {
PixelsPointer mipFace = accessStoredMipFace(level);
if (mipFace && mipFace->_sysmem.getSize()) {
if (mipFace && mipFace->getSize()) {
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getTexelFormat().getSize();
}
return 0;
@ -642,8 +697,8 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector<
// for each face of cube texture
for(int face=0; face < gpu::Texture::NUM_CUBE_FACES; face++) {
auto numComponents = cubeTexture.accessStoredMipFace(0,face)->_format.getScalarCount();
auto data = cubeTexture.accessStoredMipFace(0,face)->_sysmem.readData();
auto numComponents = cubeTexture.accessStoredMipFace(0,face)->getFormat().getScalarCount();
auto data = cubeTexture.accessStoredMipFace(0,face)->readData();
if (data == nullptr) {
continue;
}

View file

@ -138,7 +138,14 @@ protected:
};
class Texture : public Resource {
static std::atomic<uint32_t> _textureCPUCount;
static std::atomic<Size> _textureCPUMemoryUsage;
static void updateTextureCPUMemoryUsage(Size prevObjectSize, Size newObjectSize);
public:
static uint32_t getTextureCPUCount();
static Size getTextureCPUMemoryUsage();
static uint32_t getTextureGPUCount();
static Size getTextureGPUMemoryUsage();
class Usage {
public:
@ -194,9 +201,21 @@ public:
Pixels(const Element& format, Size size, const Byte* bytes);
~Pixels();
Sysmem _sysmem;
const Byte* readData() const { return _sysmem.readData(); }
Size getSize() const { return _sysmem.getSize(); }
Size resize(Size pSize);
Size setData(const Element& format, Size size, const Byte* bytes );
const Element& getFormat() const { return _format; }
void notifyGPULoaded();
protected:
Element _format;
Sysmem _sysmem;
bool _isGPULoaded;
friend class Texture;
};
typedef std::shared_ptr< Pixels > PixelsPointer;
@ -448,7 +467,7 @@ typedef std::shared_ptr<Texture> TexturePointer;
typedef std::vector< TexturePointer > Textures;
// TODO: For now TextureView works with Buffer as a place holder for the Texture.
// TODO: For now TextureView works with Texture as a place holder for the Texture.
// The overall logic should be about the same except that the Texture will be a real GL Texture under the hood
class TextureView {
public:

View file

@ -17,12 +17,14 @@
#include <gpu/Context.h>
#include "EngineStats.h"
using namespace render;
Engine::Engine() :
_sceneContext(std::make_shared<SceneContext>()),
_renderContext(std::make_shared<RenderContext>()) {
addJob<EngineStats>("Stats");
}
void Engine::load() {
@ -57,4 +59,6 @@ void Engine::run() {
for (auto job : _jobs) {
job.run(_sceneContext, _renderContext);
}
}

View file

@ -16,37 +16,37 @@
#include "Context.h"
#include "Task.h"
namespace render {
// The render engine holds all render tasks, and is itself a render task.
// State flows through tasks to jobs via the render and scene contexts -
// the engine should not be known from its jobs.
class Engine : public Task {
public:
Engine();
~Engine() = default;
// The render engine holds all render tasks, and is itself a render task.
// State flows through tasks to jobs via the render and scene contexts -
// the engine should not be known from its jobs.
class Engine : public Task {
public:
// Load any persisted settings, and set up the presets
// This should be run after adding all jobs, and before building ui
void load();
Engine();
~Engine() = default;
// Register the scene
void registerScene(const ScenePointer& scene) { _sceneContext->_scene = scene; }
// Load any persisted settings, and set up the presets
// This should be run after adding all jobs, and before building ui
void load();
// Push a RenderContext
void setRenderContext(const RenderContext& renderContext) { (*_renderContext) = renderContext; }
RenderContextPointer getRenderContext() const { return _renderContext; }
// Register the scene
void registerScene(const ScenePointer& scene) { _sceneContext->_scene = scene; }
// Render a frame
// A frame must have a scene registered and a context set to render
void run();
// Push a RenderContext
void setRenderContext(const RenderContext& renderContext) { (*_renderContext) = renderContext; }
RenderContextPointer getRenderContext() const { return _renderContext; }
protected:
SceneContextPointer _sceneContext;
RenderContextPointer _renderContext;
};
using EnginePointer = std::shared_ptr<Engine>;
// Render a frame
// A frame must have a scene registered and a context set to render
void run();
protected:
SceneContextPointer _sceneContext;
RenderContextPointer _renderContext;
};
using EnginePointer = std::shared_ptr<Engine>;
}

View file

@ -0,0 +1,49 @@
//
// 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 <gpu/Texture.h>
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<Config>(renderContext->jobConfig);
config->bufferCPUCount = gpu::Buffer::getBufferCPUCount();
config->bufferGPUCount = gpu::Buffer::getBufferGPUCount();
config->bufferCPUMemoryUsage = gpu::Buffer::getBufferCPUMemoryUsage();
config->bufferGPUMemoryUsage = gpu::Buffer::getBufferGPUMemoryUsage();
config->textureCPUCount = gpu::Texture::getTextureCPUCount();
config->textureGPUCount = gpu::Texture::getTextureGPUCount();
config->textureCPUMemoryUsage = gpu::Texture::getTextureCPUMemoryUsage();
config->textureGPUMemoryUsage = gpu::Texture::getTextureGPUMemoryUsage();
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();
}

View file

@ -0,0 +1,89 @@
//
// 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 <gpu/Context.h>
#include <QElapsedTimer>
#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(quint32 bufferCPUCount MEMBER bufferCPUCount NOTIFY dirty)
Q_PROPERTY(quint32 bufferGPUCount MEMBER bufferGPUCount NOTIFY dirty)
Q_PROPERTY(qint64 bufferCPUMemoryUsage MEMBER bufferCPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(qint64 bufferGPUMemoryUsage MEMBER bufferGPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint32 textureCPUCount MEMBER textureCPUCount NOTIFY dirty)
Q_PROPERTY(quint32 textureGPUCount MEMBER textureGPUCount NOTIFY dirty)
Q_PROPERTY(qint64 textureCPUMemoryUsage MEMBER textureCPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(qint64 textureGPUMemoryUsage MEMBER textureGPUMemoryUsage NOTIFY dirty)
Q_PROPERTY(quint32 frameDrawcallCount MEMBER frameDrawcallCount NOTIFY dirty)
Q_PROPERTY(quint32 frameDrawcallRate MEMBER frameDrawcallRate NOTIFY dirty)
Q_PROPERTY(quint32 frameTriangleCount MEMBER frameTriangleCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTriangleRate MEMBER frameTriangleRate NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureCount MEMBER frameTextureCount NOTIFY dirty)
Q_PROPERTY(quint32 frameTextureRate MEMBER frameTextureRate NOTIFY dirty)
public:
EngineStatsConfig() : Job::Config(true) {}
quint32 bufferCPUCount{ 0 };
quint32 bufferGPUCount{ 0 };
qint64 bufferCPUMemoryUsage{ 0 };
qint64 bufferGPUMemoryUsage{ 0 };
quint32 textureCPUCount{ 0 };
quint32 textureGPUCount{ 0 };
qint64 textureCPUMemoryUsage{ 0 };
qint64 textureGPUMemoryUsage{ 0 };
quint32 frameDrawcallCount{ 0 };
quint32 frameDrawcallRate{ 0 };
quint32 frameTriangleCount{ 0 };
quint32 frameTriangleRate{ 0 };
quint32 frameTextureCount{ 0 };
quint32 frameTextureRate{ 0 };
void emitDirty() { emit dirty(); }
signals:
void dirty();
};
class EngineStats {
gpu::ContextStats _gpuStats;
QElapsedTimer _frameTimer;
public:
using Config = EngineStatsConfig;
using JobModel = Job::Model<EngineStats, Config>;
EngineStats() { _frameTimer.start(); }
void configure(const Config& configuration) {}
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext);
};
}
#endif