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:
parent
dcc2704d17
commit
23f8d6c57f
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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().
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -23,6 +23,3 @@ add_subdirectory(instancing)
|
||||
add_subdirectory(noninstanced)
|
||||
add_subdirectory(tex3d)
|
||||
add_subdirectory(texturearray)
|
||||
if(QT_FEATURE_widgets)
|
||||
add_subdirectory(qrhiprof)
|
||||
endif()
|
||||
|
@ -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()
|
||||
|
@ -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"
|
@ -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"
|
@ -1,6 +0,0 @@
|
||||
TEMPLATE = app
|
||||
|
||||
QT += network widgets gui-private
|
||||
|
||||
SOURCES = \
|
||||
qrhiprof.cpp
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user