rhi: Drop the profiler for now

The system we inherited from the original Qt 5.14 introduction of QRhi
is a text stream based solution where resource creation and frame
timings are sent in a comma-separated format to a QIODevice.

This, while useful to get insights about the number of resources at a
given time, is not actively helpful. The frameworks built on top (Qt
Quick, Qt Quick 3D) are expected to provide solutions for logging
timings in a different way (e.g. via the QML Profiler). Similarly,
tracking active resources and generating statistics from that is
better handled on a higher level.

The unique bits, such as the Vulkan memory allocator statistics and
the GPU frame timestamps, are converted into APIs in QRhi. This way a
user of QRhi can query it at any time and do whatever it sees fit with
the data.

When it comes to the GPU timestamps, that has a somewhat limited value
due to the heavy asynchronousness, hence the callback based
API. Nonetheless, this is still useful since it is the only means of
reporting some frame timing data (an approx. elapsed milliseconds for
a frame) from the GPU side.

Change-Id: I67cd58b81aaa7e343c11731f9aa5b4804c2a1823
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
This commit is contained in:
Laszlo Agocs 2021-12-17 17:04:38 +01:00
parent dcc2704d17
commit 23f8d6c57f
25 changed files with 176 additions and 1941 deletions

View File

@ -206,8 +206,6 @@ qt_internal_add_module(Gui
rhi/qrhi_p_p.h
rhi/qrhinull.cpp rhi/qrhinull_p.h
rhi/qrhinull_p_p.h
rhi/qrhiprofiler.cpp rhi/qrhiprofiler_p.h
rhi/qrhiprofiler_p_p.h
rhi/qshader.cpp rhi/qshader_p.h
rhi/qshader_p_p.h
rhi/qshaderdescription.cpp rhi/qshaderdescription_p.h

View File

@ -418,8 +418,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
Errors are printed to the output via qWarning(). Additional debug messages
can be enabled via the following logging categories. Messages from these
categories are not printed by default unless explicitly enabled via
QRhi::EnableProfiling or the facilities of QLoggingCategory (such as, the
\c QT_LOGGING_RULES environment variable).
QLoggingCategory or the \c QT_LOGGING_RULES environment variable.
\list
\li \c{qt.rhi.general}
@ -427,7 +426,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
It is strongly advised to inspect the output with the logging categories
(\c{qt.rhi.*}) enabled whenever a QRhi-based application is not behaving as
expected.
expected. For better interoperation with Qt Quick, the environment variable
\c{QSG_INFO} also enables these debug prints.
*/
/*!
@ -445,11 +445,7 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\enum QRhi::Flag
Describes what special features to enable.
\value EnableProfiling Enables gathering timing (CPU, GPU) and resource
(QRhiBuffer, QRhiTexture, etc.) information and additional metadata. See
QRhiProfiler. Avoid enabling in production builds as it may involve a
performance penalty. Also enables debug messages from the \c{qt.rhi.*}
logging categories.
\value EnableProfiling This flag has currently no effect.
\value EnableDebugMarkers Enables debug marker groups. Without this frame
debugging features like making debug groups and custom resource name
@ -518,7 +514,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
QRhiCommandBuffer::debugMarkBegin()) are supported.
\value Timestamps Indicates that command buffer timestamps are supported.
Relevant for QRhiProfiler::gpuFrameTimes().
Relevant for addGpuFrameTimeCallback(). Can be expected to be supported on
D3D11 and Vulkan, assuming the underlying implementation supports it.
\value Instancing Indicates that instanced drawing is supported. In
practice this feature will be unsupported with OpenGL ES 2.0 and OpenGL
@ -2048,8 +2045,7 @@ QByteArray QRhiResource::name() const
This has two uses: to get descriptive names for the native graphics
resources visible in graphics debugging tools, such as
\l{https://renderdoc.org/}{RenderDoc} and
\l{https://developer.apple.com/xcode/}{XCode}, and in the output stream of
QRhiProfiler.
\l{https://developer.apple.com/xcode/}{XCode}.
When it comes to naming native objects by relaying the name via the
appropriate graphics API, note that the name is ignored when
@ -2066,7 +2062,6 @@ QByteArray QRhiResource::name() const
void QRhiResource::setName(const QByteArray &name)
{
m_objectName = name;
m_objectName.replace(',', '_'); // cannot contain comma for QRhiProfiler
}
/*!
@ -4851,23 +4846,6 @@ void QRhiImplementation::textureFormatInfo(QRhiTexture::Format format, const QSi
*bytesPerPixel = bpc;
}
// Approximate because it excludes subresource alignment or multisampling.
quint32 QRhiImplementation::approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, int depth,
int mipCount, int layerCount)
{
quint32 approxSize = 0;
for (int level = 0; level < mipCount; ++level) {
quint32 byteSize = 0;
const QSize size(qFloor(qreal(qMax(1, baseSize.width() >> level))),
qFloor(qreal(qMax(1, baseSize.height() >> level))));
textureFormatInfo(format, size, nullptr, &byteSize, nullptr);
approxSize += byteSize;
}
approxSize *= depth; // 3D texture depth or 1 otherwise
approxSize *= uint(layerCount); // 6 for cubemaps or 1 otherwise
return approxSize;
}
bool QRhiImplementation::sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps)
{
if (ps->cbeginShaderStages() == ps->cendShaderStages()) {
@ -5000,16 +4978,13 @@ QRhi::~QRhi()
}
/*!
\return a new QRhi instance with a backend for the graphics API specified by \a impl.
\return a new QRhi instance with a backend for the graphics API specified
by \a impl with the specified \a flags.
\a params must point to an instance of one of the backend-specific
subclasses of QRhiInitParams, such as, QRhiVulkanInitParams,
QRhiMetalInitParams, QRhiD3D11InitParams, QRhiGles2InitParams. See these
classes for examples on creating a QRhi.
\a flags is optional. It is used to enable profile and debug related
features that are potentially expensive and should only be used during
development.
*/
QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRhiNativeHandles *importDevice)
{
@ -5063,12 +5038,6 @@ QRhi *QRhi::create(Implementation impl, QRhiInitParams *params, Flags flags, QRh
if (r->d) {
r->d->q = r.data();
if (flags.testFlag(EnableProfiling)) {
QRhiProfilerPrivate *profD = QRhiProfilerPrivate::get(&r->d->profiler);
profD->rhiDWhenEnabled = r->d;
const_cast<QLoggingCategory &>(QRHI_LOG_INFO()).setEnabled(QtDebugMsg, true);
}
// Play nice with QSG_INFO since that is still the most commonly used
// way to get graphics info printed from Qt Quick apps, and the Quick
// scenegraph is our primary user.
@ -5230,6 +5199,36 @@ void QRhi::runCleanup()
d->cleanupCallbacks.clear();
}
/*!
Registers a \a callback that is called with an elapsed time calculated from
GPU timestamps asynchronously after a timestamp becomes available at some
point after presenting a frame.
The callback is called with a float value that is meant to be in
milliseconds and represents the elapsed time on the GPU side for a given
frame. Care must be exercised with the interpretation of the value, as what
it exactly is is not controlled by Qt and depends on the underlying
graphics API and its implementation. In particular, comparing the values
between different graphics APIs is discouraged and may be meaningless.
The timing values become available asynchronously, sometimes several frames
after the frame has been submitted in endFrame(). There is currently no way
to identify the frame. The callback is invoked whenever the timestamp
queries complete.
\note This is only supported when the Timestamp feature is reported as
supported from isFeatureSupported(). Otherwise the \a callback is never
called.
The \a callback is always called on the thread the QRhi lives and operates
on. While not guaranteed, it is typical that the callback is invoked from
within beginFrame().
*/
void QRhi::addGpuFrameTimeCallback(const GpuFrameTimeCallback &callback)
{
d->addGpuFrameTimeCallback(callback);
}
/*!
\class QRhiResourceUpdateBatch
\internal
@ -6403,18 +6402,6 @@ bool QRhi::makeThreadLocalNativeContextCurrent()
return d->makeThreadLocalNativeContextCurrent();
}
/*!
\return the associated QRhiProfiler instance.
An instance is always available for each QRhi, but it is not very useful
without EnableProfiling because no data is collected without setting the
flag upon creation.
*/
QRhiProfiler *QRhi::profiler()
{
return &d->profiler;
}
/*!
Attempts to release resources in the backend's caches. This can include both
CPU and GPU resources. Only memory and resources that can be recreated
@ -6548,6 +6535,45 @@ void QRhi::setPipelineCacheData(const QByteArray &data)
d->setPipelineCacheData(data);
}
/*!
\struct QRhiMemAllocStats
\internal
\inmodule QtGui
\brief Statistics provided from the underlying memory allocator.
*/
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QRhiMemAllocStats &info)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "QRhiMemAllocStats(blockCount=" << info.blockCount
<< " allocCount=" << info.allocCount
<< " usedBytes=" << info.usedBytes
<< " unusedBytes=" << info.unusedBytes
<< ')';
return dbg;
}
#endif
/*!
Gathers and returns some statistics about the memory allocation of graphics
resources. Only supported with some backends. With graphics APIs where
there is no lower level control over resource memory allocations, this will
never be supported and all fields in the results are 0.
With Vulkan, the values are valid always, and are queried from the
underlying memory allocator library. This gives an insight into the memory
requirements of the active buffers and textures.
\note Gathering the data may not be free, and therefore the function should
not be called at a high frequency.
*/
QRhiMemAllocStats QRhi::graphicsMemoryAllocationStatistics() const
{
return d->graphicsMemoryAllocationStatistics();
}
/*!
\return a new graphics pipeline resource.

View File

@ -74,7 +74,6 @@ class QRhiSampler;
class QRhiCommandBuffer;
class QRhiResourceUpdateBatch;
class QRhiResourceUpdateBatchPrivate;
class QRhiProfiler;
class Q_GUI_EXPORT QRhiDepthStencilClearValue
{
@ -1541,6 +1540,20 @@ Q_DECLARE_TYPEINFO(QRhiDriverInfo, Q_RELOCATABLE_TYPE);
Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiDriverInfo &);
#endif
struct Q_GUI_EXPORT QRhiMemAllocStats
{
quint32 blockCount = 0;
quint32 allocCount = 0;
quint64 usedBytes = 0;
quint64 unusedBytes = 0;
};
Q_DECLARE_TYPEINFO(QRhiMemAllocStats, Q_RELOCATABLE_TYPE);
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug, const QRhiMemAllocStats &);
#endif
struct Q_GUI_EXPORT QRhiInitParams
{
};
@ -1645,6 +1658,9 @@ public:
void addCleanupCallback(const CleanupCallback &callback);
void runCleanup();
using GpuFrameTimeCallback = std::function<void(float t)>;
void addGpuFrameTimeCallback(const GpuFrameTimeCallback &callback);
QRhiGraphicsPipeline *newGraphicsPipeline();
QRhiComputePipeline *newComputePipeline();
QRhiShaderResourceBindings *newShaderResourceBindings();
@ -1719,8 +1735,6 @@ public:
const QRhiNativeHandles *nativeHandles();
bool makeThreadLocalNativeContextCurrent();
QRhiProfiler *profiler();
static const int MAX_MIP_LEVELS = 16; // a width and/or height of 65536 should be enough for everyone
void releaseCachedResources();
@ -1730,6 +1744,8 @@ public:
QByteArray pipelineCacheData();
void setPipelineCacheData(const QByteArray &data);
QRhiMemAllocStats graphicsMemoryAllocationStatistics() const;
protected:
QRhi();

View File

@ -52,7 +52,6 @@
//
#include "qrhi_p.h"
#include "qrhiprofiler_p_p.h"
#include <QBitArray>
#include <QAtomicInt>
#include <QLoggingCategory>
@ -61,8 +60,6 @@ QT_BEGIN_NAMESPACE
#define QRHI_RES(t, x) static_cast<t *>(x)
#define QRHI_RES_RHI(t) t *rhiD = static_cast<t *>(m_rhi)
#define QRHI_PROF QRhiProfilerPrivate *rhiP = m_rhi->profilerPrivateOrNull()
#define QRHI_PROF_F(f) for (bool qrhip_enabled = rhiP != nullptr; qrhip_enabled; qrhip_enabled = false) rhiP->f
Q_DECLARE_LOGGING_CATEGORY(QRHI_LOG_INFO)
@ -168,7 +165,7 @@ public:
virtual int resourceLimit(QRhi::ResourceLimit limit) const = 0;
virtual const QRhiNativeHandles *nativeHandles() = 0;
virtual QRhiDriverInfo driverInfo() const = 0;
virtual void sendVMemStatsToProfiler() = 0;
virtual QRhiMemAllocStats graphicsMemoryAllocationStatistics() = 0;
virtual bool makeThreadLocalNativeContextCurrent() = 0;
virtual void releaseCachedResources() = 0;
virtual bool isDeviceLost() const = 0;
@ -182,15 +179,6 @@ public:
QSize *blockDim) const;
void textureFormatInfo(QRhiTexture::Format format, const QSize &size,
quint32 *bpl, quint32 *byteSize, quint32 *bytesPerPixel) const;
quint32 approxByteSizeForTexture(QRhiTexture::Format format, const QSize &baseSize, int depth,
int mipCount, int layerCount);
QRhiProfilerPrivate *profilerPrivateOrNull()
{
// return null when QRhi::EnableProfiling was not set
QRhiProfilerPrivate *p = QRhiProfilerPrivate::get(&profiler);
return p->rhiDWhenEnabled ? p : nullptr;
}
// only really care about resources that own native graphics resources underneath
void registerResource(QRhiResource *res)
@ -221,6 +209,22 @@ public:
cleanupCallbacks.append(callback);
}
void addGpuFrameTimeCallback(const QRhi::GpuFrameTimeCallback &callback)
{
gpuFrameTimeCallbacks.append(callback);
}
bool hasGpuFrameTimeCallback() const
{
return !gpuFrameTimeCallbacks.isEmpty();
}
void runGpuFrameTimeCallbacks(float t)
{
for (const QRhi::GpuFrameTimeCallback &f : qAsConst(gpuFrameTimeCallbacks))
f(t);
}
bool sanityCheckGraphicsPipeline(QRhiGraphicsPipeline *ps);
bool sanityCheckShaderResourceBindings(QRhiShaderResourceBindings *srb);
void updateLayoutDesc(QRhiShaderResourceBindings *srb);
@ -242,13 +246,13 @@ public:
private:
QRhi::Implementation implType;
QThread *implThread;
QRhiProfiler profiler;
QVarLengthArray<QRhiResourceUpdateBatch *, 4> resUpdPool;
quint64 resUpdPoolMap = 0;
int lastResUpdIdx = -1;
QSet<QRhiResource *> resources;
QSet<QRhiResource *> pendingDeleteResources;
QVarLengthArray<QRhi::CleanupCallback, 4> cleanupCallbacks;
QVarLengthArray<QRhi::GpuFrameTimeCallback, 4> gpuFrameTimeCallbacks;
friend class QRhi;
friend class QRhiResourceUpdateBatchPrivate;

View File

@ -611,9 +611,9 @@ QRhiDriverInfo QRhiD3D11::driverInfo() const
return driverInfoStruct;
}
void QRhiD3D11::sendVMemStatsToProfiler()
QRhiMemAllocStats QRhiD3D11::graphicsMemoryAllocationStatistics()
{
// nothing to do here
return {};
}
bool QRhiD3D11::makeThreadLocalNativeContextCurrent()
@ -1084,7 +1084,6 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
QD3D11SwapChain *swapChainD = QRHI_RES(QD3D11SwapChain, swapChain);
contextState.currentSwapChain = swapChainD;
const int currentFrameSlot = swapChainD->currentFrameSlot;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
if (swapChainD->timestampActive[currentFrameSlot]) {
ID3D11Query *tsDisjoint = swapChainD->timestampDisjointQuery[currentFrameSlot];
@ -1104,8 +1103,7 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
if (ok) {
if (!dj.Disjoint && dj.Frequency) {
const float elapsedMs = (timestamps[1] - timestamps[0]) / float(dj.Frequency) * 1000.0f;
// finally got a value, just report it, the profiler cares about min/max/avg anyway
QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
runGpuFrameTimeCallbacks(elapsedMs);
}
swapChainD->timestampActive[currentFrameSlot] = false;
} // else leave timestampActive set to true, will retry in a subsequent beginFrame
@ -1117,8 +1115,6 @@ QRhi::FrameOpResult QRhiD3D11::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->msaaRtv[currentFrameSlot] : swapChainD->backBufferRtv;
swapChainD->rt.d.dsv = swapChainD->ds ? swapChainD->ds->dsv : nullptr;
QRHI_PROF_F(beginSwapChainFrame(swapChain));
finishActiveReadbacks();
return QRhi::FrameOpSuccess;
@ -1155,10 +1151,6 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
swapChainD->timestampActive[currentFrameSlot] = true;
}
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
// this must be done before the Present
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
if (!flags.testFlag(QRhi::SkipPresent)) {
const UINT presentFlags = 0;
HRESULT hr = swapChainD->swapChain->Present(swapChainD->swapInterval, presentFlags);
@ -1449,7 +1441,6 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
{
QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
@ -1498,7 +1489,6 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Failed to create buffer: %s", qPrintable(comErrorMessage(hr)));
continue;
}
QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.stagingBuf)), bufD, readback.byteSize));
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
@ -1630,9 +1620,6 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
qWarning("Failed to create readback staging texture: %s", qPrintable(comErrorMessage(hr)));
return;
}
QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(stagingTex)),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
byteSize));
QD3D11CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QD3D11CommandBuffer::Command::CopySubRes;
@ -1677,7 +1664,6 @@ void QRhiD3D11::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
void QRhiD3D11::finishActiveReadbacks()
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiD3D11::TextureReadback &readback(activeTextureReadbacks[i]);
@ -1703,7 +1689,6 @@ void QRhiD3D11::finishActiveReadbacks()
}
readback.stagingTex->Release();
QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingTex))));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@ -1725,7 +1710,6 @@ void QRhiD3D11::finishActiveReadbacks()
}
readback.stagingBuf->Release();
QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.stagingBuf))));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@ -2680,11 +2664,8 @@ void QD3D11Buffer::destroy()
}
QRHI_RES_RHI(QRhiD3D11);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
static inline uint toD3DBufferUsage(QRhiBuffer::UsageFlags usage)
@ -2742,9 +2723,6 @@ bool QD3D11Buffer::create()
if (!m_objectName.isEmpty())
buffer->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, quint32(roundedSize), m_type == Dynamic ? 2 : 1, m_type == Dynamic ? 1 : 0));
generation += 1;
rhiD->registerResource(this);
return true;
@ -2839,11 +2817,8 @@ void QD3D11RenderBuffer::destroy()
tex = nullptr;
QRHI_RES_RHI(QRhiD3D11);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
bool QD3D11RenderBuffer::create()
@ -2912,9 +2887,6 @@ bool QD3D11RenderBuffer::create()
if (!m_objectName.isEmpty())
tex->SetPrivateData(WKPDID_D3DDebugObjectName, UINT(m_objectName.size()), m_objectName.constData());
QRHI_PROF;
QRHI_PROF_F(newRenderBuffer(this, false, false, int(sampleDesc.Count)));
generation += 1;
rhiD->registerResource(this);
return true;
@ -2969,11 +2941,8 @@ void QD3D11Texture::destroy()
tex3D = nullptr;
QRHI_RES_RHI(QRhiD3D11);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
static inline DXGI_FORMAT toD3DDepthTextureSRVFormat(QRhiTexture::Format format)
@ -3197,9 +3166,6 @@ bool QD3D11Texture::create()
if (!finishCreate())
return false;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count)));
owns = true;
rhiD->registerResource(this);
return true;
@ -3221,12 +3187,6 @@ bool QD3D11Texture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate())
return false;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), int(sampleDesc.Count)));
owns = false;
QRHI_RES_RHI(QRhiD3D11);
rhiD->registerResource(this);
@ -4422,11 +4382,8 @@ void QD3D11SwapChain::destroy()
}
QRHI_RES_RHI(QRhiD3D11);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
QRhiCommandBuffer *QD3D11SwapChain::currentFrameCommandBuffer()
@ -4777,9 +4734,7 @@ bool QD3D11SwapChain::createOrResize()
rtD->d.colorAttCount = 1;
rtD->d.dsAttCount = m_depthStencil ? 1 : 0;
QRHI_PROF;
QRHI_PROF_F(resizeSwapChain(this, BUFFER_COUNT, sampleDesc.Count > 1 ? BUFFER_COUNT : 0, int(sampleDesc.Count)));
if (rhiP) {
if (rhiD->hasGpuFrameTimeCallback()) {
D3D11_QUERY_DESC queryDesc;
memset(&queryDesc, 0, sizeof(queryDesc));
for (int i = 0; i < BUFFER_COUNT; ++i) {

View File

@ -697,7 +697,7 @@ public:
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
QRhiDriverInfo driverInfo() const override;
void sendVMemStatsToProfiler() override;
QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
bool makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
bool isDeviceLost() const override;

View File

@ -1189,9 +1189,9 @@ QRhiDriverInfo QRhiGles2::driverInfo() const
return driverInfoStruct;
}
void QRhiGles2::sendVMemStatsToProfiler()
QRhiMemAllocStats QRhiGles2::graphicsMemoryAllocationStatistics()
{
// nothing to do here
return {};
}
bool QRhiGles2::makeThreadLocalNativeContextCurrent()
@ -1774,9 +1774,6 @@ QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
currentSwapChain = swapChainD;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(beginSwapChainFrame(swapChain));
executeDeferredReleases();
swapChainD->cb.resetState();
@ -1797,10 +1794,6 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
executeCommandBuffer(&swapChainD->cb);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
// this must be done before the swap
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
if (swapChainD->surface && !flags.testFlag(QRhi::SkipPresent)) {
ctx->swapBuffers(swapChainD->surface);
needsMakeCurrentDueToSwap = true;
@ -4430,8 +4423,6 @@ void QGles2Buffer::destroy()
QRHI_RES_RHI(QRhiGles2);
if (rhiD) {
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -4442,7 +4433,6 @@ bool QGles2Buffer::create()
destroy();
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
nonZeroSize = m_size <= 0 ? 256 : m_size;
@ -4452,7 +4442,6 @@ bool QGles2Buffer::create()
return false;
}
data.resize(nonZeroSize);
QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), 0, 1));
return true;
}
@ -4471,7 +4460,6 @@ bool QGles2Buffer::create()
usageState.access = AccessNone;
QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), 1, 0));
rhiD->registerResource(this);
return true;
}
@ -4543,8 +4531,6 @@ void QGles2RenderBuffer::destroy()
if (rhiD) {
if (owns)
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -4555,14 +4541,11 @@ bool QGles2RenderBuffer::create()
destroy();
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
samples = rhiD->effectiveSampleCount(m_sampleCount);
if (m_flags.testFlag(UsedWithSwapChainOnly)) {
if (m_type == DepthStencil) {
QRHI_PROF_F(newRenderBuffer(this, false, true, samples));
if (m_type == DepthStencil)
return true;
}
qWarning("RenderBuffer: UsedWithSwapChainOnly is meaningless in combination with Color");
}
@ -4602,7 +4585,6 @@ bool QGles2RenderBuffer::create()
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, stencilStorage,
size.width(), size.height());
}
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
break;
case QRhiRenderBuffer::Color:
{
@ -4623,7 +4605,6 @@ bool QGles2RenderBuffer::create()
rhiD->f->glRenderbufferStorage(GL_RENDERBUFFER, internalFormat,
size.width(), size.height());
}
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
}
break;
default:
@ -4656,9 +4637,6 @@ bool QGles2RenderBuffer::createFrom(NativeRenderBuffer src)
renderbuffer = src.object;
QRHI_PROF;
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
owns = false;
generation += 1;
rhiD->registerResource(this);
@ -4702,8 +4680,6 @@ void QGles2Texture::destroy()
if (rhiD) {
if (owns)
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
rhiD->unregisterResource(this);
}
}
@ -4850,9 +4826,6 @@ bool QGles2Texture::create()
specified = false;
}
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, 1));
owns = true;
generation += 1;
@ -4873,13 +4846,10 @@ bool QGles2Texture::createFrom(QRhiTexture::NativeTexture src)
specified = true;
zeroInitialized = true;
QRHI_RES_RHI(QRhiGles2);
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, 1));
owns = false;
generation += 1;
QRHI_RES_RHI(QRhiGles2);
rhiD->registerResource(this);
return true;
}
@ -5447,11 +5417,8 @@ QGles2SwapChain::~QGles2SwapChain()
void QGles2SwapChain::destroy()
{
QRHI_RES_RHI(QRhiGles2);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
QRhiCommandBuffer *QGles2SwapChain::currentFrameCommandBuffer()
@ -5508,10 +5475,6 @@ bool QGles2SwapChain::createOrResize()
frameCount = 0;
QRHI_PROF;
// make something up
QRHI_PROF_F(resizeSwapChain(this, 2, m_sampleCount > 1 ? 2 : 0, m_sampleCount));
// The only reason to register this fairly fake gl swapchain
// object with no native resources underneath is to be able to
// implement a safe destroy().

View File

@ -837,7 +837,7 @@ public:
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
QRhiDriverInfo driverInfo() const override;
void sendVMemStatsToProfiler() override;
QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
bool makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
bool isDeviceLost() const override;

View File

@ -670,9 +670,9 @@ QRhiDriverInfo QRhiMetal::driverInfo() const
return driverInfoStruct;
}
void QRhiMetal::sendVMemStatsToProfiler()
QRhiMemAllocStats QRhiMetal::graphicsMemoryAllocationStatistics()
{
// nothing to do here
return {};
}
bool QRhiMetal::makeThreadLocalNativeContextCurrent()
@ -1470,9 +1470,6 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(beginSwapChainFrame(swapChain));
executeDeferredReleases();
swapChainD->cbWrapper.resetState();
finishActiveReadbacks();
@ -1504,9 +1501,6 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
[swapChainD->cbWrapper.d->cb commit];
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
[d->captureScope endScope];
if (needsPresent)
@ -1776,7 +1770,6 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
{
QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
@ -1835,7 +1828,6 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
Q_ASSERT(!utexD->d->stagingBuf[currentFrameSlot]);
utexD->d->stagingBuf[currentFrameSlot] = [d->dev newBufferWithLength: NSUInteger(stagingSize)
options: MTLResourceStorageModeShared];
QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, quint32(stagingSize)));
void *mp = [utexD->d->stagingBuf[currentFrameSlot] contents];
qsizetype curOfs = 0;
@ -1854,7 +1846,6 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
e.stagingBuffer.buffer = utexD->d->stagingBuf[currentFrameSlot];
utexD->d->stagingBuf[currentFrameSlot] = nil;
d->releaseQueue.append(e);
QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
} else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
Q_ASSERT(u.src && u.dst);
QMetalTexture *srcD = QRHI_RES(QMetalTexture, u.src);
@ -1916,10 +1907,6 @@ void QRhiMetal::enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdate
textureFormatInfo(readback.format, readback.pixelSize, &bpl, &readback.bufSize, nullptr);
readback.buf = [d->dev newBufferWithLength: readback.bufSize options: MTLResourceStorageModeShared];
QRHI_PROF_F(newReadbackBuffer(qint64(qintptr(readback.buf)),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
readback.bufSize));
ensureBlit();
[blitEnc copyFromTexture: src
sourceSlice: NSUInteger(is3D ? 0 : u.rb.layer())
@ -2218,7 +2205,6 @@ void QRhiMetal::executeDeferredReleases(bool forced)
void QRhiMetal::finishActiveReadbacks(bool forced)
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = d->activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiMetalData::TextureReadback &readback(d->activeTextureReadbacks[i]);
@ -2230,8 +2216,6 @@ void QRhiMetal::finishActiveReadbacks(bool forced)
memcpy(readback.result->data.data(), p, readback.bufSize);
[readback.buf release];
QRHI_PROF_F(releaseReadbackBuffer(qint64(qintptr(readback.buf))));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@ -2275,8 +2259,6 @@ void QMetalBuffer::destroy()
QRHI_RES_RHI(QRhiMetal);
if (rhiD) {
rhiD->d->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -2324,9 +2306,6 @@ bool QMetalBuffer::create()
}
}
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, roundedSize, d->slotted ? QMTL_FRAMES_IN_FLIGHT : 1, 0));
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@ -2556,8 +2535,6 @@ void QMetalRenderBuffer::destroy()
QRHI_RES_RHI(QRhiMetal);
if (rhiD) {
rhiD->d->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -2582,7 +2559,6 @@ bool QMetalRenderBuffer::create()
desc.resourceOptions = MTLResourceStorageModePrivate;
desc.usage = MTLTextureUsageRenderTarget;
bool transientBacking = false;
switch (m_type) {
case DepthStencil:
#ifdef Q_OS_MACOS
@ -2591,7 +2567,6 @@ bool QMetalRenderBuffer::create()
? MTLPixelFormatDepth24Unorm_Stencil8 : MTLPixelFormatDepth32Float_Stencil8;
#else
desc.storageMode = MTLStorageModeMemoryless;
transientBacking = true;
d->format = MTLPixelFormatDepth32Float_Stencil8;
#endif
desc.pixelFormat = d->format;
@ -2615,9 +2590,6 @@ bool QMetalRenderBuffer::create()
if (!m_objectName.isEmpty())
d->tex.label = [NSString stringWithUTF8String: m_objectName.constData()];
QRHI_PROF;
QRHI_PROF_F(newRenderBuffer(this, transientBacking, false, samples));
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@ -2675,8 +2647,6 @@ void QMetalTexture::destroy()
QRHI_RES_RHI(QRhiMetal);
if (rhiD) {
rhiD->d->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
rhiD->unregisterResource(this);
}
}
@ -2793,9 +2763,6 @@ bool QMetalTexture::create()
d->owns = true;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), samples));
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@ -2815,9 +2782,6 @@ bool QMetalTexture::createFrom(QRhiTexture::NativeTexture src)
d->owns = false;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
lastActiveFrameSlot = -1;
generation += 1;
QRHI_RES_RHI(QRhiMetal);
@ -3966,8 +3930,6 @@ void QMetalSwapChain::destroy()
QRHI_RES_RHI(QRhiMetal);
if (rhiD) {
rhiD->swapchains.remove(this);
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
rhiD->unregisterResource(this);
}
}
@ -4159,9 +4121,6 @@ bool QMetalSwapChain::createOrResize()
[desc release];
}
QRHI_PROF;
QRHI_PROF_F(resizeSwapChain(this, QMTL_FRAMES_IN_FLIGHT, samples > 1 ? QMTL_FRAMES_IN_FLIGHT : 0, samples));
if (needsRegistration)
rhiD->registerResource(this);

View File

@ -448,7 +448,7 @@ public:
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
QRhiDriverInfo driverInfo() const override;
void sendVMemStatsToProfiler() override;
QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
bool makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
bool isDeviceLost() const override;

View File

@ -59,8 +59,7 @@ QT_BEGIN_NAMESPACE
The Null backend does not issue any graphics calls and creates no
resources. All QRhi operations will succeed as normal so applications can
still be run, albeit potentially at an unthrottled speed, depending on
their frame rendering strategy. The backend reports resources to
QRhiProfiler as usual.
their frame rendering strategy.
*/
/*!
@ -184,9 +183,9 @@ QRhiDriverInfo QRhiNull::driverInfo() const
return info;
}
void QRhiNull::sendVMemStatsToProfiler()
QRhiMemAllocStats QRhiNull::graphicsMemoryAllocationStatistics()
{
// nothing to do here
return {};
}
bool QRhiNull::makeThreadLocalNativeContextCurrent()
@ -382,8 +381,6 @@ QRhi::FrameOpResult QRhiNull::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFr
{
Q_UNUSED(flags);
currentSwapChain = swapChain;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(beginSwapChainFrame(swapChain));
return QRhi::FrameOpSuccess;
}
@ -391,9 +388,6 @@ QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameF
{
Q_UNUSED(flags);
QNullSwapChain *swapChainD = QRHI_RES(QNullSwapChain, swapChain);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
QRHI_PROF_F(swapChainFrameGpuTime(swapChain, 0.000666f));
swapChainD->frameCount += 1;
currentSwapChain = nullptr;
return QRhi::FrameOpSuccess;
@ -604,11 +598,8 @@ void QNullBuffer::destroy()
data = nullptr;
QRHI_RES_RHI(QRhiNull);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
bool QNullBuffer::create()
@ -619,11 +610,6 @@ bool QNullBuffer::create()
data = new char[m_size];
memset(data, 0, m_size);
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, uint(m_size), 1, 0));
// If we register the buffer to the profiler, then it needs to be registered to the
// QRhi too (even though we normally do that for native-resource-owning objects only),
// in order to be able to implement destroy() in a robust manner.
QRHI_RES_RHI(QRhiNull);
rhiD->registerResource(this);
@ -653,11 +639,8 @@ void QNullRenderBuffer::destroy()
valid = false;
QRHI_RES_RHI(QRhiNull);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
bool QNullRenderBuffer::create()
@ -668,8 +651,6 @@ bool QNullRenderBuffer::create()
valid = true;
generation += 1;
QRHI_PROF;
QRHI_PROF_F(newRenderBuffer(this, false, false, 1));
QRHI_RES_RHI(QRhiNull);
rhiD->registerResource(this);
@ -697,11 +678,8 @@ void QNullTexture::destroy()
valid = false;
QRHI_RES_RHI(QRhiNull);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
bool QNullTexture::create()
@ -735,8 +713,6 @@ bool QNullTexture::create()
generation += 1;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, mipLevelCount, layerCount, 1));
rhiD->registerResource(this);
return true;
@ -750,17 +726,9 @@ bool QNullTexture::createFrom(QRhiTexture::NativeTexture src)
valid = true;
QRHI_RES_RHI(QRhiNull);
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
const bool hasMipMaps = m_flags.testFlag(MipMapped);
QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
const int mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
generation += 1;
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, mipLevelCount, isCube ? 6 : (isArray ? m_arraySize : 1), 1));
QRHI_RES_RHI(QRhiNull);
rhiD->registerResource(this);
return true;
@ -1003,11 +971,8 @@ QNullSwapChain::~QNullSwapChain()
void QNullSwapChain::destroy()
{
QRHI_RES_RHI(QRhiNull);
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
QRhiCommandBuffer *QNullSwapChain::currentFrameCommandBuffer()
@ -1047,8 +1012,6 @@ bool QNullSwapChain::createOrResize()
rt.d.pixelSize = m_currentPixelSize;
frameCount = 0;
QRHI_PROF;
QRHI_PROF_F(resizeSwapChain(this, 1, 0, 1));
if (needsRegistration) {
QRHI_RES_RHI(QRhiNull);
rhiD->registerResource(this);

View File

@ -309,7 +309,7 @@ public:
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
QRhiDriverInfo driverInfo() const override;
void sendVMemStatsToProfiler() override;
QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
bool makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
bool isDeviceLost() const override;

View File

@ -1,611 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qrhiprofiler_p_p.h"
#include "qrhi_p_p.h"
#include <QtCore/qiodevice.h>
QT_BEGIN_NAMESPACE
/*!
\class QRhiProfiler
\internal
\inmodule QtGui
\brief Collects resource and timing information from an active QRhi.
A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The
profiler is active only when the QRhi was created with
QRhi::EnableProfiling. No data is collected otherwise.
\note GPU timings are only available when QRhi indicates that
QRhi::Timestamps is supported.
Besides collecting data from the QRhi implementations, some additional
values are calculated. For example, for textures and similar resources the
profiler gives an estimate of the complete amount of memory the resource
needs.
\section2 Output Format
The output is comma-separated text. Each line has a number of
comma-separated entries and each line ends with a comma.
For example:
\badcode
1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524,
1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576,
1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400,
11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800,
9,9,140446058913648,Qt texture,slot,0,size,262144,
10,9,140446058913648,Qt texture,slot,0,
17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167,
18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333,
17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583,
18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0,
12,5070,140446057944592,,
2,5079,140446057947376,Triangle ubuf,
2,5079,140446057946208,Triangle vbuf,
2,5079,140446057947440,,
2,5079,140446057950544,,
2,5079,140446057950416,,
8,5079,140446058913648,Qt texture,
2,5079,140446057982528,Cube ubuf (textured),
2,5079,140446057984784,Cube vbuf (textured),
2,5079,140446058964560,Triangle ubuf,
2,5079,140446058963904,Triangle vbuf,
8,5079,140446058794928,Texture for offscreen content,
2,5079,140446058947920,Cube ubuf (textured with offscreen),
2,5079,140446058795856,Cube vbuf (textured with offscreen),
6,5079,140446057945392,,
\endcode
Each line starts with \c op, \c timestamp, \c res, \c name where op is a
value from StreamOp, timestamp is a recording timestamp in milliseconds
(qint64), res is a number (quint64) referring to the QRhiResource the entry
refers to, or 0 if not applicable. \c name is the value of
QRhiResource::name() and may be empty as well. The \c name will never
contain a comma.
This is followed by any number of \c{key, value} pairs where \c key is an
unspecified string and \c value is a number. If \c key starts with \c F, it
indicates the value is a float. Otherwise assume that the value is a
qint64.
*/
/*!
\enum QRhiProfiler::StreamOp
Describes an entry in the profiler's output stream.
\value NewBuffer A buffer is created
\value ReleaseBuffer A buffer is destroyed
\value NewBufferStagingArea A staging buffer for buffer upload is created
\value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed
\value NewRenderBuffer A renderbuffer is created
\value ReleaseRenderBuffer A renderbuffer is destroyed
\value NewTexture A texture is created
\value ReleaseTexture A texture is destroyed
\value NewTextureStagingArea A staging buffer for texture upload is created
\value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed
\value ResizeSwapChain A swapchain is created or resized
\value ReleaseSwapChain A swapchain is destroyed
\value NewReadbackBuffer A staging buffer for readback is created
\value ReleaseReadbackBuffer A staging buffer for readback is destroyed
\value GpuMemAllocStats GPU memory allocator statistics
\value GpuFrameTime GPU frame times
\value FrameToFrameTime CPU frame-to-frame times
\value FrameBuildTime CPU beginFrame-endFrame times
*/
/*!
\class QRhiProfiler::CpuTime
\internal
\inmodule QtGui
\brief Contains CPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
and average values (in milliseconds) from various measurements are made
available in this struct queryable from QRhiProfiler::frameToFrameTimes()
and QRhiProfiler::frameBuildTimes().
\sa QRhiProfiler::setFrameTimingWriteInterval()
*/
/*!
\class QRhiProfiler::GpuTime
\internal
\inmodule QtGui
\brief Contains GPU-side frame timings.
Once sufficient number of frames have been rendered, the minimum, maximum,
and average values (in milliseconds) calculated from GPU command buffer
timestamps are made available in this struct queryable from
QRhiProfiler::gpuFrameTimes().
\sa QRhiProfiler::setFrameTimingWriteInterval()
*/
/*!
\internal
*/
QRhiProfiler::QRhiProfiler()
: d(new QRhiProfilerPrivate)
{
d->ts.start();
}
/*!
Destructor.
*/
QRhiProfiler::~QRhiProfiler()
{
// Flush because there is a high chance we have writes that were made since
// the event loop last ran. (esp. relevant for network devices like QTcpSocket)
if (d->outputDevice)
d->outputDevice->waitForBytesWritten(1000);
delete d;
}
/*!
Sets the output \a device.
\note No output will be generated when QRhi::EnableProfiling was not set.
*/
void QRhiProfiler::setDevice(QIODevice *device)
{
d->outputDevice = device;
}
/*!
Requests writing a GpuMemAllocStats entry into the output, when applicable.
Backends that do not support this will ignore the request. This is an
explicit request since getting the allocator status and statistics may be
an expensive operation.
*/
void QRhiProfiler::addVMemAllocatorStats()
{
if (d->rhiDWhenEnabled)
d->rhiDWhenEnabled->sendVMemStatsToProfiler();
}
/*!
\return the currently set frame timing writeout interval.
*/
int QRhiProfiler::frameTimingWriteInterval() const
{
return d->frameTimingWriteInterval;
}
/*!
Sets the number of frames that need to be rendered before the collected CPU
and GPU timings are processed (min, max, average are calculated) to \a
frameCount.
The default value is 120.
*/
void QRhiProfiler::setFrameTimingWriteInterval(int frameCount)
{
if (frameCount > 0)
d->frameTimingWriteInterval = frameCount;
}
/*!
\return min, max, and avg in milliseconds for the time that elapsed between two
QRhi::endFrame() calls.
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
*/
QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->frameToFrameTime;
return QRhiProfiler::CpuTime();
}
/*!
\return min, max, and avg in milliseconds for the time that elapsed between
a QRhi::beginFrame() and QRhi::endFrame().
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
*/
QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->beginToEndFrameTime;
return QRhiProfiler::CpuTime();
}
/*!
\return min, max, and avg in milliseconds for the GPU time that is spent on
one frame.
\note The values are all 0 until at least frameTimingWriteInterval() frames
have been rendered.
The GPU times should only be compared between runs on the same GPU of the
same system with the same backend. Comparing times for different graphics
cards or for different backends can give misleading results. The numbers are
not meant to be comparable that way.
\note Some backends have no support for this, and even for those that have,
it is not guaranteed that the driver will support it at run time. Support
can be checked via QRhi::Timestamps.
*/
QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
{
auto it = d->swapchains.constFind(sc);
if (it != d->swapchains.constEnd())
return it->gpuFrameTime;
return QRhiProfiler::GpuTime();
}
void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
{
buf.clear();
buf.append(QByteArray::number(op));
buf.append(',');
buf.append(QByteArray::number(timestamp));
buf.append(',');
buf.append(QByteArray::number(quint64(quintptr(res))));
buf.append(',');
if (res)
buf.append(res->name());
buf.append(',');
}
void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
{
Q_ASSERT(key[0] != 'F');
buf.append(key);
buf.append(',');
buf.append(QByteArray::number(v));
buf.append(',');
}
void QRhiProfilerPrivate::writeFloat(const char *key, float f)
{
Q_ASSERT(key[0] == 'F');
buf.append(key);
buf.append(',');
buf.append(QByteArray::number(double(f)));
buf.append(',');
}
void QRhiProfilerPrivate::endEntry()
{
buf.append('\n');
outputDevice->write(buf);
}
void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf);
writeInt("type", buf->type());
writeInt("usage", buf->usage());
writeInt("logical_size", buf->size());
writeInt("effective_size", realSize);
writeInt("backing_gpu_buf_count", backingGpuBufCount);
writeInt("backing_cpu_buf_count", backingCpuBufCount);
endEntry();
}
void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf);
endEntry();
}
void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf);
writeInt("slot", slot);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf);
writeInt("slot", slot);
endEntry();
}
void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount)
{
if (!outputDevice)
return;
const QRhiRenderBuffer::Type type = rb->type();
const QSize sz = rb->pixelSize();
// just make up something, ds is likely D24S8 while color is RGBA8 or similar
const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(assumedFormat, sz, 1, 1, 1);
if (sampleCount > 1)
byteSize *= uint(sampleCount);
startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb);
writeInt("type", type);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("effective_sample_count", sampleCount);
writeInt("transient_backing", transientBacking);
writeInt("winsys_backing", winSysBacking);
writeInt("approx_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb);
endEntry();
}
void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount)
{
if (!outputDevice)
return;
const QRhiTexture::Format format = tex->format();
const QSize sz = tex->pixelSize();
const int depth = tex->depth();
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, sz, depth, mipCount, layerCount);
if (sampleCount > 1)
byteSize *= uint(sampleCount);
startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("format", format);
writeInt("owns_native_resource", owns);
writeInt("mip_count", mipCount);
writeInt("layer_count", layerCount);
writeInt("effective_sample_count", sampleCount);
writeInt("approx_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex);
endEntry();
}
void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex);
writeInt("slot", slot);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex);
writeInt("slot", slot);
endEntry();
}
void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount)
{
if (!outputDevice)
return;
const QSize sz = sc->currentPixelSize();
quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(QRhiTexture::BGRA8, sz, 1, 1, 1);
byteSize = byteSize * uint(bufferCount) + byteSize * uint(msaaBufferCount) * uint(sampleCount);
startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
writeInt("width", sz.width());
writeInt("height", sz.height());
writeInt("buffer_count", bufferCount);
writeInt("msaa_buffer_count", msaaBufferCount);
writeInt("effective_sample_count", sampleCount);
writeInt("approx_total_byte_size", byteSize);
endEntry();
}
void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc);
endEntry();
}
template<typename T>
void calcTiming(QList<T> *vec, T *minDelta, T *maxDelta, float *avgDelta)
{
if (vec->isEmpty())
return;
*minDelta = *maxDelta = 0;
float totalDelta = 0;
for (T delta : qAsConst(*vec)) {
totalDelta += float(delta);
if (*minDelta == 0 || delta < *minDelta)
*minDelta = delta;
if (*maxDelta == 0 || delta > *maxDelta)
*maxDelta = delta;
}
*avgDelta = totalDelta / vec->count();
vec->clear();
}
void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
{
Sc &scd(swapchains[sc]);
scd.beginToEndTimer.start();
}
void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount)
{
Sc &scd(swapchains[sc]);
if (!scd.frameToFrameRunning) {
scd.frameToFrameTimer.start();
scd.frameToFrameRunning = true;
return;
}
scd.frameToFrameSamples.append(scd.frameToFrameTimer.restart());
if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.frameToFrameSamples,
&scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc);
writeInt("frames_since_resize", frameCount);
writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime);
writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime);
writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime);
endEntry();
}
}
scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed());
if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.beginToEndSamples,
&scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc);
writeInt("frames_since_resize", frameCount);
writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime);
writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime);
writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime);
endEntry();
}
}
}
void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
{
Sc &scd(swapchains[sc]);
scd.gpuFrameSamples.append(gpuTime);
if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
calcTiming(&scd.gpuFrameSamples,
&scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime);
if (outputDevice) {
startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc);
writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime);
writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime);
writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime);
endEntry();
}
}
}
void QRhiProfilerPrivate::newReadbackBuffer(qint64 id, QRhiResource *src, quint32 size)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src);
writeInt("id", id);
writeInt("size", size);
endEntry();
}
void QRhiProfilerPrivate::releaseReadbackBuffer(qint64 id)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr);
writeInt("id", id);
endEntry();
}
void QRhiProfilerPrivate::vmemStat(uint realAllocCount, uint subAllocCount, quint32 totalSize, quint32 unusedSize)
{
if (!outputDevice)
return;
startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr);
writeInt("real_alloc_count", realAllocCount);
writeInt("sub_alloc_count", subAllocCount);
writeInt("total_size", totalSize);
writeInt("unused_size", unusedSize);
endEntry();
}
QT_END_NAMESPACE

