rhi: gl: Implement lastCompletedGpuTime()
Only for OpenGL 3.3+ (not ES). Change-Id: I016ba934d7f5038b3d56f8d0c62e72b3921d86c6 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
72a453c6a8
commit
4580003341
@ -702,9 +702,12 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
|
||||
QRhiCommandBuffer::debugMarkBegin()) are supported.
|
||||
|
||||
\value Timestamps Indicates that command buffer timestamps are supported.
|
||||
Relevant for QRhiCommandBuffer::lastCompletedGpuTime(). Can be expected to
|
||||
be supported on Metal, Vulkan, and Direct 3D, assuming the underlying
|
||||
implementation supports timestamp queries or similar.
|
||||
Relevant for QRhiCommandBuffer::lastCompletedGpuTime(). This can be
|
||||
expected to be supported on Metal, Vulkan, Direct 3D 11 and 12, and OpenGL
|
||||
contexts of version 3.3 or newer. However, with some of these APIs support
|
||||
for timestamp queries is technically optional, and therefore it cannot be
|
||||
guaranteed that this feature is always supported with every implementation
|
||||
of them.
|
||||
|
||||
\value Instancing Indicates that instanced drawing is supported. In
|
||||
practice this feature will be unsupported with OpenGL ES 2.0 and OpenGL
|
||||
|
@ -492,21 +492,33 @@ QT_BEGIN_NAMESPACE
|
||||
#endif
|
||||
|
||||
#ifndef GL_TEXTURE_1D
|
||||
# define GL_TEXTURE_1D 0x0DE0
|
||||
# define GL_TEXTURE_1D 0x0DE0
|
||||
#endif
|
||||
|
||||
#ifndef GL_TEXTURE_1D_ARRAY
|
||||
# define GL_TEXTURE_1D_ARRAY 0x8C18
|
||||
# define GL_TEXTURE_1D_ARRAY 0x8C18
|
||||
#endif
|
||||
|
||||
#ifndef GL_HALF_FLOAT
|
||||
#define GL_HALF_FLOAT 0x140B
|
||||
#define GL_HALF_FLOAT 0x140B
|
||||
#endif
|
||||
|
||||
#ifndef GL_MAX_VERTEX_OUTPUT_COMPONENTS
|
||||
#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122
|
||||
#endif
|
||||
|
||||
#ifndef GL_TIMESTAMP
|
||||
#define GL_TIMESTAMP 0x8E28
|
||||
#endif
|
||||
|
||||
#ifndef GL_QUERY_RESULT
|
||||
#define GL_QUERY_RESULT 0x8866
|
||||
#endif
|
||||
|
||||
#ifndef GL_QUERY_RESULT_AVAILABLE
|
||||
#define GL_QUERY_RESULT_AVAILABLE 0x8867
|
||||
#endif
|
||||
|
||||
/*!
|
||||
Constructs a new QRhiGles2InitParams.
|
||||
|
||||
@ -1026,6 +1038,17 @@ bool QRhiGles2::create(QRhi::Flags flags)
|
||||
ctx->getProcAddress(QByteArrayLiteral("glFramebufferTextureMultiviewOVR")));
|
||||
}
|
||||
|
||||
// Only do timestamp queries on OpenGL 3.3+.
|
||||
caps.timestamps = !caps.gles && (caps.ctxMajor > 3 || (caps.ctxMajor == 3 && caps.ctxMinor >= 3));
|
||||
if (caps.timestamps) {
|
||||
glQueryCounter = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum)>(
|
||||
ctx->getProcAddress(QByteArrayLiteral("glQueryCounter")));
|
||||
glGetQueryObjectui64v = reinterpret_cast<void(QOPENGLF_APIENTRYP)(GLuint, GLenum, quint64 *)>(
|
||||
ctx->getProcAddress(QByteArrayLiteral("glGetQueryObjectui64v")));
|
||||
if (!glQueryCounter || !glGetQueryObjectui64v)
|
||||
caps.timestamps = false;
|
||||
}
|
||||
|
||||
nativeHandlesStruct.context = ctx;
|
||||
|
||||
contextLost = false;
|
||||
@ -1041,6 +1064,11 @@ void QRhiGles2::destroy()
|
||||
ensureContext();
|
||||
executeDeferredReleases();
|
||||
|
||||
if (ofr.tsQueries[0]) {
|
||||
f->glDeleteQueries(2, ofr.tsQueries);
|
||||
ofr.tsQueries[0] = ofr.tsQueries[1] = 0;
|
||||
}
|
||||
|
||||
if (vao) {
|
||||
f->glDeleteVertexArrays(1, &vao);
|
||||
vao = 0;
|
||||
@ -1315,7 +1343,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
|
||||
case QRhi::DebugMarkers:
|
||||
return false;
|
||||
case QRhi::Timestamps:
|
||||
return false;
|
||||
return caps.timestamps;
|
||||
case QRhi::Instancing:
|
||||
return caps.instancing;
|
||||
case QRhi::CustomInstanceStepRate:
|
||||
@ -1964,10 +1992,14 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type)
|
||||
static inline void addBoundaryCommand(QGles2CommandBuffer *cbD, QGles2CommandBuffer::Command::Cmd type, GLuint tsQuery = 0)
|
||||
{
|
||||
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
|
||||
cmd.cmd = type;
|
||||
if (type == QGles2CommandBuffer::Command::BeginFrame)
|
||||
cmd.args.beginFrame.timestampQuery = tsQuery;
|
||||
else if (type == QGles2CommandBuffer::Command::EndFrame)
|
||||
cmd.args.endFrame.timestampQuery = tsQuery;
|
||||
}
|
||||
|
||||
void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
|
||||
@ -2029,8 +2061,8 @@ void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
|
||||
|
||||
double QRhiGles2::lastCompletedGpuTime(QRhiCommandBuffer *cb)
|
||||
{
|
||||
Q_UNUSED(cb);
|
||||
return 0;
|
||||
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
|
||||
return cbD->lastGpuTime;
|
||||
}
|
||||
|
||||
QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags)
|
||||
@ -2046,7 +2078,17 @@ QRhi::FrameOpResult QRhiGles2::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
|
||||
executeDeferredReleases();
|
||||
swapChainD->cb.resetState();
|
||||
|
||||
addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame);
|
||||
if (swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex]) {
|
||||
double elapsedSec = 0;
|
||||
if (swapChainD->timestamps.tryQueryTimestamps(swapChainD->currentTimestampPairIndex, this, &elapsedSec))
|
||||
swapChainD->cb.lastGpuTime = elapsedSec;
|
||||
}
|
||||
|
||||
GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
|
||||
GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
|
||||
const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
|
||||
|
||||
addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::BeginFrame, recordTimestamps ? tsStart : 0);
|
||||
|
||||
return QRhi::FrameOpSuccess;
|
||||
}
|
||||
@ -2056,7 +2098,15 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
|
||||
QGles2SwapChain *swapChainD = QRHI_RES(QGles2SwapChain, swapChain);
|
||||
Q_ASSERT(currentSwapChain == swapChainD);
|
||||
|
||||
addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame);
|
||||
GLuint tsStart = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2];
|
||||
GLuint tsEnd = swapChainD->timestamps.query[swapChainD->currentTimestampPairIndex * 2 + 1];
|
||||
const bool recordTimestamps = tsStart && tsEnd && !swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex];
|
||||
if (recordTimestamps) {
|
||||
swapChainD->timestamps.active[swapChainD->currentTimestampPairIndex] = true;
|
||||
swapChainD->currentTimestampPairIndex = (swapChainD->currentTimestampPairIndex + 1) % QGles2SwapChainTimestamps::TIMESTAMP_PAIRS;
|
||||
}
|
||||
|
||||
addBoundaryCommand(&swapChainD->cb, QGles2CommandBuffer::Command::EndFrame, recordTimestamps ? tsEnd : 0);
|
||||
|
||||
if (!ensureContext(swapChainD->surface))
|
||||
return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
|
||||
@ -2088,7 +2138,12 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi:
|
||||
executeDeferredReleases();
|
||||
ofr.cbWrapper.resetState();
|
||||
|
||||
addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame);
|
||||
if (rhiFlags.testFlag(QRhi::EnableTimestamps) && caps.timestamps) {
|
||||
if (!ofr.tsQueries[0])
|
||||
f->glGenQueries(2, ofr.tsQueries);
|
||||
}
|
||||
|
||||
addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::BeginFrame, ofr.tsQueries[0]);
|
||||
*cb = &ofr.cbWrapper;
|
||||
|
||||
return QRhi::FrameOpSuccess;
|
||||
@ -2100,7 +2155,7 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
|
||||
Q_ASSERT(ofr.active);
|
||||
ofr.active = false;
|
||||
|
||||
addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame);
|
||||
addBoundaryCommand(&ofr.cbWrapper, QGles2CommandBuffer::Command::EndFrame, ofr.tsQueries[1]);
|
||||
|
||||
if (!ensureContext())
|
||||
return contextLost ? QRhi::FrameOpDeviceLost : QRhi::FrameOpError;
|
||||
@ -2113,6 +2168,16 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
|
||||
// another, sharing context.
|
||||
f->glFlush();
|
||||
|
||||
if (ofr.tsQueries[0]) {
|
||||
quint64 timestamps[2];
|
||||
glGetQueryObjectui64v(ofr.tsQueries[1], GL_QUERY_RESULT, ×tamps[1]);
|
||||
glGetQueryObjectui64v(ofr.tsQueries[0], GL_QUERY_RESULT, ×tamps[0]);
|
||||
if (timestamps[1] >= timestamps[0]) {
|
||||
const quint64 nanoseconds = timestamps[1] - timestamps[0];
|
||||
ofr.cbWrapper.lastGpuTime = nanoseconds / 1000000000.0; // seconds
|
||||
}
|
||||
}
|
||||
|
||||
return QRhi::FrameOpSuccess;
|
||||
}
|
||||
|
||||
@ -2867,6 +2932,8 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
||||
const QGles2CommandBuffer::Command &cmd(*it);
|
||||
switch (cmd.cmd) {
|
||||
case QGles2CommandBuffer::Command::BeginFrame:
|
||||
if (cmd.args.beginFrame.timestampQuery)
|
||||
glQueryCounter(cmd.args.beginFrame.timestampQuery, GL_TIMESTAMP);
|
||||
if (caps.coreProfile) {
|
||||
if (!vao)
|
||||
f->glGenVertexArrays(1, &vao);
|
||||
@ -2893,6 +2960,8 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
|
||||
#endif
|
||||
if (vao)
|
||||
f->glBindVertexArray(0);
|
||||
if (cmd.args.endFrame.timestampQuery)
|
||||
glQueryCounter(cmd.args.endFrame.timestampQuery, GL_TIMESTAMP);
|
||||
break;
|
||||
case QGles2CommandBuffer::Command::ResetFrame:
|
||||
if (vao)
|
||||
@ -6210,15 +6279,56 @@ bool QGles2SwapChain::createOrResize()
|
||||
|
||||
frameCount = 0;
|
||||
|
||||
QRHI_RES_RHI(QRhiGles2);
|
||||
if (rhiD->rhiFlags.testFlag(QRhi::EnableTimestamps) && rhiD->caps.timestamps)
|
||||
timestamps.prepare(rhiD);
|
||||
|
||||
// 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().
|
||||
if (needsRegistration) {
|
||||
QRHI_RES_RHI(QRhiGles2);
|
||||
if (needsRegistration)
|
||||
rhiD->registerResource(this, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QGles2SwapChainTimestamps::prepare(QRhiGles2 *rhiD)
|
||||
{
|
||||
rhiD->f->glGenQueries(TIMESTAMP_PAIRS * 2, query);
|
||||
}
|
||||
|
||||
void QGles2SwapChainTimestamps::destroy(QRhiGles2 *rhiD)
|
||||
{
|
||||
rhiD->f->glDeleteQueries(TIMESTAMP_PAIRS * 2, query);
|
||||
}
|
||||
|
||||
bool QGles2SwapChainTimestamps::tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec)
|
||||
{
|
||||
if (!active[pairIndex])
|
||||
return false;
|
||||
|
||||
GLuint tsStart = query[pairIndex * 2];
|
||||
GLuint tsEnd = query[pairIndex * 2 + 1];
|
||||
|
||||
GLuint ready = GL_FALSE;
|
||||
rhiD->f->glGetQueryObjectuiv(tsEnd, GL_QUERY_RESULT_AVAILABLE, &ready);
|
||||
|
||||
if (!ready)
|
||||
return false;
|
||||
|
||||
bool result = false;
|
||||
quint64 timestamps[2];
|
||||
rhiD->glGetQueryObjectui64v(tsStart, GL_QUERY_RESULT, ×tamps[0]);
|
||||
rhiD->glGetQueryObjectui64v(tsEnd, GL_QUERY_RESULT, ×tamps[1]);
|
||||
|
||||
if (timestamps[1] >= timestamps[0]) {
|
||||
const quint64 nanoseconds = timestamps[1] - timestamps[0];
|
||||
*elapsedSec = nanoseconds / 1000000000.0;
|
||||
result = true;
|
||||
}
|
||||
|
||||
active[pairIndex] = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -27,6 +27,7 @@
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QOpenGLExtensions;
|
||||
class QRhiGles2;
|
||||
|
||||
struct QGles2Buffer : public QRhiBuffer
|
||||
{
|
||||
@ -343,6 +344,12 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
||||
// QRhi*/QGles2* references should be kept at minimum (so no
|
||||
// QRhiTexture/Buffer/etc. pointers).
|
||||
union Args {
|
||||
struct {
|
||||
GLuint timestampQuery;
|
||||
} beginFrame;
|
||||
struct {
|
||||
GLuint timestampQuery;
|
||||
} endFrame;
|
||||
struct {
|
||||
float x, y, w, h;
|
||||
float d0, d1;
|
||||
@ -544,6 +551,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
||||
|
||||
PassType recordingPass;
|
||||
bool passNeedsResourceTracking;
|
||||
double lastGpuTime = 0;
|
||||
QRhiRenderTarget *currentTarget;
|
||||
QRhiGraphicsPipeline *currentGraphicsPipeline;
|
||||
QRhiComputePipeline *currentComputePipeline;
|
||||
@ -639,6 +647,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
|
||||
void resetState() {
|
||||
recordingPass = NoPass;
|
||||
passNeedsResourceTracking = true;
|
||||
// do not zero lastGpuTime
|
||||
currentTarget = nullptr;
|
||||
resetCommands();
|
||||
resetCachedState();
|
||||
@ -700,6 +709,18 @@ inline bool operator!=(const QGles2CommandBuffer::GraphicsPassState::Blend &a,
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
struct QGles2SwapChainTimestamps
|
||||
{
|
||||
static const int TIMESTAMP_PAIRS = 2;
|
||||
|
||||
bool active[TIMESTAMP_PAIRS] = {};
|
||||
GLuint query[TIMESTAMP_PAIRS * 2] = {};
|
||||
|
||||
void prepare(QRhiGles2 *rhiD);
|
||||
void destroy(QRhiGles2 *rhiD);
|
||||
bool tryQueryTimestamps(int pairIndex, QRhiGles2 *rhiD, double *elapsedSec);
|
||||
};
|
||||
|
||||
struct QGles2SwapChain : public QRhiSwapChain
|
||||
{
|
||||
QGles2SwapChain(QRhiImplementation *rhi);
|
||||
@ -725,6 +746,8 @@ struct QGles2SwapChain : public QRhiSwapChain
|
||||
QGles2SwapChainRenderTarget rtRight;
|
||||
QGles2CommandBuffer cb;
|
||||
int frameCount = 0;
|
||||
QGles2SwapChainTimestamps timestamps;
|
||||
int currentTimestampPairIndex = 0;
|
||||
};
|
||||
|
||||
class QRhiGles2 : public QRhiImplementation
|
||||
@ -924,6 +947,8 @@ public:
|
||||
GLint) = nullptr;
|
||||
void(QOPENGLF_APIENTRYP glFramebufferTextureMultiviewOVR)(GLenum, GLenum, GLuint, GLint,
|
||||
GLint, GLsizei) = nullptr;
|
||||
void (QOPENGLF_APIENTRYP glQueryCounter)(GLuint, GLenum) = nullptr;
|
||||
void (QOPENGLF_APIENTRYP glGetQueryObjectui64v)(GLuint, GLenum, quint64 *) = nullptr;
|
||||
|
||||
uint vao = 0;
|
||||
struct Caps {
|
||||
@ -978,7 +1003,8 @@ public:
|
||||
texture1D(false),
|
||||
hasDrawBuffersFunc(false),
|
||||
halfAttributes(false),
|
||||
multiView(false)
|
||||
multiView(false),
|
||||
timestamps(false)
|
||||
{ }
|
||||
int ctxMajor;
|
||||
int ctxMinor;
|
||||
@ -1033,6 +1059,7 @@ public:
|
||||
uint hasDrawBuffersFunc : 1;
|
||||
uint halfAttributes : 1;
|
||||
uint multiView : 1;
|
||||
uint timestamps : 1;
|
||||
} caps;
|
||||
QGles2SwapChain *currentSwapChain = nullptr;
|
||||
QSet<GLint> supportedCompressedFormats;
|
||||
@ -1075,6 +1102,7 @@ public:
|
||||
OffscreenFrame(QRhiImplementation *rhi) : cbWrapper(rhi) { }
|
||||
bool active = false;
|
||||
QGles2CommandBuffer cbWrapper;
|
||||
GLuint tsQueries[2] = {};
|
||||
} ofr;
|
||||
|
||||
QHash<QRhiShaderStage, uint> m_shaderCache;
|
||||
|
Loading…
Reference in New Issue
Block a user