View File

@ -1,123 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIPROFILER_H
#define QRHIPROFILER_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qrhi_p.h>
QT_BEGIN_NAMESPACE
class QRhiProfilerPrivate;
class QIODevice;
class Q_GUI_EXPORT QRhiProfiler
{
public:
enum StreamOp {
NewBuffer = 1,
ReleaseBuffer,
NewBufferStagingArea,
ReleaseBufferStagingArea,
NewRenderBuffer,
ReleaseRenderBuffer,
NewTexture,
ReleaseTexture,
NewTextureStagingArea,
ReleaseTextureStagingArea,
ResizeSwapChain,
ReleaseSwapChain,
NewReadbackBuffer,
ReleaseReadbackBuffer,
GpuMemAllocStats,
GpuFrameTime,
FrameToFrameTime,
FrameBuildTime
};
~QRhiProfiler();
void setDevice(QIODevice *device);
void addVMemAllocatorStats();
int frameTimingWriteInterval() const;
void setFrameTimingWriteInterval(int frameCount);
struct CpuTime {
qint64 minTime = 0;
qint64 maxTime = 0;
float avgTime = 0;
};
struct GpuTime {
float minTime = 0;
float maxTime = 0;
float avgTime = 0;
};
CpuTime frameToFrameTimes(QRhiSwapChain *sc) const;
CpuTime frameBuildTimes(QRhiSwapChain *sc) const; // beginFrame - endFrame
GpuTime gpuFrameTimes(QRhiSwapChain *sc) const;
private:
Q_DISABLE_COPY(QRhiProfiler)
QRhiProfiler();
QRhiProfilerPrivate *d;
friend class QRhiImplementation;
friend class QRhiProfilerPrivate;
};
Q_DECLARE_TYPEINFO(QRhiProfiler::CpuTime, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(QRhiProfiler::GpuTime, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif

View File

@ -1,124 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Gui module
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRHIPROFILER_P_H
#define QRHIPROFILER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qrhiprofiler_p.h"
#include <QElapsedTimer>
#include <QHash>
QT_BEGIN_NAMESPACE
class QRhiProfilerPrivate
{
public:
static QRhiProfilerPrivate *get(QRhiProfiler *p) { return p->d; }
void newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount);
void releaseBuffer(QRhiBuffer *buf);
void newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size);
void releaseBufferStagingArea(QRhiBuffer *buf, int slot);
void newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount);
void releaseRenderBuffer(QRhiRenderBuffer *rb);
void newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount);
void releaseTexture(QRhiTexture *tex);
void newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size);
void releaseTextureStagingArea(QRhiTexture *tex, int slot);
void resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount);
void releaseSwapChain(QRhiSwapChain *sc);
void beginSwapChainFrame(QRhiSwapChain *sc);
void endSwapChainFrame(QRhiSwapChain *sc, int frameCount);
void swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTimeMs);
void newReadbackBuffer(qint64 id, QRhiResource *src, quint32 size);
void releaseReadbackBuffer(qint64 id);
void vmemStat(uint realAllocCount, uint subAllocCount, quint32 totalSize, quint32 unusedSize);
void startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res);
void writeInt(const char *key, qint64 v);
void writeFloat(const char *key, float f);
void endEntry();
QRhiImplementation *rhiDWhenEnabled = nullptr;
QIODevice *outputDevice = nullptr;
QElapsedTimer ts;
QByteArray buf;
static const int DEFAULT_FRAME_TIMING_WRITE_INTERVAL = 120; // frames
int frameTimingWriteInterval = DEFAULT_FRAME_TIMING_WRITE_INTERVAL;
struct Sc {
Sc() {
frameToFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
beginToEndSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
gpuFrameSamples.reserve(DEFAULT_FRAME_TIMING_WRITE_INTERVAL);
}
QElapsedTimer frameToFrameTimer;
bool frameToFrameRunning = false;
QElapsedTimer beginToEndTimer;
QList<qint64> frameToFrameSamples;
QList<qint64> beginToEndSamples;
QList<float> gpuFrameSamples;
QRhiProfiler::CpuTime frameToFrameTime;
QRhiProfiler::CpuTime beginToEndFrameTime;
QRhiProfiler::GpuTime gpuFrameTime;
};
QHash<QRhiSwapChain *, Sc> swapchains;
};
Q_DECLARE_TYPEINFO(QRhiProfilerPrivate::Sc, Q_RELOCATABLE_TYPE);
QT_END_NAMESPACE
#endif

View File

@ -1713,7 +1713,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
const int frameResIndex = swapChainD->bufferCount > 1 ? swapChainD->currentFrameSlot : 0;
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[frameResIndex]);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
if (!frame.imageAcquired) {
// Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
@ -1775,9 +1774,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
if (!qFuzzyIsNull(nsecsPerTick)) {
const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
// now we have the gpu time for the previous frame for this slot, report it
// (does not matter that it is not for this frame)
QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
runGpuFrameTimeCallbacks(elapsedMs);
}
} else {
qWarning("Failed to query timestamp: %d", err);
@ -1799,7 +1796,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
// when profiling is enabled, pick a free query (pair) from the pool
int timestampQueryIdx = -1;
if (profilerPrivateOrNull() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
if (hasGpuFrameTimeCallback() && swapChainD->bufferCount > 1) { // no timestamps if not having at least 2 frames in flight
for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
if (!timestampQueryPoolMap.testBit(i)) {
timestampQueryPoolMap.setBit(i);
@ -1821,8 +1818,6 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
swapChainD->rtWrapper.d.fb = image.fb;
QRHI_PROF_F(beginSwapChainFrame(swapChain));
prepareNewFrame(&swapChainD->cbWrapper);
return QRhi::FrameOpSuccess;
@ -1888,10 +1883,6 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
frame.imageSemWaitable = false;
frame.cmdFenceWaitable = true;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
// this must be done before the Present
QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
if (needsPresent) {
// add the Present to the queue
VkPresentInfoKHR presInfo;
@ -3068,7 +3059,6 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
{
QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int opIdx = 0; opIdx < ud->activeBufferOpCount; ++opIdx) {
const QRhiResourceUpdateBatchPrivate::BufferOp &u(ud->bufferOps[opIdx]);
@ -3103,7 +3093,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
&bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
if (err == VK_SUCCESS) {
bufD->stagingAllocations[currentFrameSlot] = allocation;
QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, quint32(bufD->m_size)));
} else {
qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err);
continue;
@ -3154,7 +3143,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
bufD->stagingAllocations[currentFrameSlot] = nullptr;
releaseQueue.append(e);
QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
}
} else if (u.type == QRhiResourceUpdateBatchPrivate::BufferOp::Read) {
QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
@ -3196,7 +3184,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf), bufD, uint(readback.byteSize)));
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
continue;
@ -3254,7 +3241,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
continue;
}
utexD->stagingAllocations[currentFrameSlot] = allocation;
QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, quint32(stagingSize)));
BufferImageCopyList copyInfos;
size_t curOfs = 0;
@ -3301,7 +3287,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
utexD->stagingAllocations[currentFrameSlot] = nullptr;
releaseQueue.append(e);
QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
// Similarly to buffers, transitioning away from DST is done later,
// when a renderpass using the texture is encountered.
@ -3411,9 +3396,6 @@ void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdat
VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &readback.stagingBuf, &allocation, nullptr);
if (err == VK_SUCCESS) {
readback.stagingAlloc = allocation;
QRHI_PROF_F(newReadbackBuffer(qint64(readback.stagingBuf),
texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
readback.byteSize));
} else {
qWarning("Failed to create readback buffer of size %u: %d", readback.byteSize, err);
continue;
@ -3688,7 +3670,6 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
void QRhiVulkan::finishActiveReadbacks(bool forced)
{
QVarLengthArray<std::function<void()>, 4> completedCallbacks;
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
for (int i = activeTextureReadbacks.count() - 1; i >= 0; --i) {
const QRhiVulkan::TextureReadback &readback(activeTextureReadbacks[i]);
@ -3707,7 +3688,6 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
}
vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@ -3731,7 +3711,6 @@ void QRhiVulkan::finishActiveReadbacks(bool forced)
}
vmaDestroyBuffer(toVmaAllocator(allocator), readback.stagingBuf, a);
QRHI_PROF_F(releaseReadbackBuffer(qint64(readback.stagingBuf)));
if (readback.result->completed)
completedCallbacks.append(readback.result->completed);
@ -4342,16 +4321,16 @@ QRhiDriverInfo QRhiVulkan::driverInfo() const
return driverInfoStruct;
}
void QRhiVulkan::sendVMemStatsToProfiler()
QRhiMemAllocStats QRhiVulkan::graphicsMemoryAllocationStatistics()
{
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
if (!rhiP)
return;
VmaStats stats;
vmaCalculateStats(toVmaAllocator(allocator), &stats);
QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
quint32(stats.total.usedBytes), quint32(stats.total.unusedBytes)));
return {
stats.total.blockCount,
stats.total.allocationCount,
stats.total.usedBytes,
stats.total.unusedBytes
};
}
bool QRhiVulkan::makeThreadLocalNativeContextCurrent()
@ -5557,8 +5536,6 @@ void QVkBuffer::destroy()
// a (leaked) resource after the QRhi.
if (rhiD) {
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -5620,9 +5597,6 @@ bool QVkBuffer::create()
return false;
}
QRHI_PROF;
QRHI_PROF_F(newBuffer(this, uint(nonZeroSize), m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0));
lastActiveFrameSlot = -1;
generation += 1;
rhiD->registerResource(this);
@ -5714,8 +5688,6 @@ void QVkRenderBuffer::destroy()
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseRenderBuffer(this));
rhiD->unregisterResource(this);
}
}
@ -5729,7 +5701,6 @@ bool QVkRenderBuffer::create()
return false;
QRHI_RES_RHI(QRhiVulkan);
QRHI_PROF;
samples = rhiD->effectiveSampleCount(m_sampleCount);
switch (m_type) {
@ -5750,7 +5721,6 @@ bool QVkRenderBuffer::create()
if (!backingTexture->create())
return false;
vkformat = backingTexture->vkformat;
QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
}
break;
case QRhiRenderBuffer::DepthStencil:
@ -5768,7 +5738,6 @@ bool QVkRenderBuffer::create()
return false;
}
rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
QRHI_PROF_F(newRenderBuffer(this, true, false, samples));
break;
default:
Q_UNREACHABLE();
@ -5839,8 +5808,6 @@ void QVkTexture::destroy()
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
rhiD->releaseQueue.append(e);
QRHI_PROF;
QRHI_PROF_F(releaseTexture(this));
rhiD->unregisterResource(this);
}
}
@ -6034,9 +6001,6 @@ bool QVkTexture::create()
rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
QRHI_PROF;
QRHI_PROF_F(newTexture(this, true, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples));
owns = true;
rhiD->registerResource(this);
return true;
@ -6056,12 +6020,6 @@ bool QVkTexture::createFrom(QRhiTexture::NativeTexture src)
if (!finishCreate())
return false;
const bool isCube = m_flags.testFlag(CubeMap);
const bool isArray = m_flags.testFlag(TextureArray);
QRHI_PROF;
QRHI_PROF_F(newTexture(this, false, int(mipLevelCount), isCube ? 6 : (isArray ? m_arraySize : 1), samples));
usageState.layout = VkImageLayout(src.layout);
owns = false;
@ -7174,11 +7132,8 @@ void QVkSwapChain::destroy()
surface = lastConnectedSurface = VK_NULL_HANDLE;
if (rhiD) {
QRHI_PROF;
QRHI_PROF_F(releaseSwapChain(this));
if (rhiD)
rhiD->unregisterResource(this);
}
}
QRhiCommandBuffer *QVkSwapChain::currentFrameCommandBuffer()
@ -7445,9 +7400,6 @@ bool QVkSwapChain::createOrResize()
frameCount = 0;
QRHI_PROF;
QRHI_PROF_F(resizeSwapChain(this, QVK_FRAMES_IN_FLIGHT, samples > VK_SAMPLE_COUNT_1_BIT ? QVK_FRAMES_IN_FLIGHT : 0, samples));
if (needsRegistration)
rhiD->registerResource(this);

View File

@ -766,7 +766,7 @@ public:
int resourceLimit(QRhi::ResourceLimit limit) const override;
const QRhiNativeHandles *nativeHandles() override;
QRhiDriverInfo driverInfo() const override;
void sendVMemStatsToProfiler() override;
QRhiMemAllocStats graphicsMemoryAllocationStatistics() override;
bool makeThreadLocalNativeContextCurrent() override;
void releaseCachedResources() override;
bool isDeviceLost() const override;

View File

@ -353,7 +353,6 @@ void tst_QRhi::create()
QVERIFY(uniBufRangeMax >= 224 * 4 * 4);
QVERIFY(rhi->nativeHandles());
QVERIFY(rhi->profiler());
const QRhi::Feature features[] = {
QRhi::MultisampleTexture,

View File

@ -23,6 +23,3 @@ add_subdirectory(instancing)
add_subdirectory(noninstanced)
add_subdirectory(tex3d)
add_subdirectory(texturearray)
if(QT_FEATURE_widgets)
add_subdirectory(qrhiprof)
endif()

View File

@ -66,7 +66,6 @@
#include <QtGui/private/qshader_p.h>
#include <QFile>
#include <QtGui/private/qrhiprofiler_p.h>
#ifndef QT_NO_OPENGL
#include <QtGui/private/qrhigles2_p.h>
@ -348,7 +347,7 @@ void Renderer::createRhi()
return;
qDebug() << "renderer" << this << "creating rhi";
QRhi::Flags rhiFlags = QRhi::EnableProfiling;
QRhi::Flags rhiFlags;
#ifndef QT_NO_OPENGL
if (graphicsApi == OpenGL) {
@ -625,26 +624,6 @@ void Renderer::render(bool newlyExposed, bool wakeBeforePresent)
r->endFrame(m_sc);
m_frameCount += 1;
if ((m_frameCount % 300) == 0) {
const QRhiProfiler::CpuTime ff = r->profiler()->frameToFrameTimes(m_sc);
const QRhiProfiler::CpuTime be = r->profiler()->frameBuildTimes(m_sc);
const QRhiProfiler::GpuTime gp = r->profiler()->gpuFrameTimes(m_sc);
if (r->isFeatureSupported(QRhi::Timestamps)) {
qDebug("[renderer %p] frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. "
"gpu frame time: min %f max %f avg %f",
this,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime,
gp.minTime, gp.maxTime, gp.avgTime);
} else {
qDebug("[renderer %p] frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. ",
this,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime);
}
}
}
void Renderer::sendInit()

View File

@ -1,19 +0,0 @@
# Generated from qrhiprof.pro.
#####################################################################
## qrhiprof Binary:
#####################################################################
qt_internal_add_manual_test(qrhiprof
GUI
SOURCES
qrhiprof.cpp
PUBLIC_LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Network
Qt::Widgets
)
#### Keys ignored in scope 1:.:.:qrhiprof.pro:<TRUE>:
# TEMPLATE = "app"

View File

@ -1,671 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
// Simple QRhiProfiler receiver app. Start it and then in a QRhi-based
// application connect with a QTcpSocket to 127.0.0.1:30667 and set that as the
// QRhiProfiler's device.
#include <QTcpServer>
#include <QTcpSocket>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QTextEdit>
#include <QLabel>
#include <QTime>
#include <QtGui/private/qrhiprofiler_p.h>
const int MIN_KNOWN_OP = 1;
const int MAX_KNOWN_OP = 18;
class Parser : public QObject
{
Q_OBJECT
public:
void feed(const QByteArray &line);
struct Event {
QRhiProfiler::StreamOp op;
qint64 timestamp;
quint64 resource;
QByteArray resourceName;
struct Param {
enum ValueType {
Int64,
Float
};
QByteArray key;
ValueType valueType;
union {
qint64 intValue;
float floatValue;
};
};
QList<Param> params;
const Param *param(const char *key) const {
auto it = std::find_if(params.cbegin(), params.cend(), [key](const Param &p) {
return !strcmp(p.key.constData(), key);
});
return it == params.cend() ? nullptr : &*it;
}
};
signals:
void eventReceived(const Event &e);
};
void Parser::feed(const QByteArray &line)
{
const QList<QByteArray> elems = line.split(',');
if (elems.count() < 4) {
qWarning("Malformed line '%s'", line.constData());
return;
}
bool ok = false;
const int op = elems[0].toInt(&ok);
if (!ok) {
qWarning("Invalid op %s", elems[0].constData());
return;
}
if (op < MIN_KNOWN_OP || op > MAX_KNOWN_OP) {
qWarning("Unknown op %d", op);
return;
}
Event e;
e.op = QRhiProfiler::StreamOp(op);
e.timestamp = elems[1].toLongLong();
e.resource = elems[2].toULongLong();
e.resourceName = elems[3];
const int elemCount = elems.count();
for (int i = 4; i < elemCount; i += 2) {
if (i + 1 < elemCount && !elems[i].isEmpty() && !elems[i + 1].isEmpty()) {
QByteArray key = elems[i];
if (key.startsWith('F')) {
key = key.mid(1);
bool ok = false;
const float value = elems[i + 1].toFloat(&ok);
if (!ok) {
qWarning("Failed to parse float %s in line '%s'", elems[i + 1].constData(), line.constData());
continue;
}
Event::Param param;
param.key = key;
param.valueType = Event::Param::Float;
param.floatValue = value;
e.params.append(param);
} else {
const qint64 value = elems[i + 1].toLongLong();
Event::Param param;
param.key = key;
param.valueType = Event::Param::Int64;
param.intValue = value;
e.params.append(param);
}
}
}
emit eventReceived(e);
}
class Tracker : public QObject
{
Q_OBJECT
public slots:
void handleEvent(const Parser::Event &e);
signals:
void buffersTouched();
void texturesTouched();
void swapchainsTouched();
void frameTimeTouched();
void gpuFrameTimeTouched();
void gpuMemAllocStatsTouched();
public:
Tracker() {
reset();
}
static const int MAX_STAGING_SLOTS = 3;
struct Buffer {
Buffer()
{
memset(stagingExtraSize, 0, sizeof(stagingExtraSize));
}
quint64 lastTimestamp;
QByteArray resourceName;
qint64 effectiveSize = 0;
int backingGpuBufCount = 1;
qint64 stagingExtraSize[MAX_STAGING_SLOTS];
};
QHash<qint64, Buffer> m_buffers;
qint64 m_totalBufferApproxByteSize;
qint64 m_peakBufferApproxByteSize;
qint64 m_totalStagingBufferApproxByteSize;
qint64 m_peakStagingBufferApproxByteSize;
struct Texture {
Texture()
{
memset(stagingExtraSize, 0, sizeof(stagingExtraSize));
}
quint64 lastTimestamp;
QByteArray resourceName;
qint64 approxByteSize = 0;
bool ownsNativeResource = true;
qint64 stagingExtraSize[MAX_STAGING_SLOTS];
};
QHash<qint64, Texture> m_textures;
qint64 m_totalTextureApproxByteSize;
qint64 m_peakTextureApproxByteSize;
qint64 m_totalTextureStagingBufferApproxByteSize;
qint64 m_peakTextureStagingBufferApproxByteSize;
struct SwapChain {
quint64 lastTimestamp;
QByteArray resourceName;
qint64 approxByteSize = 0;
};
QHash<qint64, SwapChain> m_swapchains;
qint64 m_totalSwapChainApproxByteSize;
qint64 m_peakSwapChainApproxByteSize;
struct FrameTime {
qint64 framesSinceResize = 0;
int minDelta = 0;
int maxDelta = 0;
float avgDelta = 0;
};
FrameTime m_lastFrameTime;
struct GpuFrameTime {
float minTime = 0;
float maxTime = 0;
float avgTime = 0;
};
GpuFrameTime m_lastGpuFrameTime;
struct GpuMemAllocStats {
qint64 realAllocCount;
qint64 subAllocCount;
qint64 totalSize;
qint64 unusedSize;
};
GpuMemAllocStats m_lastGpuMemAllocStats;
void reset() {
m_buffers.clear();
m_textures.clear();
m_totalBufferApproxByteSize = 0;
m_peakBufferApproxByteSize = 0;
m_totalStagingBufferApproxByteSize = 0;
m_peakStagingBufferApproxByteSize = 0;
m_totalTextureApproxByteSize = 0;
m_peakTextureApproxByteSize = 0;
m_totalTextureStagingBufferApproxByteSize = 0;
m_peakTextureStagingBufferApproxByteSize = 0;
m_totalSwapChainApproxByteSize = 0;
m_peakSwapChainApproxByteSize = 0;
m_lastFrameTime = FrameTime();
m_lastGpuFrameTime = GpuFrameTime();
m_lastGpuMemAllocStats = GpuMemAllocStats();
emit buffersTouched();
emit texturesTouched();
emit swapchainsTouched();
emit frameTimeTouched();
emit gpuFrameTimeTouched();
emit gpuMemAllocStatsTouched();
}
};
Q_DECLARE_TYPEINFO(Tracker::Buffer, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::Texture, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::FrameTime, Q_RELOCATABLE_TYPE);
Q_DECLARE_TYPEINFO(Tracker::GpuFrameTime, Q_RELOCATABLE_TYPE);
void Tracker::handleEvent(const Parser::Event &e)
{
switch (e.op) {
case QRhiProfiler::NewBuffer:
{
Buffer b;
b.lastTimestamp = e.timestamp;
b.resourceName = e.resourceName;
// type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("effective_size"))
b.effectiveSize = p.intValue;
else if (p.key == QByteArrayLiteral("backing_gpu_buf_count"))
b.backingGpuBufCount = p.intValue;
}
m_totalBufferApproxByteSize += b.effectiveSize * b.backingGpuBufCount;
m_peakBufferApproxByteSize = qMax(m_peakBufferApproxByteSize, m_totalBufferApproxByteSize);
m_buffers.insert(e.resource, b);
emit buffersTouched();
}
break;
case QRhiProfiler::ReleaseBuffer:
{
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
m_totalBufferApproxByteSize -= it->effectiveSize * it->backingGpuBufCount;
m_buffers.erase(it);
emit buffersTouched();
}
}
break;
case QRhiProfiler::NewBufferStagingArea:
{
qint64 slot = -1;
qint64 size = 0;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
else if (p.key == QByteArrayLiteral("size"))
size = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
it->stagingExtraSize[slot] = size;
m_totalStagingBufferApproxByteSize += size;
m_peakStagingBufferApproxByteSize = qMax(m_peakStagingBufferApproxByteSize, m_totalStagingBufferApproxByteSize);
emit buffersTouched();
}
}
}
break;
case QRhiProfiler::ReleaseBufferStagingArea:
{
qint64 slot = -1;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_buffers.find(e.resource);
if (it != m_buffers.end()) {
m_totalStagingBufferApproxByteSize -= it->stagingExtraSize[slot];
it->stagingExtraSize[slot] = 0;
emit buffersTouched();
}
}
}
break;
case QRhiProfiler::NewTexture:
{
Texture t;
t.lastTimestamp = e.timestamp;
t.resourceName = e.resourceName;
// width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("approx_byte_size"))
t.approxByteSize = p.intValue;
else if (p.key == QByteArrayLiteral("owns_native_resource"))
t.ownsNativeResource = p.intValue;
}
if (t.ownsNativeResource) {
m_totalTextureApproxByteSize += t.approxByteSize;
m_peakTextureApproxByteSize = qMax(m_peakTextureApproxByteSize, m_totalTextureApproxByteSize);
}
m_textures.insert(e.resource, t);
emit texturesTouched();
}
break;
case QRhiProfiler::ReleaseTexture:
{
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
if (it->ownsNativeResource)
m_totalTextureApproxByteSize -= it->approxByteSize;
m_textures.erase(it);
emit texturesTouched();
}
}
break;
case QRhiProfiler::NewTextureStagingArea:
{
qint64 slot = -1;
qint64 size = 0;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
else if (p.key == QByteArrayLiteral("size"))
size = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
it->stagingExtraSize[slot] = size;
m_totalTextureStagingBufferApproxByteSize += size;
m_peakTextureStagingBufferApproxByteSize = qMax(m_peakTextureStagingBufferApproxByteSize, m_totalTextureStagingBufferApproxByteSize);
emit texturesTouched();
}
}
}
break;
case QRhiProfiler::ReleaseTextureStagingArea:
{
qint64 slot = -1;
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("slot"))
slot = p.intValue;
}
if (slot >= 0 && slot < MAX_STAGING_SLOTS) {
auto it = m_textures.find(e.resource);
if (it != m_textures.end()) {
m_totalTextureStagingBufferApproxByteSize -= it->stagingExtraSize[slot];
it->stagingExtraSize[slot] = 0;
emit texturesTouched();
}
}
}
break;
case QRhiProfiler::ResizeSwapChain:
{
auto it = m_swapchains.find(e.resource);
if (it != m_swapchains.end())
m_totalSwapChainApproxByteSize -= it->approxByteSize;
SwapChain s;
s.lastTimestamp = e.timestamp;
s.resourceName = e.resourceName;
// width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("approx_total_byte_size"))
s.approxByteSize = p.intValue;
}
m_totalSwapChainApproxByteSize += s.approxByteSize;
m_peakSwapChainApproxByteSize = qMax(m_peakSwapChainApproxByteSize, m_totalSwapChainApproxByteSize);
m_swapchains.insert(e.resource, s);
emit swapchainsTouched();
}
break;
case QRhiProfiler::ReleaseSwapChain:
{
auto it = m_swapchains.find(e.resource);
if (it != m_swapchains.end()) {
m_totalSwapChainApproxByteSize -= it->approxByteSize;
m_swapchains.erase(it);
emit swapchainsTouched();
}
}
break;
case QRhiProfiler::GpuFrameTime:
{
// Fmin_ms_gpu_frame_time,0.15488,Fmax_ms_gpu_frame_time,0.494592,Favg_ms_gpu_frame_time,0.33462
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("min_ms_gpu_frame_time"))
m_lastGpuFrameTime.minTime = p.floatValue;
else if (p.key == QByteArrayLiteral("max_ms_gpu_frame_time"))
m_lastGpuFrameTime.maxTime = p.floatValue;
else if (p.key == QByteArrayLiteral("avg_ms_gpu_frame_time"))
m_lastGpuFrameTime.avgTime = p.floatValue;
}
emit gpuFrameTimeTouched();
}
break;
case QRhiProfiler::FrameToFrameTime:
{
// frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("frames_since_resize"))
m_lastFrameTime.framesSinceResize = p.intValue;
else if (p.key == QByteArrayLiteral("min_ms_frame_delta"))
m_lastFrameTime.minDelta = p.intValue;
else if (p.key == QByteArrayLiteral("max_ms_frame_delta"))
m_lastFrameTime.maxDelta = p.intValue;
else if (p.key == QByteArrayLiteral("avg_ms_frame_delta"))
m_lastFrameTime.avgDelta = p.floatValue;
}
emit frameTimeTouched();
}
break;
case QRhiProfiler::GpuMemAllocStats:
{
// real_alloc_count,2,sub_alloc_count,154,total_size,10752,unused_size,50320896
for (const Parser::Event::Param &p : e.params) {
if (p.key == QByteArrayLiteral("real_alloc_count"))
m_lastGpuMemAllocStats.realAllocCount = p.intValue;
else if (p.key == QByteArrayLiteral("sub_alloc_count"))
m_lastGpuMemAllocStats.subAllocCount = p.intValue;
else if (p.key == QByteArrayLiteral("total_size"))
m_lastGpuMemAllocStats.totalSize = p.intValue;
else if (p.key == QByteArrayLiteral("unused_size"))
m_lastGpuMemAllocStats.unusedSize = p.intValue;
}
emit gpuMemAllocStatsTouched();
}
break;
default:
break;
}
}
class Server : public QTcpServer
{
Q_OBJECT
protected:
void incomingConnection(qintptr socketDescriptor) override;
signals:
void clientConnected();
void clientDisconnected();
void receiveStarted();
void lineReceived(const QByteArray &line);
private:
bool m_valid = false;
QTcpSocket m_socket;
QByteArray m_buf;
};
void Server::incomingConnection(qintptr socketDescriptor)
{
if (m_valid)
return;
m_socket.setSocketDescriptor(socketDescriptor);
m_valid = true;
emit clientConnected();
connect(&m_socket, &QAbstractSocket::readyRead, this, [this] {
bool receiveStartedSent = false;
m_buf += m_socket.readAll();
while (m_buf.contains('\n')) {
const int lfpos = m_buf.indexOf('\n');
const QByteArray line = m_buf.left(lfpos).trimmed();
m_buf = m_buf.mid(lfpos + 1);
if (!receiveStartedSent) {
receiveStartedSent = true;
emit receiveStarted();
}
emit lineReceived(line);
}
});
connect(&m_socket, &QAbstractSocket::disconnected, this, [this] {
if (m_valid) {
m_valid = false;
emit clientDisconnected();
}
});
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
Tracker tracker;
Parser parser;
QObject::connect(&parser, &Parser::eventReceived, &tracker, &Tracker::handleEvent);
Server server;
if (!server.listen(QHostAddress::Any, 30667))
qFatal("Failed to start server: %s", qPrintable(server.errorString()));
QVBoxLayout *layout = new QVBoxLayout;
QLabel *infoLabel = new QLabel(QLatin1String("<i>Launch a Qt Quick application with QSG_RHI_PROFILE=1 and QSG_RHI_PROFILE_HOST set to the IP address.<br>"
"(resource memory usage reporting works best with the Vulkan backend)</i>"));
layout->addWidget(infoLabel);
QGroupBox *groupBox = new QGroupBox(QLatin1String("RHI statistics"));
QVBoxLayout *groupLayout = new QVBoxLayout;
QLabel *buffersLabel = new QLabel;
QObject::connect(&tracker, &Tracker::buffersTouched, buffersLabel, [buffersLabel, &tracker] {
const QString msg = QString::asprintf("%d buffers with ca. %lld bytes of current memory (sub)allocations (peak %lld) + %lld bytes of known staging buffers (peak %lld)",
int(tracker.m_buffers.count()),
tracker.m_totalBufferApproxByteSize, tracker.m_peakBufferApproxByteSize,
tracker.m_totalStagingBufferApproxByteSize, tracker.m_peakStagingBufferApproxByteSize);
buffersLabel->setText(msg);
});
groupLayout->addWidget(buffersLabel);
QLabel *texturesLabel = new QLabel;
QObject::connect(&tracker, &Tracker::texturesTouched, texturesLabel, [texturesLabel, &tracker] {
const QString msg = QString::asprintf("%d textures with ca. %lld bytes of current memory (sub)allocations (peak %lld) + %lld bytes of known staging buffers (peak %lld)",
int(tracker.m_textures.count()),
tracker.m_totalTextureApproxByteSize, tracker.m_peakTextureApproxByteSize,
tracker.m_totalTextureStagingBufferApproxByteSize, tracker.m_peakTextureStagingBufferApproxByteSize);
texturesLabel->setText(msg);
});
groupLayout->addWidget(texturesLabel);
QLabel *swapchainsLabel = new QLabel;
QObject::connect(&tracker, &Tracker::swapchainsTouched, swapchainsLabel, [swapchainsLabel, &tracker] {
const QString msg = QString::asprintf("Estimated total swapchain color buffer size is %lld bytes (peak %lld)",
tracker.m_totalSwapChainApproxByteSize, tracker.m_peakSwapChainApproxByteSize);
swapchainsLabel->setText(msg);
});
groupLayout->addWidget(swapchainsLabel);
QLabel *frameTimeLabel = new QLabel;
QObject::connect(&tracker, &Tracker::frameTimeTouched, frameTimeLabel, [frameTimeLabel, &tracker] {
const QString msg = QString::asprintf("Frames since resize %lld Frame delta min %d ms max %d ms avg %f ms",
tracker.m_lastFrameTime.framesSinceResize,
tracker.m_lastFrameTime.minDelta,
tracker.m_lastFrameTime.maxDelta,
tracker.m_lastFrameTime.avgDelta);
frameTimeLabel->setText(msg);
});
groupLayout->addWidget(frameTimeLabel);
QLabel *gpuFrameTimeLabel = new QLabel;
QObject::connect(&tracker, &Tracker::gpuFrameTimeTouched, gpuFrameTimeLabel, [gpuFrameTimeLabel, &tracker] {
const QString msg = QString::asprintf("GPU frame time min %f ms max %f ms avg %f ms",
tracker.m_lastGpuFrameTime.minTime,
tracker.m_lastGpuFrameTime.maxTime,
tracker.m_lastGpuFrameTime.avgTime);
gpuFrameTimeLabel->setText(msg);
});
groupLayout->addWidget(gpuFrameTimeLabel);
QLabel *gpuMemAllocStatsLabel = new QLabel;
QObject::connect(&tracker, &Tracker::gpuMemAllocStatsTouched, gpuMemAllocStatsLabel, [gpuMemAllocStatsLabel, &tracker] {
const QString msg = QString::asprintf("GPU memory allocator status: %lld real allocations %lld sub-allocations %lld total bytes %lld unused bytes",
tracker.m_lastGpuMemAllocStats.realAllocCount,
tracker.m_lastGpuMemAllocStats.subAllocCount,
tracker.m_lastGpuMemAllocStats.totalSize,
tracker.m_lastGpuMemAllocStats.unusedSize);
gpuMemAllocStatsLabel->setText(msg);
});
groupLayout->addWidget(gpuMemAllocStatsLabel);
groupBox->setLayout(groupLayout);
layout->addWidget(groupBox);
QTextEdit *rawLog = new QTextEdit;
rawLog->setReadOnly(true);
layout->addWidget(rawLog);
QObject::connect(&server, &Server::clientConnected, rawLog, [rawLog] {
rawLog->append(QLatin1String("\nCONNECTED\n"));
});
QObject::connect(&server, &Server::clientDisconnected, rawLog, [rawLog, &tracker] {
rawLog->append(QLatin1String("\nDISCONNECTED\n"));
tracker.reset();
});
QObject::connect(&server, &Server::receiveStarted, rawLog, [rawLog] {
rawLog->setFontItalic(true);
rawLog->append(QLatin1String("[") + QTime::currentTime().toString() + QLatin1String("]"));
rawLog->setFontItalic(false);
});
QObject::connect(&server, &Server::lineReceived, rawLog, [rawLog, &parser](const QByteArray &line) {
rawLog->append(QString::fromUtf8(line));
parser.feed(line);
});
QWidget w;
w.resize(800, 600);
w.setLayout(layout);
w.show();
return app.exec();
}
#include "qrhiprof.moc"

View File

@ -1,6 +0,0 @@
TEMPLATE = app
QT += network widgets gui-private
SOURCES = \
qrhiprof.cpp

View File

@ -63,7 +63,6 @@
#include <QtGui/private/qshader_p.h>
#include <QFile>
#include <QtGui/private/qrhiprofiler_p.h>
#include <QtGui/private/qrhinull_p.h>
#ifndef QT_NO_OPENGL
@ -401,31 +400,7 @@ void Window::render()
m_frameCount += 1;
if (m_timer.elapsed() > 1000) {
if (rhiFlags.testFlag(QRhi::EnableProfiling)) {
const QRhiProfiler::CpuTime ff = m_r->profiler()->frameToFrameTimes(m_sc);
const QRhiProfiler::CpuTime be = m_r->profiler()->frameBuildTimes(m_sc);
const QRhiProfiler::GpuTime gp = m_r->profiler()->gpuFrameTimes(m_sc);
if (m_r->isFeatureSupported(QRhi::Timestamps)) {
qDebug("ca. %d fps. "
"frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. "
"gpu frame time: min %f max %f avg %f",
m_frameCount,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime,
gp.minTime, gp.maxTime, gp.avgTime);
} else {
qDebug("ca. %d fps. "
"frame-to-frame: min %lld max %lld avg %f. "
"frame build: min %lld max %lld avg %f. ",
m_frameCount,
ff.minTime, ff.maxTime, ff.avgTime,
be.minTime, be.maxTime, be.avgTime);
}
} else {
qDebug("ca. %d fps", m_frameCount);
}
qDebug("ca. %d fps", m_frameCount);
m_timer.restart();
m_frameCount = 0;
}

View File

@ -62,9 +62,8 @@
#include <QFileInfo>
#include <QFile>
#include <QtGui/private/qrhiprofiler_p.h>
#include <QElapsedTimer>
#define PROFILE_TO_FILE
//#define SKIP_PRESENT
//#define USE_MSAA
//#define USE_SRGB_SWAPCHAIN
@ -83,18 +82,12 @@ struct {
QSize lastOutputSize;
int frameCount = 0;
QFile profOut;
QVarLengthArray<float, 64> gpuFrameTimes;
QElapsedTimer gpuFrameTimePrintTimer;
} d;
void preInit()
{
#ifdef PROFILE_TO_FILE
rhiFlags |= QRhi::EnableProfiling;
const QString profFn = QFileInfo(QLatin1String("rhiprof.txt")).absoluteFilePath();
qDebug("Writing profiling output to %s", qPrintable(profFn));
d.profOut.setFileName(profFn);
d.profOut.open(QIODevice::WriteOnly);
#endif
#ifdef SKIP_PRESENT
endFrameFlags |= QRhi::SkipPresent;
#endif
@ -129,10 +122,6 @@ void preInit()
void Window::customInit()
{
#ifdef PROFILE_TO_FILE
m_r->profiler()->setDevice(&d.profOut);
#endif
d.triRenderer.setRhi(m_r);
d.triRenderer.setSampleCount(sampleCount);
d.triRenderer.initResources(m_rp);
@ -159,10 +148,6 @@ void Window::customInit()
d.liveTexCubeRenderer.setTranslation(QVector3D(-2.0f, 0, 0));
}
// Put the gpu mem allocator statistics to the profiling stream after doing
// all the init. (where applicable)
m_r->profiler()->addVMemAllocatorStats();
// Check some features/limits.
qDebug("isFeatureSupported(MultisampleTexture): %d", m_r->isFeatureSupported(QRhi::MultisampleTexture));
qDebug("isFeatureSupported(MultisampleRenderBuffer): %d", m_r->isFeatureSupported(QRhi::MultisampleRenderBuffer));
@ -192,6 +177,24 @@ void Window::customInit()
qDebug("MaxThreadGroupZ: %d", m_r->resourceLimit(QRhi::MaxThreadGroupZ));
qDebug("TextureArraySizeMax: %d", m_r->resourceLimit(QRhi::TextureArraySizeMax));
qDebug("MaxUniformBufferRange: %d", m_r->resourceLimit(QRhi::MaxUniformBufferRange));
// With Vulkan at least we should see some details from the memory allocator.
qDebug() << m_r->graphicsMemoryAllocationStatistics();
// Every two seconds try printing an average of the gpu frame times.
d.gpuFrameTimePrintTimer.start();
m_r->addGpuFrameTimeCallback([](float elapsedMs) {
d.gpuFrameTimes.append(elapsedMs);
if (d.gpuFrameTimePrintTimer.elapsed() > 2000) {
float at = 0.0f;
for (float t : d.gpuFrameTimes)
at += t;
at /= d.gpuFrameTimes.count();
qDebug() << "Average GPU frame time" << at;
d.gpuFrameTimes.clear();
d.gpuFrameTimePrintTimer.restart();
}
});
}
void Window::customRelease()