Merge "Merge remote-tracking branch 'origin/5.14' into dev"

This commit is contained in:
Qt Forward Merge Bot 2019-08-29 21:24:07 +02:00
commit 0d6e555778
26 changed files with 701 additions and 268 deletions

View File

@ -50,10 +50,14 @@ QMAKE_CFLAGS_USE_PRECOMPILE = -include ${QMAKE_PCH_OUTPUT_BASE}
QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT} QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE
NDK_LLVM_PATH = $$NDK_ROOT/toolchains/llvm/prebuilt/$$NDK_HOST NDK_LLVM_PATH = $$NDK_ROOT/toolchains/llvm/prebuilt/$$NDK_HOST
QMAKE_CC = $$NDK_LLVM_PATH/bin/clang QMAKE_CC = $$NDK_LLVM_PATH/bin/clang
QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++ QMAKE_CXX = $$NDK_LLVM_PATH/bin/clang++
QMAKE_LINK = $$QMAKE_CXX QMAKE_LINK = $$QMAKE_CXX
QMAKE_OBJCOPY = $$NDK_LLVM_PATH/bin/llvm-objcopy
QMAKE_AR = $$NDK_LLVM_PATH/bin/llvm-ar cqs
QMAKE_OBJCOPY = $$NDK_LLVM_PATH/bin/llvm-objcopy
QMAKE_NM = $$NDK_LLVM_PATH/bin/llvm-nm -P
QMAKE_CFLAGS_OPTIMIZE = -Oz QMAKE_CFLAGS_OPTIMIZE = -Oz
QMAKE_CFLAGS_OPTIMIZE_FULL = -Oz QMAKE_CFLAGS_OPTIMIZE_FULL = -Oz

View File

@ -61,14 +61,15 @@ QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD
QMAKE_LIBS_EGL = -lEGL QMAKE_LIBS_EGL = -lEGL
QMAKE_LIBS_OPENGL_ES2 = -lGLESv2 QMAKE_LIBS_OPENGL_ES2 = -lGLESv2
# modifications to linux.conf
QMAKE_AR = $${CROSS_COMPILE}ar cqs
QMAKE_OBJCOPY = $${CROSS_COMPILE}objcopy
QMAKE_NM = $${CROSS_COMPILE}nm -P
QMAKE_STRIP = QMAKE_STRIP =
#$${CROSS_COMPILE}strip #$${CROSS_COMPILE}strip
equals(QT_ARCH, x86): CROSS_COMPILE = $$NDK_LLVM_PATH/bin/i686-linux-android-
else: equals(QT_ARCH, x86_64): CROSS_COMPILE = $$NDK_LLVM_PATH/bin/x86_64-linux-android-
else: equals(QT_ARCH, arm64-v8a): CROSS_COMPILE = $$NDK_LLVM_PATH/bin/aarch64-linux-android-
else: CROSS_COMPILE = $$NDK_LLVM_PATH/bin/arm-linux-androideabi-
QMAKE_RANLIB = $${CROSS_COMPILE}ranlib QMAKE_RANLIB = $${CROSS_COMPILE}ranlib
QMAKE_LINK_SHLIB = $$QMAKE_LINK QMAKE_LINK_SHLIB = $$QMAKE_LINK
QMAKE_LFLAGS = QMAKE_LFLAGS =

View File

@ -1,6 +1,6 @@
load(resolve_config) load(resolve_config)
!host_build:!single_arch:!java:android { !equals(TEMPLATE, aux):!host_build:!single_arch:!java:android {
isEmpty(ANDROID_ABIS): ANDROID_ABIS = $$ALL_ANDROID_ABIS isEmpty(ANDROID_ABIS): ANDROID_ABIS = $$ALL_ANDROID_ABIS
ALL_ABIS = $$join(ANDROID_ABIS, _and_) ALL_ABIS = $$join(ANDROID_ABIS, _and_)

View File

@ -100,7 +100,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QEventDispatcherWin32Private::QEventDispatcherWin32Private() QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0),
wakeUps(0), activateNotifiersPosted(false), getMessageHook(0), wakeUps(0), activateNotifiersPosted(false),
winEventNotifierActivatedEvent(NULL) winEventNotifierActivatedEvent(NULL)
{ {
} }
@ -245,9 +245,6 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
case WM_QT_SENDPOSTEDEVENTS: case WM_QT_SENDPOSTEDEVENTS:
Q_ASSERT(d != 0); Q_ASSERT(d != 0);
// Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0);
// We send posted events manually, if the window procedure was invoked // We send posted events manually, if the window procedure was invoked
// by the foreign event loop (e.g. from the native modal dialog). // by the foreign event loop (e.g. from the native modal dialog).
q->sendPostedEvents(); q->sendPostedEvents();
@ -257,9 +254,9 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
return DefWindowProc(hwnd, message, wp, lp); return DefWindowProc(hwnd, message, wp, lp);
} }
static inline UINT inputTimerMask() static inline UINT inputQueueMask()
{ {
UINT result = QS_TIMER | QS_INPUT | QS_RAWINPUT; UINT result = QS_ALLEVENTS;
// QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of
// QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8. // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8.
#if WINVER > 0x0601 #if WINVER > 0x0601
@ -269,6 +266,25 @@ static inline UINT inputTimerMask()
return result; return result;
} }
LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
{
QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
Q_ASSERT(q != 0);
QEventDispatcherWin32Private *d = q->d_func();
MSG *msg = reinterpret_cast<MSG *>(lp);
static const UINT mask = inputQueueMask();
if (HIWORD(GetQueueStatus(mask)) == 0 && wp == PM_REMOVE) {
// Allow posting WM_QT_SENDPOSTEDEVENTS message.
d->wakeUps.storeRelaxed(0);
if (!(msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS)) {
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
WMWP_QT_TOFOREIGNLOOP, 0);
}
}
return d->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0;
}
// Provide class name and atom for the message window used by // Provide class name and atom for the message window used by
// QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads. // QEventDispatcherWin32Private via Q_GLOBAL_STATIC shared between threads.
struct QWindowsMessageWindowClassContext struct QWindowsMessageWindowClassContext
@ -447,6 +463,14 @@ void QEventDispatcherWin32::createInternalHwnd()
return; return;
d->internalHwnd = qt_create_internal_window(this); d->internalHwnd = qt_create_internal_window(this);
// setup GetMessage hook needed to drive our posted events
d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
if (Q_UNLIKELY(!d->getMessageHook)) {
int errorCode = GetLastError();
qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls",
errorCode, qUtf16Printable(qt_error_string(errorCode)));
}
// start all normal timers // start all normal timers
for (int i = 0; i < d->timerVec.count(); ++i) for (int i = 0; i < d->timerVec.count(); ++i)
d->registerTimer(d->timerVec.at(i)); d->registerTimer(d->timerVec.at(i));
@ -499,7 +523,6 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
bool canWait; bool canWait;
bool retVal = false; bool retVal = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do { do {
DWORD waitRet = 0; DWORD waitRet = 0;
DWORD nCount = 0; DWORD nCount = 0;
@ -549,11 +572,8 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
if (haveMessage) { if (haveMessage) {
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
// Set result to 'true', if the message was sent by wakeUp(). // Set result to 'true', if the message was sent by wakeUp().
if (msg.wParam == WMWP_QT_FROMWAKEUP) { if (msg.wParam == WMWP_QT_FROMWAKEUP)
d->wakeUps.storeRelaxed(0);
retVal = true; retVal = true;
}
needWM_QT_SENDPOSTEDEVENTS = true;
continue; continue;
} }
if (msg.message == WM_TIMER) { if (msg.message == WM_TIMER) {
@ -573,22 +593,10 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
} }
if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
// Post WM_QT_SENDPOSTEDEVENTS before calling external code,
// as it can start a foreign event loop.
if (needWM_QT_SENDPOSTEDEVENTS) {
needWM_QT_SENDPOSTEDEVENTS = false;
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
WMWP_QT_TOFOREIGNLOOP, 0);
}
TranslateMessage(&msg); TranslateMessage(&msg);
DispatchMessage(&msg); DispatchMessage(&msg);
} }
} else if (waitRet - WAIT_OBJECT_0 < nCount) { } else if (waitRet - WAIT_OBJECT_0 < nCount) {
if (needWM_QT_SENDPOSTEDEVENTS) {
needWM_QT_SENDPOSTEDEVENTS = false;
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
WMWP_QT_TOFOREIGNLOOP, 0);
}
activateEventNotifiers(); activateEventNotifiers();
} else { } else {
// nothing todo so break // nothing todo so break
@ -606,21 +614,12 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake(); emit awake();
if (waitRet - WAIT_OBJECT_0 < nCount) { if (waitRet - WAIT_OBJECT_0 < nCount) {
if (needWM_QT_SENDPOSTEDEVENTS) {
needWM_QT_SENDPOSTEDEVENTS = false;
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
WMWP_QT_TOFOREIGNLOOP, 0);
}
activateEventNotifiers(); activateEventNotifiers();
retVal = true; retVal = true;
} }
} }
} while (canWait); } while (canWait);
if (needWM_QT_SENDPOSTEDEVENTS)
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
WMWP_QT_TOFOREIGNLOOP, 0);
return retVal; return retVal;
} }
@ -1004,6 +1003,10 @@ void QEventDispatcherWin32::closingDown()
d->timerDict.clear(); d->timerDict.clear();
d->closingDown = true; d->closingDown = true;
if (d->getMessageHook)
UnhookWindowsHookEx(d->getMessageHook);
d->getMessageHook = 0;
} }
bool QEventDispatcherWin32::event(QEvent *e) bool QEventDispatcherWin32::event(QEvent *e)

View File

@ -113,6 +113,7 @@ protected:
private: private:
friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
friend LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int, WPARAM, LPARAM);
}; };
struct QSockNot { struct QSockNot {
@ -166,6 +167,7 @@ public:
// internal window handle used for socketnotifiers/timers/etc // internal window handle used for socketnotifiers/timers/etc
HWND internalHwnd; HWND internalHwnd;
HHOOK getMessageHook;
// for controlling when to send posted events // for controlling when to send posted events
QAtomicInt wakeUps; QAtomicInt wakeUps;

View File

@ -275,6 +275,8 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
QRhi does not expose APIs for resource barriers or image layout QRhi does not expose APIs for resource barriers or image layout
transitions. Such synchronization is done implicitly by the backends, where transitions. Such synchronization is done implicitly by the backends, where
applicable (for example, Vulkan), by tracking resource usage as necessary. applicable (for example, Vulkan), by tracking resource usage as necessary.
Buffer and image barriers are inserted before render or compute passes
transparently to the application.
\note Resources within a render or compute pass are expected to be bound to \note Resources within a render or compute pass are expected to be bound to
a single usage during that pass. For example, a buffer can be used as a single usage during that pass. For example, a buffer can be used as
@ -554,6 +556,11 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
/*! /*!
\enum QRhi::BeginFrameFlag \enum QRhi::BeginFrameFlag
Flag values for QRhi::beginFrame() Flag values for QRhi::beginFrame()
\value ExternalContentsInPass Specifies that one or more render or compute
passes in this frame will call QRhiCommandBuffer::beginExternal(). Some
backends, Vulkan in particular, will fail if this flag is not set and
beginExternal() is still called.
*/ */
/*! /*!
@ -2246,6 +2253,10 @@ QRhiResource::Type QRhiSampler::resourceType() const
\internal \internal
\inmodule QtGui \inmodule QtGui
\brief Render pass resource. \brief Render pass resource.
A render pass, if such a concept exists in the underlying graphics API, is
a collection of attachments (color, depth, stencil) and describes how those
attachments are used.
*/ */
/*! /*!
@ -2264,6 +2275,18 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
return RenderPassDescriptor; return RenderPassDescriptor;
} }
/*!
\return a pointer to a backend-specific QRhiNativeHandles subclass, such as
QRhiVulkanRenderPassNativeHandles. The returned value is null when exposing
the underlying native resources is not supported by the backend.
\sa QRhiVulkanRenderPassNativeHandles
*/
const QRhiNativeHandles *QRhiRenderPassDescriptor::nativeHandles()
{
return nullptr;
}
/*! /*!
\class QRhiRenderTarget \class QRhiRenderTarget
\internal \internal
@ -4785,6 +4808,11 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
enqueue commands to the current pass' command buffer by calling graphics enqueue commands to the current pass' command buffer by calling graphics
API functions directly. API functions directly.
\note This is only available when the intent was declared up front in
beginFrame(). Therefore this function must only be called when the frame
was started with specifying QRhi::ExternalContentsInPass in the flags
passed to QRhi::beginFrame().
With Vulkan or Metal one can query the native command buffer or encoder With Vulkan or Metal one can query the native command buffer or encoder
objects via nativeHandles() and enqueue commands to them. With OpenGL or objects via nativeHandles() and enqueue commands to them. With OpenGL or
Direct3D 11 the (device) context can be retrieved from Direct3D 11 the (device) context can be retrieved from
@ -4802,6 +4830,16 @@ const QRhiNativeHandles *QRhiCommandBuffer::nativeHandles()
functions (\c set* or \c draw*) must be called on the functions (\c set* or \c draw*) must be called on the
QRhiCommandBuffer until endExternal(). QRhiCommandBuffer until endExternal().
\warning Some backends may return a native command buffer object from
QRhiCommandBuffer::nativeHandles() that is different from the primary one
when inside a beginExternal() - endExternal() block. Therefore it is
important to (re)query the native command buffer object after calling
beginExternal(). In practical terms this means that with Vulkan for example
the externally recorded Vulkan commands are placed onto a secondary command
buffer (with VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT).
nativeHandles() returns this secondary command buffer when called between
begin/endExternal.
\sa endExternal(), nativeHandles() \sa endExternal(), nativeHandles()
*/ */
void QRhiCommandBuffer::beginExternal() void QRhiCommandBuffer::beginExternal()
@ -5108,6 +5146,13 @@ QRhiSwapChain *QRhi::newSwapChain()
/*! /*!
Starts a new frame targeting the next available buffer of \a swapChain. Starts a new frame targeting the next available buffer of \a swapChain.
A frame consists of resource updates and one or more render and compute
passes.
\a flags can indicate certain special cases. For example, the fact that
QRhiCommandBuffer::beginExternal() will be called within this new frame
must be declared up front by setting the ExternalContentsInPass flag.
The high level pattern of rendering into a QWindow using a swapchain: The high level pattern of rendering into a QWindow using a swapchain:
\list \list
@ -5135,9 +5180,7 @@ QRhiSwapChain *QRhi::newSwapChain()
\endlist \endlist
\a flags is currently unused. \sa endFrame(), beginOffscreenFrame()
\sa endFrame()
*/ */
QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags) QRhi::FrameOpResult QRhi::beginFrame(QRhiSwapChain *swapChain, BeginFrameFlags flags)
{ {
@ -5237,7 +5280,8 @@ int QRhi::currentFrameSlot() const
/*! /*!
Starts a new offscreen frame. Provides a command buffer suitable for Starts a new offscreen frame. Provides a command buffer suitable for
recording rendering commands in \a cb. recording rendering commands in \a cb. \a flags is used to indicate
certain special cases, just like with beginFrame().
\note The QRhiCommandBuffer stored to *cb is not owned by the caller. \note The QRhiCommandBuffer stored to *cb is not owned by the caller.
@ -5271,14 +5315,14 @@ int QRhi::currentFrameSlot() const
// image data available in rbResult // image data available in rbResult
\endcode \endcode
\sa endOffscreenFrame() \sa endOffscreenFrame(), beginFrame()
*/ */
QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags)
{ {
if (d->inFrame) if (d->inFrame)
qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored"); qWarning("Attempted to call beginOffscreenFrame() within a still active frame; ignored");
QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb) : FrameOpSuccess; QRhi::FrameOpResult r = !d->inFrame ? d->beginOffscreenFrame(cb, flags) : FrameOpSuccess;
if (r == FrameOpSuccess) if (r == FrameOpSuccess)
d->inFrame = true; d->inFrame = true;
@ -5290,12 +5334,12 @@ QRhi::FrameOpResult QRhi::beginOffscreenFrame(QRhiCommandBuffer **cb)
\sa beginOffscreenFrame() \sa beginOffscreenFrame()
*/ */
QRhi::FrameOpResult QRhi::endOffscreenFrame() QRhi::FrameOpResult QRhi::endOffscreenFrame(EndFrameFlags flags)
{ {
if (!d->inFrame) if (!d->inFrame)
qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored"); qWarning("Attempted to call endOffscreenFrame() without an active frame; ignored");
QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame() : FrameOpSuccess; QRhi::FrameOpResult r = d->inFrame ? d->endOffscreenFrame(flags) : FrameOpSuccess;
d->inFrame = false; d->inFrame = false;
qDeleteAll(d->pendingReleaseAndDestroyResources); qDeleteAll(d->pendingReleaseAndDestroyResources);
d->pendingReleaseAndDestroyResources.clear(); d->pendingReleaseAndDestroyResources.clear();

View File

@ -843,6 +843,8 @@ class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
public: public:
QRhiResource::Type resourceType() const override; QRhiResource::Type resourceType() const override;
virtual const QRhiNativeHandles *nativeHandles();
protected: protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi); QRhiRenderPassDescriptor(QRhiImplementation *rhi);
}; };
@ -1324,6 +1326,7 @@ public:
}; };
enum BeginFrameFlag { enum BeginFrameFlag {
ExternalContentsInPass = 0x01
}; };
Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag) Q_DECLARE_FLAGS(BeginFrameFlags, BeginFrameFlag)
@ -1384,8 +1387,8 @@ public:
bool isRecordingFrame() const; bool isRecordingFrame() const;
int currentFrameSlot() const; int currentFrameSlot() const;
FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb); FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, BeginFrameFlags flags = BeginFrameFlags());
FrameOpResult endOffscreenFrame(); FrameOpResult endOffscreenFrame(EndFrameFlags flags = EndFrameFlags());
QRhi::FrameOpResult finish(); QRhi::FrameOpResult finish();

View File

@ -95,8 +95,8 @@ public:
virtual QRhiSwapChain *createSwapChain() = 0; virtual QRhiSwapChain *createSwapChain() = 0;
virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0; virtual QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) = 0;
virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0; virtual QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) = 0;
virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) = 0; virtual QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) = 0;
virtual QRhi::FrameOpResult endOffscreenFrame() = 0; virtual QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) = 0;
virtual QRhi::FrameOpResult finish() = 0; virtual QRhi::FrameOpResult finish() = 0;
virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0; virtual void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) = 0;

View File

@ -873,8 +873,10 @@ const QRhiNativeHandles *QRhiD3D11::nativeHandles(QRhiCommandBuffer *cb)
void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb) void QRhiD3D11::beginExternal(QRhiCommandBuffer *cb)
{ {
Q_UNUSED(cb); QD3D11CommandBuffer *cbD = QRHI_RES(QD3D11CommandBuffer, cb);
flushCommandBuffer(); // no timestampSwapChain, in order to avoid timestamp mess
executeCommandBuffer(cbD);
cbD->resetCommands();
} }
void QRhiD3D11::endExternal(QRhiCommandBuffer *cb) void QRhiD3D11::endExternal(QRhiCommandBuffer *cb)
@ -989,8 +991,9 @@ QRhi::FrameOpResult QRhiD3D11::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{ {
Q_UNUSED(flags);
ofr.active = true; ofr.active = true;
ofr.cbWrapper.resetState(); ofr.cbWrapper.resetState();
@ -999,8 +1002,9 @@ QRhi::FrameOpResult QRhiD3D11::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame() QRhi::FrameOpResult QRhiD3D11::endOffscreenFrame(QRhi::EndFrameFlags flags)
{ {
Q_UNUSED(flags);
ofr.active = false; ofr.active = false;
executeCommandBuffer(&ofr.cbWrapper); executeCommandBuffer(&ofr.cbWrapper);
@ -1135,27 +1139,25 @@ static inline bool isDepthTextureFormat(QRhiTexture::Format format)
QRhi::FrameOpResult QRhiD3D11::finish() QRhi::FrameOpResult QRhiD3D11::finish()
{ {
if (inFrame) if (inFrame) {
flushCommandBuffer(); if (ofr.active) {
Q_ASSERT(!contextState.currentSwapChain);
Q_ASSERT(ofr.cbWrapper.recordingPass == QD3D11CommandBuffer::NoPass);
executeCommandBuffer(&ofr.cbWrapper);
ofr.cbWrapper.resetCommands();
} else {
Q_ASSERT(contextState.currentSwapChain);
Q_ASSERT(contextState.currentSwapChain->cb.recordingPass == QD3D11CommandBuffer::NoPass);
executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
contextState.currentSwapChain->cb.resetCommands();
}
}
finishActiveReadbacks(); finishActiveReadbacks();
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
void QRhiD3D11::flushCommandBuffer()
{
if (ofr.active) {
Q_ASSERT(!contextState.currentSwapChain);
executeCommandBuffer(&ofr.cbWrapper);
ofr.cbWrapper.resetCommands();
} else {
Q_ASSERT(contextState.currentSwapChain);
executeCommandBuffer(&contextState.currentSwapChain->cb); // no timestampSwapChain, in order to avoid timestamp mess
contextState.currentSwapChain->cb.resetCommands();
}
}
void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, void QRhiD3D11::enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc) int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc)
{ {

View File

@ -473,9 +473,9 @@ struct QD3D11CommandBuffer : public QRhiCommandBuffer
imageRetainPool.clear(); imageRetainPool.clear();
} }
void resetState() { void resetState() {
resetCommands();
recordingPass = NoPass; recordingPass = NoPass;
currentTarget = nullptr; currentTarget = nullptr;
resetCommands();
resetCachedState(); resetCachedState();
} }
void resetCachedState() { void resetCachedState() {
@ -569,8 +569,8 @@ public:
QRhiSwapChain *createSwapChain() override; QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@ -633,7 +633,6 @@ public:
void sendVMemStatsToProfiler() override; void sendVMemStatsToProfiler() override;
void makeThreadLocalNativeContextCurrent() override; void makeThreadLocalNativeContextCurrent() override;
void flushCommandBuffer();
void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD, void enqueueSubresUpload(QD3D11Texture *texD, QD3D11CommandBuffer *cbD,
int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc); int layer, int level, const QRhiTextureSubresourceUploadDescription &subresDesc);
void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates); void enqueueResourceUpdates(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates);

View File

@ -1094,15 +1094,36 @@ const QRhiNativeHandles *QRhiGles2::nativeHandles(QRhiCommandBuffer *cb)
void QRhiGles2::beginExternal(QRhiCommandBuffer *cb) void QRhiGles2::beginExternal(QRhiCommandBuffer *cb)
{ {
Q_UNUSED(cb); if (ofr.active) {
flushCommandBuffer(); // also ensures the context is current Q_ASSERT(!currentSwapChain);
if (!ensureContext())
return;
} else {
Q_ASSERT(currentSwapChain);
if (!ensureContext(currentSwapChain->surface))
return;
}
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
executeCommandBuffer(cbD);
cbD->resetCommands();
} }
void QRhiGles2::endExternal(QRhiCommandBuffer *cb) void QRhiGles2::endExternal(QRhiCommandBuffer *cb)
{ {
QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb); QGles2CommandBuffer *cbD = QRHI_RES(QGles2CommandBuffer, cb);
Q_ASSERT(cbD->commands.isEmpty()); Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
cbD->resetCachedState(); cbD->resetCachedState();
if (cbD->recordingPass != QGles2CommandBuffer::NoPass) {
// Commands that come after this point need a resource tracker and also
// a BarriersForPass command enqueued. (the ones we had from
// beginPass() are now gone since beginExternal() processed all that
// due to calling executeCommandBuffer()).
enqueueBarriersForPass(cbD);
}
if (cbD->currentTarget) if (cbD->currentTarget)
enqueueBindFramebuffer(cbD->currentTarget, cbD); enqueueBindFramebuffer(cbD->currentTarget, cbD);
} }
@ -1163,8 +1184,9 @@ QRhi::FrameOpResult QRhiGles2::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{ {
Q_UNUSED(flags);
if (!ensureContext()) if (!ensureContext())
return QRhi::FrameOpError; return QRhi::FrameOpError;
@ -1179,8 +1201,9 @@ QRhi::FrameOpResult QRhiGles2::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiGles2::endOffscreenFrame() QRhi::FrameOpResult QRhiGles2::endOffscreenFrame(QRhi::EndFrameFlags flags)
{ {
Q_UNUSED(flags);
Q_ASSERT(ofr.active); Q_ASSERT(ofr.active);
ofr.active = false; ofr.active = false;
@ -1196,23 +1219,22 @@ QRhi::FrameOpResult QRhiGles2::endOffscreenFrame()
QRhi::FrameOpResult QRhiGles2::finish() QRhi::FrameOpResult QRhiGles2::finish()
{ {
return inFrame ? flushCommandBuffer() : QRhi::FrameOpSuccess; if (inFrame) {
} if (ofr.active) {
Q_ASSERT(!currentSwapChain);
QRhi::FrameOpResult QRhiGles2::flushCommandBuffer() Q_ASSERT(ofr.cbWrapper.recordingPass == QGles2CommandBuffer::NoPass);
{ if (!ensureContext())
if (ofr.active) { return QRhi::FrameOpError;
Q_ASSERT(!currentSwapChain); executeCommandBuffer(&ofr.cbWrapper);
if (!ensureContext()) ofr.cbWrapper.resetCommands();
return QRhi::FrameOpError; } else {
executeCommandBuffer(&ofr.cbWrapper); Q_ASSERT(currentSwapChain);
ofr.cbWrapper.resetCommands(); Q_ASSERT(currentSwapChain->cb.recordingPass == QGles2CommandBuffer::NoPass);
} else { if (!ensureContext(currentSwapChain->surface))
Q_ASSERT(currentSwapChain); return QRhi::FrameOpError;
if (!ensureContext(currentSwapChain->surface)) executeCommandBuffer(&currentSwapChain->cb);
return QRhi::FrameOpError; currentSwapChain->cb.resetCommands();
executeCommandBuffer(&currentSwapChain->cb); }
currentSwapChain->cb.resetCommands();
} }
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
@ -2483,6 +2505,16 @@ QGles2RenderTargetData *QRhiGles2::enqueueBindFramebuffer(QRhiRenderTarget *rt,
return rtD; return rtD;
} }
void QRhiGles2::enqueueBarriersForPass(QGles2CommandBuffer *cbD)
{
cbD->passResTrackers.append(QRhiPassResourceTracker());
cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
cbD->commands.append(cmd);
}
void QRhiGles2::beginPass(QRhiCommandBuffer *cb, void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt, QRhiRenderTarget *rt,
const QColor &colorClearValue, const QColor &colorClearValue,
@ -2497,12 +2529,7 @@ void QRhiGles2::beginPass(QRhiCommandBuffer *cb,
// Get a new resource tracker. Then add a command that will generate // Get a new resource tracker. Then add a command that will generate
// glMemoryBarrier() calls based on that tracker when submitted. // glMemoryBarrier() calls based on that tracker when submitted.
cbD->passResTrackers.append(QRhiPassResourceTracker()); enqueueBarriersForPass(cbD);
cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
cbD->commands.append(cmd);
bool wantsColorClear, wantsDsClear; bool wantsColorClear, wantsDsClear;
QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear); QGles2RenderTargetData *rtD = enqueueBindFramebuffer(rt, cbD, &wantsColorClear, &wantsDsClear);
@ -2578,12 +2605,7 @@ void QRhiGles2::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
if (resourceUpdates) if (resourceUpdates)
enqueueResourceUpdates(cb, resourceUpdates); enqueueResourceUpdates(cb, resourceUpdates);
cbD->passResTrackers.append(QRhiPassResourceTracker()); enqueueBarriersForPass(cbD);
cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
QGles2CommandBuffer::Command cmd;
cmd.cmd = QGles2CommandBuffer::Command::BarriersForPass;
cmd.args.barriersForPass.trackerIndex = cbD->currentPassResTrackerIndex;
cbD->commands.append(cmd);
cbD->recordingPass = QGles2CommandBuffer::ComputePass; cbD->recordingPass = QGles2CommandBuffer::ComputePass;

View File

@ -525,19 +525,16 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
} }
void resetCommands() { void resetCommands() {
commands.clear(); commands.clear();
// beginExternal() can lead to calling resetCommands() inside a pass,
// hence the condition
if (recordingPass == NoPass) {
passResTrackers.clear();
currentPassResTrackerIndex = -1;
}
dataRetainPool.clear(); dataRetainPool.clear();
imageRetainPool.clear(); imageRetainPool.clear();
passResTrackers.clear();
currentPassResTrackerIndex = -1;
} }
void resetState() { void resetState() {
resetCommands();
recordingPass = NoPass; recordingPass = NoPass;
currentTarget = nullptr; currentTarget = nullptr;
resetCommands();
resetCachedState(); resetCachedState();
} }
void resetCachedState() { void resetCachedState() {
@ -605,8 +602,8 @@ public:
QRhiSwapChain *createSwapChain() override; QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@ -671,7 +668,6 @@ public:
bool ensureContext(QSurface *surface = nullptr) const; bool ensureContext(QSurface *surface = nullptr) const;
void executeDeferredReleases(); void executeDeferredReleases();
QRhi::FrameOpResult flushCommandBuffer();
void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access); void trackedBufferBarrier(QGles2CommandBuffer *cbD, QGles2Buffer *bufD, QGles2Buffer::Access access);
void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access); void trackedImageBarrier(QGles2CommandBuffer *cbD, QGles2Texture *texD, QGles2Texture::Access access);
void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD, void enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cbD,
@ -692,6 +688,7 @@ public:
const uint *dynOfsPairs, int dynOfsCount); const uint *dynOfsPairs, int dynOfsCount);
QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD, QGles2RenderTargetData *enqueueBindFramebuffer(QRhiRenderTarget *rt, QGles2CommandBuffer *cbD,
bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr); bool *wantsColorClear = nullptr, bool *wantsDsClear = nullptr);
void enqueueBarriersForPass(QGles2CommandBuffer *cbD);
int effectiveSampleCount(int sampleCount) const; int effectiveSampleCount(int sampleCount) const;
bool compileShader(GLuint program, const QRhiShaderStage &shaderStage, bool compileShader(GLuint program, const QRhiShaderStage &shaderStage,
QShaderDescription *desc, int *glslVersionUsed); QShaderDescription *desc, int *glslVersionUsed);

View File

@ -1243,8 +1243,10 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{ {
Q_UNUSED(flags);
currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT; currentFrameSlot = (currentFrameSlot + 1) % QMTL_FRAMES_IN_FLIGHT;
if (swapchains.count() > 1) { if (swapchains.count() > 1) {
for (QMetalSwapChain *sc : qAsConst(swapchains)) { for (QMetalSwapChain *sc : qAsConst(swapchains)) {
@ -1268,8 +1270,9 @@ QRhi::FrameOpResult QRhiMetal::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiMetal::endOffscreenFrame() QRhi::FrameOpResult QRhiMetal::endOffscreenFrame(QRhi::EndFrameFlags flags)
{ {
Q_UNUSED(flags);
Q_ASSERT(d->ofr.active); Q_ASSERT(d->ofr.active);
d->ofr.active = false; d->ofr.active = false;

View File

@ -354,8 +354,8 @@ public:
QRhiSwapChain *createSwapChain() override; QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;

View File

@ -356,14 +356,16 @@ QRhi::FrameOpResult QRhiNull::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameF
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiNull::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{ {
Q_UNUSED(flags);
*cb = &offscreenCommandBuffer; *cb = &offscreenCommandBuffer;
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiNull::endOffscreenFrame() QRhi::FrameOpResult QRhiNull::endOffscreenFrame(QRhi::EndFrameFlags flags)
{ {
Q_UNUSED(flags);
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }

View File

@ -220,8 +220,8 @@ public:
QRhiSwapChain *createSwapChain() override; QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;

View File

@ -58,6 +58,28 @@ QT_BEGIN_NAMESPACE
and a separate, host visible staging buffer is used to upload data to them. and a separate, host visible staging buffer is used to upload data to them.
"Dynamic" buffers are in host visible memory and are duplicated (since there "Dynamic" buffers are in host visible memory and are duplicated (since there
can be 2 frames in flight). This is handled transparently to the application. can be 2 frames in flight). This is handled transparently to the application.
Barriers are generated automatically for each render or compute pass, based
on the resources that are used in that pass (in QRhiShaderResourceBindings,
vertex inputs, etc.). This implies deferring the recording of the command
buffer since the barriers have to be placed at the right place (before the
pass), and that can only be done once we know all the things the pass does.
This in turn has implications for integrating external commands
(beginExternal() - direct Vulkan calls - endExternal()) because that is
incompatible with this approach by nature. Therefore we support another mode
of operation, where each render or compute pass uses one or more secondary
command buffers (recorded right away), with each beginExternal() leading to
closing the current secondary cb, creating a new secondary cb for the
external content, and then starting yet another one in endExternal() for
whatever comes afterwards in the pass. This way the primary command buffer
only has vkCmdExecuteCommand(s) within a renderpass instance
(Begin-EndRenderPass). (i.e. our only subpass is then
VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS instead of
VK_SUBPASS_CONTENTS_INLINE)
The command buffer management mode is decided on a per frame basis,
controlled by the ExternalContentsInPass flag of beginFrame().
*/ */
/*! /*!
@ -182,6 +204,13 @@ QT_BEGIN_NAMESPACE
\l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair. \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
*/ */
/*!
\class QRhiVulkanRenderPassNativeHandles
\internal
\inmodule QtGui
\brief Holds the Vulkan render pass object backing a QRhiRenderPassDescriptor.
*/
static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
{ {
return (v + byteAlign - 1) & ~(byteAlign - 1); return (v + byteAlign - 1) & ~(byteAlign - 1);
@ -1469,7 +1498,6 @@ static inline bool checkDeviceLost(VkResult err)
QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
{ {
Q_UNUSED(flags);
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QRhiProfilerPrivate *rhiP = profilerPrivateOrNull(); QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
@ -1518,7 +1546,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
if (frame.timestampQueryIndex >= 0) { if (frame.timestampQueryIndex >= 0) {
quint64 timestamp[2] = { 0, 0 }; quint64 timestamp[2] = { 0, 0 };
VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2, VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT); 2 * sizeof(quint64), timestamp, sizeof(quint64),
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2); timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
frame.timestampQueryIndex = -1; frame.timestampQueryIndex = -1;
if (err == VK_SUCCESS) { if (err == VK_SUCCESS) {
@ -1540,7 +1569,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
} }
// build new draw command buffer // build new draw command buffer
QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf); QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&frame.cmdBuf);
if (cbres != QRhi::FrameOpSuccess) if (cbres != QRhi::FrameOpSuccess)
return cbres; return cbres;
@ -1564,6 +1593,8 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin
} }
swapChainD->cbWrapper.cb = frame.cmdBuf; swapChainD->cbWrapper.cb = frame.cmdBuf;
swapChainD->cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
swapChainD->rtWrapper.d.fb = image.fb; swapChainD->rtWrapper.d.fb = image.fb;
@ -1584,7 +1615,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain); QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
Q_ASSERT(currentSwapChain == swapChainD); Q_ASSERT(currentSwapChain == swapChainD);
recordCommandBuffer(&swapChainD->cbWrapper); recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]); QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]); QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
@ -1628,10 +1659,10 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram
// stop recording and submit to the queue // stop recording and submit to the queue
Q_ASSERT(!frame.cmdFenceWaitable); Q_ASSERT(!frame.cmdFenceWaitable);
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent); const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf, QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(frame.cmdBuf,
frame.cmdFence, frame.cmdFence,
frame.imageSemWaitable ? &frame.imageSem : nullptr, frame.imageSemWaitable ? &frame.imageSem : nullptr,
needsPresent ? &frame.drawSem : nullptr); needsPresent ? &frame.drawSem : nullptr);
if (submitres != QRhi::FrameOpSuccess) if (submitres != QRhi::FrameOpSuccess)
return submitres; return submitres;
@ -1702,7 +1733,7 @@ void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
} }
QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb) QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb)
{ {
if (*cb) { if (*cb) {
df->vkFreeCommandBuffers(dev, cmdPool, 1, cb); df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
@ -1741,8 +1772,8 @@ QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
VkSemaphore *waitSem, VkSemaphore *signalSem) VkSemaphore *waitSem, VkSemaphore *signalSem)
{ {
VkResult err = df->vkEndCommandBuffer(cb); VkResult err = df->vkEndCommandBuffer(cb);
if (err != VK_SUCCESS) { if (err != VK_SUCCESS) {
@ -1793,9 +1824,9 @@ void QRhiVulkan::waitCommandCompletion(int frameSlot)
} }
} }
QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb) QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags)
{ {
QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb); QRhi::FrameOpResult cbres = startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
if (cbres != QRhi::FrameOpSuccess) if (cbres != QRhi::FrameOpSuccess)
return cbres; return cbres;
@ -1812,6 +1843,8 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
if (swapchains.count() > 1) if (swapchains.count() > 1)
waitCommandCompletion(currentFrameSlot); waitCommandCompletion(currentFrameSlot);
ofr.cbWrapper.useSecondaryCb = flags.testFlag(QRhi::ExternalContentsInPass);
prepareNewFrame(&ofr.cbWrapper); prepareNewFrame(&ofr.cbWrapper);
ofr.active = true; ofr.active = true;
@ -1819,12 +1852,13 @@ QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
return QRhi::FrameOpSuccess; return QRhi::FrameOpSuccess;
} }
QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame() QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame(QRhi::EndFrameFlags flags)
{ {
Q_UNUSED(flags);
Q_ASSERT(ofr.active); Q_ASSERT(ofr.active);
ofr.active = false; ofr.active = false;
recordCommandBuffer(&ofr.cbWrapper); recordPrimaryCommandBuffer(&ofr.cbWrapper);
if (!ofr.cmdFence) { if (!ofr.cmdFence) {
VkFenceCreateInfo fenceInfo; VkFenceCreateInfo fenceInfo;
@ -1837,7 +1871,7 @@ QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
} }
} }
QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr); QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess) if (submitres != QRhi::FrameOpSuccess)
return submitres; return submitres;
@ -1861,15 +1895,19 @@ QRhi::FrameOpResult QRhiVulkan::finish()
VkCommandBuffer cb; VkCommandBuffer cb;
if (ofr.active) { if (ofr.active) {
Q_ASSERT(!currentSwapChain); Q_ASSERT(!currentSwapChain);
recordCommandBuffer(&ofr.cbWrapper); Q_ASSERT(ofr.cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
recordPrimaryCommandBuffer(&ofr.cbWrapper);
ofr.cbWrapper.resetCommands();
cb = ofr.cbWrapper.cb; cb = ofr.cbWrapper.cb;
} else { } else {
Q_ASSERT(currentSwapChain); Q_ASSERT(currentSwapChain);
Q_ASSERT(currentSwapChain->cbWrapper.recordingPass == QVkCommandBuffer::NoPass);
swapChainD = currentSwapChain; swapChainD = currentSwapChain;
recordCommandBuffer(&swapChainD->cbWrapper); recordPrimaryCommandBuffer(&swapChainD->cbWrapper);
swapChainD->cbWrapper.resetCommands();
cb = swapChainD->cbWrapper.cb; cb = swapChainD->cbWrapper.cb;
} }
QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr); QRhi::FrameOpResult submitres = endAndSubmitPrimaryCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
if (submitres != QRhi::FrameOpSuccess) if (submitres != QRhi::FrameOpSuccess)
return submitres; return submitres;
} }
@ -1879,9 +1917,9 @@ QRhi::FrameOpResult QRhiVulkan::finish()
if (inFrame) { if (inFrame) {
// Allocate and begin recording on a new command buffer. // Allocate and begin recording on a new command buffer.
if (ofr.active) if (ofr.active)
startCommandBuffer(&ofr.cbWrapper.cb); startPrimaryCommandBuffer(&ofr.cbWrapper.cb);
else else
startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf); startPrimaryCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
} }
executeDeferredReleases(true); executeDeferredReleases(true);
@ -1955,6 +1993,69 @@ void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
enqueueResourceUpdates(cbD, resourceUpdates); enqueueResourceUpdates(cbD, resourceUpdates);
} }
VkCommandBuffer QRhiVulkan::startSecondaryCommandBuffer(QVkRenderTargetData *rtD)
{
VkCommandBuffer secondaryCb;
VkCommandBufferAllocateInfo cmdBufInfo;
memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufInfo.commandPool = cmdPool;
cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
cmdBufInfo.commandBufferCount = 1;
VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, &secondaryCb);
if (err != VK_SUCCESS) {
qWarning("Failed to create secondary command buffer: %d", err);
return VK_NULL_HANDLE;
}
VkCommandBufferBeginInfo cmdBufBeginInfo;
memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufBeginInfo.flags = rtD ? VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT : 0;
VkCommandBufferInheritanceInfo cmdBufInheritInfo;
memset(&cmdBufInheritInfo, 0, sizeof(cmdBufInheritInfo));
cmdBufInheritInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
cmdBufInheritInfo.subpass = 0;
if (rtD) {
cmdBufInheritInfo.renderPass = rtD->rp->rp;
cmdBufInheritInfo.framebuffer = rtD->fb;
}
cmdBufBeginInfo.pInheritanceInfo = &cmdBufInheritInfo;
err = df->vkBeginCommandBuffer(secondaryCb, &cmdBufBeginInfo);
if (err != VK_SUCCESS) {
qWarning("Failed to begin secondary command buffer: %d", err);
df->vkFreeCommandBuffers(dev, cmdPool, 1, &secondaryCb);
return VK_NULL_HANDLE;
}
return secondaryCb;
}
void QRhiVulkan::endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD)
{
VkResult err = df->vkEndCommandBuffer(cb);
if (err != VK_SUCCESS)
qWarning("Failed to end secondary command buffer: %d", err);
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::ExecuteSecondary;
cmd.args.executeSecondary.cb = cb;
cbD->commands.append(cmd);
deferredReleaseSecondaryCommandBuffer(cb);
}
void QRhiVulkan::deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb)
{
QRhiVulkan::DeferredReleaseEntry e;
e.type = QRhiVulkan::DeferredReleaseEntry::CommandBuffer;
e.lastActiveFrameSlot = currentFrameSlot;
e.commandBuffer.cb = cb;
releaseQueue.append(e);
}
void QRhiVulkan::beginPass(QRhiCommandBuffer *cb, void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
QRhiRenderTarget *rt, QRhiRenderTarget *rt,
const QColor &colorClearValue, const QColor &colorClearValue,
@ -2034,6 +2135,9 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count(); cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
cbD->pools.clearValue.append(cvs.constData(), cvs.count()); cbD->pools.clearValue.append(cvs.constData(), cvs.count());
cbD->commands.append(cmd); cbD->commands.append(cmd);
if (cbD->useSecondaryCb)
cbD->secondaryCbs.append(startSecondaryCommandBuffer(rtD));
} }
void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@ -2041,6 +2145,13 @@ void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourc
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
if (cbD->useSecondaryCb) {
VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
cbD->secondaryCbs.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
cbD->resetCachedState();
}
QVkCommandBuffer::Command cmd; QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::EndRenderPass; cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
cbD->commands.append(cmd); cbD->commands.append(cmd);
@ -2063,6 +2174,9 @@ void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch
enqueueTransitionPassResources(cbD); enqueueTransitionPassResources(cbD);
cbD->recordingPass = QVkCommandBuffer::ComputePass; cbD->recordingPass = QVkCommandBuffer::ComputePass;
if (cbD->useSecondaryCb)
cbD->secondaryCbs.append(startSecondaryCommandBuffer());
} }
void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@ -2070,6 +2184,13 @@ void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
if (cbD->useSecondaryCb) {
VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
cbD->secondaryCbs.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
cbD->resetCachedState();
}
cbD->recordingPass = QVkCommandBuffer::NoPass; cbD->recordingPass = QVkCommandBuffer::NoPass;
if (resourceUpdates) if (resourceUpdates)
@ -2084,11 +2205,15 @@ void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) { if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::BindPipeline; df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_COMPUTE, psD->pipeline);
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; } else {
cmd.args.bindPipeline.pipeline = psD->pipeline; QVkCommandBuffer::Command cmd;
cbD->commands.append(cmd); cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
cmd.args.bindPipeline.pipeline = psD->pipeline;
cbD->commands.append(cmd);
}
cbD->currentGraphicsPipeline = nullptr; cbD->currentGraphicsPipeline = nullptr;
cbD->currentComputePipeline = ps; cbD->currentComputePipeline = ps;
@ -2103,12 +2228,16 @@ void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::Dispatch; df->vkCmdDispatch(cbD->secondaryCbs.last(), x, y, z);
cmd.args.dispatch.x = x; } else {
cmd.args.dispatch.y = y; QVkCommandBuffer::Command cmd;
cmd.args.dispatch.z = z; cmd.cmd = QVkCommandBuffer::Command::Dispatch;
cbD->commands.append(cmd); cmd.args.dispatch.x = x;
cmd.args.dispatch.y = y;
cmd.args.dispatch.z = z;
cbD->commands.append(cmd);
}
} }
VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv) VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
@ -3013,6 +3142,9 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
case QRhiVulkan::DeferredReleaseEntry::StagingBuffer: case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation)); vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
break; break;
case QRhiVulkan::DeferredReleaseEntry::CommandBuffer:
df->vkFreeCommandBuffers(dev, cmdPool, 1, &e.commandBuffer.cb);
break;
default: default:
Q_UNREACHABLE(); Q_UNREACHABLE();
break; break;
@ -3113,14 +3245,15 @@ VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD) void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
{ {
cbD->passResTrackers.append(QRhiPassResourceTracker()); cbD->passResTrackers.append(QRhiPassResourceTracker());
cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
QVkCommandBuffer::Command cmd; QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources; cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1; cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
cbD->commands.append(cmd); cbD->commands.append(cmd);
cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
} }
void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD) void QRhiVulkan::recordPrimaryCommandBuffer(QVkCommandBuffer *cbD)
{ {
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
@ -3165,7 +3298,8 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break; break;
case QVkCommandBuffer::Command::BeginRenderPass: case QVkCommandBuffer::Command::BeginRenderPass:
cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex; cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE); df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc,
cbD->useSecondaryCb ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
break; break;
case QVkCommandBuffer::Command::EndRenderPass: case QVkCommandBuffer::Command::EndRenderPass:
df->vkCmdEndRenderPass(cbD->cb); df->vkCmdEndRenderPass(cbD->cb);
@ -3218,13 +3352,15 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
break; break;
case QVkCommandBuffer::Command::DebugMarkerBegin: case QVkCommandBuffer::Command::DebugMarkerBegin:
cmd.args.debugMarkerBegin.marker.pMarkerName = cmd.args.debugMarkerBegin.marker.pMarkerName =
cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData(); cbD->pools.debugMarkerData[cmd.args.debugMarkerBegin.markerNameIndex].constData();
vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker); vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
break; break;
case QVkCommandBuffer::Command::DebugMarkerEnd: case QVkCommandBuffer::Command::DebugMarkerEnd:
vkCmdDebugMarkerEnd(cbD->cb); vkCmdDebugMarkerEnd(cbD->cb);
break; break;
case QVkCommandBuffer::Command::DebugMarkerInsert: case QVkCommandBuffer::Command::DebugMarkerInsert:
cmd.args.debugMarkerInsert.marker.pMarkerName =
cbD->pools.debugMarkerData[cmd.args.debugMarkerInsert.markerNameIndex].constData();
vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker); vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
break; break;
case QVkCommandBuffer::Command::TransitionPassResources: case QVkCommandBuffer::Command::TransitionPassResources:
@ -3233,12 +3369,13 @@ void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
case QVkCommandBuffer::Command::Dispatch: case QVkCommandBuffer::Command::Dispatch:
df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z); df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
break; break;
case QVkCommandBuffer::Command::ExecuteSecondary:
df->vkCmdExecuteCommands(cbD->cb, 1, &cmd.args.executeSecondary.cb);
break;
default: default:
break; break;
} }
} }
cbD->resetCommands();
} }
static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access) static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
@ -3655,11 +3792,15 @@ void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) { if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::BindPipeline; df->vkCmdBindPipeline(cbD->secondaryCbs.last(), VK_PIPELINE_BIND_POINT_GRAPHICS, psD->pipeline);
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; } else {
cmd.args.bindPipeline.pipeline = psD->pipeline; QVkCommandBuffer::Command cmd;
cbD->commands.append(cmd); cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
cmd.args.bindPipeline.pipeline = psD->pipeline;
cbD->commands.append(cmd);
}
cbD->currentGraphicsPipeline = ps; cbD->currentGraphicsPipeline = ps;
cbD->currentComputePipeline = nullptr; cbD->currentComputePipeline = nullptr;
@ -3857,16 +3998,25 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin
} }
} }
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet; df->vkCmdBindDescriptorSets(cbD->secondaryCbs.last(),
cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
: VK_PIPELINE_BIND_POINT_COMPUTE; gfxPsD ? gfxPsD->layout : compPsD->layout,
cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout; 0, 1, &srbD->descSets[descSetIdx],
cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx]; dynOfs.count(),
cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count(); dynOfs.count() ? dynOfs.constData() : nullptr);
cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count(); } else {
cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count()); QVkCommandBuffer::Command cmd;
cbD->commands.append(cmd); cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
: VK_PIPELINE_BIND_POINT_COMPUTE;
cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
cbD->commands.append(cmd);
}
if (gfxPsD) { if (gfxPsD) {
cbD->currentGraphicsSrb = srb; cbD->currentGraphicsSrb = srb;
@ -3922,15 +4072,20 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
QRhiPassResourceTracker::BufVertexInputStage); QRhiPassResourceTracker::BufVertexInputStage);
} }
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer; df->vkCmdBindVertexBuffers(cbD->secondaryCbs.last(), startBinding,
cmd.args.bindVertexBuffer.startBinding = startBinding; bufs.count(), bufs.constData(), ofs.constData());
cmd.args.bindVertexBuffer.count = bufs.count(); } else {
cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count(); QVkCommandBuffer::Command cmd;
cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count()); cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count(); cmd.args.bindVertexBuffer.startBinding = startBinding;
cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count()); cmd.args.bindVertexBuffer.count = bufs.count();
cbD->commands.append(cmd); cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
cbD->commands.append(cmd);
}
} }
if (indexBuf) { if (indexBuf) {
@ -3953,12 +4108,16 @@ void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
cbD->currentIndexOffset = indexOffset; cbD->currentIndexOffset = indexOffset;
cbD->currentIndexFormat = type; cbD->currentIndexFormat = type;
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer; df->vkCmdBindIndexBuffer(cbD->secondaryCbs.last(), vkindexbuf, indexOffset, type);
cmd.args.bindIndexBuffer.buf = vkindexbuf; } else {
cmd.args.bindIndexBuffer.ofs = indexOffset; QVkCommandBuffer::Command cmd;
cmd.args.bindIndexBuffer.type = type; cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
cbD->commands.append(cmd); cmd.args.bindIndexBuffer.buf = vkindexbuf;
cmd.args.bindIndexBuffer.ofs = indexOffset;
cmd.args.bindIndexBuffer.type = type;
cbD->commands.append(cmd);
}
trackedRegisterBuffer(&passResTracker, ibufD, slot, trackedRegisterBuffer(&passResTracker, ibufD, slot,
QRhiPassResourceTracker::BufIndexRead, QRhiPassResourceTracker::BufIndexRead,
@ -3979,7 +4138,6 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
return; return;
QVkCommandBuffer::Command cmd; QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::SetViewport;
VkViewport *vp = &cmd.args.setViewport.viewport; VkViewport *vp = &cmd.args.setViewport.viewport;
vp->x = x; vp->x = x;
vp->y = y; vp->y = y;
@ -3987,16 +4145,26 @@ void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport
vp->height = h; vp->height = h;
vp->minDepth = viewport.minDepth(); vp->minDepth = viewport.minDepth();
vp->maxDepth = viewport.maxDepth(); vp->maxDepth = viewport.maxDepth();
cbD->commands.append(cmd);
if (cbD->useSecondaryCb) {
df->vkCmdSetViewport(cbD->secondaryCbs.last(), 0, 1, vp);
} else {
cmd.cmd = QVkCommandBuffer::Command::SetViewport;
cbD->commands.append(cmd);
}
if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) { if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor; VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x; s->offset.x = x;
s->offset.y = y; s->offset.y = y;
s->extent.width = w; s->extent.width = w;
s->extent.height = h; s->extent.height = h;
cbD->commands.append(cmd); if (cbD->useSecondaryCb) {
df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
} else {
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
cbD->commands.append(cmd);
}
} }
} }
@ -4013,13 +4181,18 @@ void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
return; return;
QVkCommandBuffer::Command cmd; QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
VkRect2D *s = &cmd.args.setScissor.scissor; VkRect2D *s = &cmd.args.setScissor.scissor;
s->offset.x = x; s->offset.x = x;
s->offset.y = y; s->offset.y = y;
s->extent.width = w; s->extent.width = w;
s->extent.height = h; s->extent.height = h;
cbD->commands.append(cmd);
if (cbD->useSecondaryCb) {
df->vkCmdSetScissor(cbD->secondaryCbs.last(), 0, 1, s);
} else {
cmd.cmd = QVkCommandBuffer::Command::SetScissor;
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c) void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
@ -4027,13 +4200,18 @@ void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants; float constants[] = { float(c.redF()), float(c.greenF()), float(c.blueF()), float(c.alphaF()) };
cmd.args.setBlendConstants.c[0] = c.redF(); df->vkCmdSetBlendConstants(cbD->secondaryCbs.last(), constants);
cmd.args.setBlendConstants.c[1] = c.greenF(); } else {
cmd.args.setBlendConstants.c[2] = c.blueF(); QVkCommandBuffer::Command cmd;
cmd.args.setBlendConstants.c[3] = c.alphaF(); cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
cbD->commands.append(cmd); cmd.args.setBlendConstants.c[0] = c.redF();
cmd.args.setBlendConstants.c[1] = c.greenF();
cmd.args.setBlendConstants.c[2] = c.blueF();
cmd.args.setBlendConstants.c[3] = c.alphaF();
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue) void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
@ -4041,10 +4219,14 @@ void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::SetStencilRef; df->vkCmdSetStencilReference(cbD->secondaryCbs.last(), VK_STENCIL_FRONT_AND_BACK, refValue);
cmd.args.setStencilRef.ref = refValue; } else {
cbD->commands.append(cmd); QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
cmd.args.setStencilRef.ref = refValue;
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount, void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
@ -4053,13 +4235,17 @@ void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::Draw; df->vkCmdDraw(cbD->secondaryCbs.last(), vertexCount, instanceCount, firstVertex, firstInstance);
cmd.args.draw.vertexCount = vertexCount; } else {
cmd.args.draw.instanceCount = instanceCount; QVkCommandBuffer::Command cmd;
cmd.args.draw.firstVertex = firstVertex; cmd.cmd = QVkCommandBuffer::Command::Draw;
cmd.args.draw.firstInstance = firstInstance; cmd.args.draw.vertexCount = vertexCount;
cbD->commands.append(cmd); cmd.args.draw.instanceCount = instanceCount;
cmd.args.draw.firstVertex = firstVertex;
cmd.args.draw.firstInstance = firstInstance;
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount, void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
@ -4068,14 +4254,19 @@ void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass); Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
QVkCommandBuffer::Command cmd; if (cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::DrawIndexed; df->vkCmdDrawIndexed(cbD->secondaryCbs.last(), indexCount, instanceCount,
cmd.args.drawIndexed.indexCount = indexCount; firstIndex, vertexOffset, firstInstance);
cmd.args.drawIndexed.instanceCount = instanceCount; } else {
cmd.args.drawIndexed.firstIndex = firstIndex; QVkCommandBuffer::Command cmd;
cmd.args.drawIndexed.vertexOffset = vertexOffset; cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
cmd.args.drawIndexed.firstInstance = firstInstance; cmd.args.drawIndexed.indexCount = indexCount;
cbD->commands.append(cmd); cmd.args.drawIndexed.instanceCount = instanceCount;
cmd.args.drawIndexed.firstIndex = firstIndex;
cmd.args.drawIndexed.vertexOffset = vertexOffset;
cmd.args.drawIndexed.firstInstance = firstInstance;
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name) void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
@ -4088,12 +4279,17 @@ void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
QVkCommandBuffer::Command cmd; if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin; marker.pMarkerName = name.constData();
cmd.args.debugMarkerBegin.marker = marker; vkCmdDebugMarkerBegin(cbD->secondaryCbs.last(), &marker);
cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count(); } else {
cbD->pools.debugMarkerName.append(name); QVkCommandBuffer::Command cmd;
cbD->commands.append(cmd); cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
cmd.args.debugMarkerBegin.marker = marker;
cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerData.count();
cbD->pools.debugMarkerData.append(name);
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb) void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
@ -4102,9 +4298,13 @@ void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
return; return;
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
QVkCommandBuffer::Command cmd; if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd; vkCmdDebugMarkerEnd(cbD->secondaryCbs.last());
cbD->commands.append(cmd); } else {
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
cbD->commands.append(cmd);
}
} }
void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg) void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
@ -4115,13 +4315,19 @@ void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
VkDebugMarkerMarkerInfoEXT marker; VkDebugMarkerMarkerInfoEXT marker;
memset(&marker, 0, sizeof(marker)); memset(&marker, 0, sizeof(marker));
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
marker.pMarkerName = msg.constData();
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
QVkCommandBuffer::Command cmd; if (cbD->recordingPass != QVkCommandBuffer::NoPass && cbD->useSecondaryCb) {
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert; marker.pMarkerName = msg.constData();
cmd.args.debugMarkerInsert.marker = marker; vkCmdDebugMarkerInsert(cbD->secondaryCbs.last(), &marker);
cbD->commands.append(cmd); } else {
QVkCommandBuffer::Command cmd;
cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
cmd.args.debugMarkerInsert.marker = marker;
cmd.args.debugMarkerInsert.markerNameIndex = cbD->pools.debugMarkerData.count();
cbD->pools.debugMarkerData.append(msg);
cbD->commands.append(cmd);
}
} }
const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb) const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
@ -4129,14 +4335,76 @@ const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles(); return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
} }
static inline QVkRenderTargetData *maybeRenderTargetData(QVkCommandBuffer *cbD)
{
Q_ASSERT(cbD->currentTarget);
QVkRenderTargetData *rtD = nullptr;
if (cbD->recordingPass == QVkCommandBuffer::RenderPass) {
switch (cbD->currentTarget->resourceType()) {
case QRhiResource::RenderTarget:
rtD = &QRHI_RES(QVkReferenceRenderTarget, cbD->currentTarget)->d;
break;
case QRhiResource::TextureRenderTarget:
rtD = &QRHI_RES(QVkTextureRenderTarget, cbD->currentTarget)->d;
break;
default:
Q_UNREACHABLE();
break;
}
}
return rtD;
}
void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb) void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
{ {
Q_UNUSED(cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
// When not in a pass, it is simple: record what we have (but do not
// submit), the cb can then be used to record more external commands.
if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
recordPrimaryCommandBuffer(cbD);
cbD->resetCommands();
return;
}
// Otherwise, inside a pass, have a secondary command buffer (with
// RENDER_PASS_CONTINUE). Using the main one is not acceptable since we
// cannot just record at this stage, that would mess up the resource
// tracking and commands like TransitionPassResources.
if (cbD->inExternal)
return;
if (!cbD->useSecondaryCb) {
qWarning("beginExternal() within a pass is only supported with secondary command buffers. "
"This can be enabled by passing QRhi::ExternalContentsInPass to beginFrame().");
return;
}
VkCommandBuffer secondaryCb = cbD->secondaryCbs.last();
cbD->secondaryCbs.removeLast();
endAndEnqueueSecondaryCommandBuffer(secondaryCb, cbD);
VkCommandBuffer extCb = startSecondaryCommandBuffer(maybeRenderTargetData(cbD));
if (extCb) {
cbD->secondaryCbs.append(extCb);
cbD->inExternal = true;
}
} }
void QRhiVulkan::endExternal(QRhiCommandBuffer *cb) void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
{ {
QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb); QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
if (cbD->recordingPass == QVkCommandBuffer::NoPass) {
Q_ASSERT(cbD->commands.isEmpty() && cbD->currentPassResTrackerIndex == -1);
} else if (cbD->inExternal) {
VkCommandBuffer extCb = cbD->secondaryCbs.last();
cbD->secondaryCbs.removeLast();
endAndEnqueueSecondaryCommandBuffer(extCb, cbD);
cbD->secondaryCbs.append(startSecondaryCommandBuffer(maybeRenderTargetData(cbD)));
}
cbD->resetCachedState(); cbD->resetCachedState();
} }
@ -5098,6 +5366,12 @@ void QVkRenderPassDescriptor::release()
rhiD->unregisterResource(this); rhiD->unregisterResource(this);
} }
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{
nativeHandlesStruct.renderPass = rp;
return &nativeHandlesStruct;
}
QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi) QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi) : QRhiRenderTarget(rhi)
{ {
@ -5786,6 +6060,22 @@ void QVkCommandBuffer::release()
// nothing to do here, cb is not owned by us // nothing to do here, cb is not owned by us
} }
const QRhiNativeHandles *QVkCommandBuffer::nativeHandles()
{
// Ok this is messy but no other way has been devised yet. Outside
// begin(Compute)Pass - end(Compute)Pass it is simple - just return the
// primary VkCommandBuffer. Inside, however, we need to provide the current
// secondary command buffer (typically the one started by beginExternal(),
// in case we are between beginExternal - endExternal inside a pass).
if (useSecondaryCb && !secondaryCbs.isEmpty())
nativeHandlesStruct.commandBuffer = secondaryCbs.last();
else
nativeHandlesStruct.commandBuffer = cb;
return &nativeHandlesStruct;
}
QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi) QVkSwapChain::QVkSwapChain(QRhiImplementation *rhi)
: QRhiSwapChain(rhi), : QRhiSwapChain(rhi),
rtWrapper(rhi), rtWrapper(rhi),

View File

@ -80,6 +80,11 @@ struct Q_GUI_EXPORT QRhiVulkanCommandBufferNativeHandles : public QRhiNativeHand
VkCommandBuffer commandBuffer = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
}; };
struct Q_GUI_EXPORT QRhiVulkanRenderPassNativeHandles : public QRhiNativeHandles
{
VkRenderPass renderPass = VK_NULL_HANDLE;
};
QT_END_NAMESPACE QT_END_NAMESPACE
#endif #endif

View File

@ -171,9 +171,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVkRenderPassDescriptor(QRhiImplementation *rhi); QVkRenderPassDescriptor(QRhiImplementation *rhi);
~QVkRenderPassDescriptor(); ~QVkRenderPassDescriptor();
void release() override; void release() override;
const QRhiNativeHandles *nativeHandles() override;
VkRenderPass rp = VK_NULL_HANDLE; VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false; bool ownsRp = false;
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1; int lastActiveFrameSlot = -1;
}; };
@ -307,13 +309,11 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
~QVkCommandBuffer(); ~QVkCommandBuffer();
void release() override; void release() override;
VkCommandBuffer cb = VK_NULL_HANDLE; const QRhiNativeHandles *nativeHandles();
QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
const QRhiNativeHandles *nativeHandles() { VkCommandBuffer cb = VK_NULL_HANDLE; // primary
nativeHandlesStruct.commandBuffer = cb; bool useSecondaryCb = false;
return &nativeHandlesStruct; QRhiVulkanCommandBufferNativeHandles nativeHandlesStruct;
}
enum PassType { enum PassType {
NoPass, NoPass,
@ -322,9 +322,12 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
}; };
void resetState() { void resetState() {
resetCommands();
recordingPass = NoPass; recordingPass = NoPass;
currentTarget = nullptr; currentTarget = nullptr;
secondaryCbs.clear();
resetCommands();
resetCachedState(); resetCachedState();
} }
@ -341,6 +344,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
currentIndexFormat = VK_INDEX_TYPE_UINT16; currentIndexFormat = VK_INDEX_TYPE_UINT16;
memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers)); memset(currentVertexBuffers, 0, sizeof(currentVertexBuffers));
memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets)); memset(currentVertexOffsets, 0, sizeof(currentVertexOffsets));
inExternal = false;
} }
PassType recordingPass; PassType recordingPass;
@ -358,6 +362,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32; static const int VERTEX_INPUT_RESOURCE_SLOT_COUNT = 32;
VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT]; VkBuffer currentVertexBuffers[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT]; quint32 currentVertexOffsets[VERTEX_INPUT_RESOURCE_SLOT_COUNT];
QVarLengthArray<VkCommandBuffer, 4> secondaryCbs;
bool inExternal;
struct Command { struct Command {
enum Cmd { enum Cmd {
@ -384,7 +390,8 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
DebugMarkerEnd, DebugMarkerEnd,
DebugMarkerInsert, DebugMarkerInsert,
TransitionPassResources, TransitionPassResources,
Dispatch Dispatch,
ExecuteSecondary
}; };
Cmd cmd; Cmd cmd;
@ -493,6 +500,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
} debugMarkerEnd; } debugMarkerEnd;
struct { struct {
VkDebugMarkerMarkerInfoEXT marker; VkDebugMarkerMarkerInfoEXT marker;
int markerNameIndex;
} debugMarkerInsert; } debugMarkerInsert;
struct { struct {
int trackerIndex; int trackerIndex;
@ -500,6 +508,9 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
struct { struct {
int x, y, z; int x, y, z;
} dispatch; } dispatch;
struct {
VkCommandBuffer cb;
} executeSecondary;
} args; } args;
}; };
QVector<Command> commands; QVector<Command> commands;
@ -508,9 +519,10 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
void resetCommands() { void resetCommands() {
commands.clear(); commands.clear();
resetPools();
passResTrackers.clear(); passResTrackers.clear();
currentPassResTrackerIndex = -1; currentPassResTrackerIndex = -1;
resetPools();
} }
void resetPools() { void resetPools() {
@ -519,7 +531,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
pools.dynamicOffset.clear(); pools.dynamicOffset.clear();
pools.vertexBuffer.clear(); pools.vertexBuffer.clear();
pools.vertexBufferOffset.clear(); pools.vertexBufferOffset.clear();
pools.debugMarkerName.clear(); pools.debugMarkerData.clear();
} }
struct { struct {
@ -528,7 +540,7 @@ struct QVkCommandBuffer : public QRhiCommandBuffer
QVarLengthArray<uint32_t, 4> dynamicOffset; QVarLengthArray<uint32_t, 4> dynamicOffset;
QVarLengthArray<VkBuffer, 4> vertexBuffer; QVarLengthArray<VkBuffer, 4> vertexBuffer;
QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset; QVarLengthArray<VkDeviceSize, 4> vertexBufferOffset;
QVarLengthArray<QByteArray, 4> debugMarkerName; QVarLengthArray<QByteArray, 4> debugMarkerData;
} pools; } pools;
friend class QRhiVulkan; friend class QRhiVulkan;
@ -592,7 +604,7 @@ struct QVkSwapChain : public QRhiSwapChain
bool imageAcquired = false; bool imageAcquired = false;
bool imageSemWaitable = false; bool imageSemWaitable = false;
quint32 imageIndex = 0; quint32 imageIndex = 0;
VkCommandBuffer cmdBuf = VK_NULL_HANDLE; VkCommandBuffer cmdBuf = VK_NULL_HANDLE; // primary
VkFence cmdFence = VK_NULL_HANDLE; VkFence cmdFence = VK_NULL_HANDLE;
bool cmdFenceWaitable = false; bool cmdFenceWaitable = false;
int timestampQueryIndex = -1; int timestampQueryIndex = -1;
@ -637,8 +649,8 @@ public:
QRhiSwapChain *createSwapChain() override; QRhiSwapChain *createSwapChain() override;
QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override; QRhi::FrameOpResult beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override; QRhi::FrameOpResult endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb) override; QRhi::FrameOpResult beginOffscreenFrame(QRhiCommandBuffer **cb, QRhi::BeginFrameFlags flags) override;
QRhi::FrameOpResult endOffscreenFrame() override; QRhi::FrameOpResult endOffscreenFrame(QRhi::EndFrameFlags flags) override;
QRhi::FrameOpResult finish() override; QRhi::FrameOpResult finish() override;
void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override; void resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates) override;
@ -727,9 +739,12 @@ public:
VkShaderModule createShader(const QByteArray &spirv); VkShaderModule createShader(const QByteArray &spirv);
void prepareNewFrame(QRhiCommandBuffer *cb); void prepareNewFrame(QRhiCommandBuffer *cb);
QRhi::FrameOpResult startCommandBuffer(VkCommandBuffer *cb); VkCommandBuffer startSecondaryCommandBuffer(QVkRenderTargetData *rtD = nullptr);
QRhi::FrameOpResult endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence, void endAndEnqueueSecondaryCommandBuffer(VkCommandBuffer cb, QVkCommandBuffer *cbD);
VkSemaphore *waitSem, VkSemaphore *signalSem); void deferredReleaseSecondaryCommandBuffer(VkCommandBuffer cb);
QRhi::FrameOpResult startPrimaryCommandBuffer(VkCommandBuffer *cb);
QRhi::FrameOpResult endAndSubmitPrimaryCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
VkSemaphore *waitSem, VkSemaphore *signalSem);
void waitCommandCompletion(int frameSlot); void waitCommandCompletion(int frameSlot);
VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const; VkDeviceSize subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const;
using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>; using BufferImageCopyList = QVarLengthArray<VkBufferImageCopy, 16>;
@ -740,7 +755,7 @@ public:
void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates); void enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates);
void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD); void executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD);
void enqueueTransitionPassResources(QVkCommandBuffer *cbD); void enqueueTransitionPassResources(QVkCommandBuffer *cbD);
void recordCommandBuffer(QVkCommandBuffer *cbD); void recordPrimaryCommandBuffer(QVkCommandBuffer *cbD);
void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker, void trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
QVkBuffer *bufD, QVkBuffer *bufD,
int slot, int slot,
@ -856,7 +871,8 @@ public:
Sampler, Sampler,
TextureRenderTarget, TextureRenderTarget,
RenderPass, RenderPass,
StagingBuffer StagingBuffer,
CommandBuffer
}; };
Type type; Type type;
int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1 int lastActiveFrameSlot; // -1 if not used otherwise 0..FRAMES_IN_FLIGHT-1
@ -903,6 +919,9 @@ public:
VkBuffer stagingBuffer; VkBuffer stagingBuffer;
QVkAlloc stagingAllocation; QVkAlloc stagingAllocation;
} stagingBuffer; } stagingBuffer;
struct {
VkCommandBuffer cb;
} commandBuffer;
}; };
}; };
QVector<DeferredReleaseEntry> releaseQueue; QVector<DeferredReleaseEntry> releaseQueue;

View File

@ -1689,7 +1689,7 @@ void QNetworkAccessManager::clearConnectionCache()
\sa setAutoDeleteReplies, \sa setAutoDeleteReplies,
QNetworkRequest::AutoDeleteReplyOnFinishAttribute QNetworkRequest::AutoDeleteReplyOnFinishAttribute
*/ */
bool QNetworkAccessManager::autoDeleteReplies() bool QNetworkAccessManager::autoDeleteReplies() const
{ {
return d_func()->autoDeleteReplies; return d_func()->autoDeleteReplies;
} }

View File

@ -167,7 +167,7 @@ public:
void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy); void setRedirectPolicy(QNetworkRequest::RedirectPolicy policy);
QNetworkRequest::RedirectPolicy redirectPolicy() const; QNetworkRequest::RedirectPolicy redirectPolicy() const;
bool autoDeleteReplies(); bool autoDeleteReplies() const;
void setAutoDeleteReplies(bool autoDelete); void setAutoDeleteReplies(bool autoDelete);
Q_SIGNALS: Q_SIGNALS:

View File

@ -1966,6 +1966,7 @@ void QAbstractItemView::mouseDoubleClickEvent(QMouseEvent *event)
if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event) if ((event->button() == Qt::LeftButton) && !edit(persistent, DoubleClicked, event)
&& !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
emit activated(persistent); emit activated(persistent);
d->pressedIndex = QModelIndex();
} }
#if QT_CONFIG(draganddrop) #if QT_CONFIG(draganddrop)

View File

@ -1943,6 +1943,7 @@ void QTreeView::mouseDoubleClickEvent(QMouseEvent *event)
if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this)) if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, 0, this))
emit activated(persistent); emit activated(persistent);
d->pressedIndex = QModelIndex();
d->executePostedLayout(); // we need to make sure viewItems is updated d->executePostedLayout(); // we need to make sure viewItems is updated
if (d->itemsExpandable if (d->itemsExpandable
&& d->expandsOnDoubleClick && d->expandsOnDoubleClick

View File

@ -36,6 +36,7 @@
#include <QtNetwork/qtcpserver.h> #include <QtNetwork/qtcpserver.h>
#include <QtNetwork/qtcpsocket.h> #include <QtNetwork/qtcpsocket.h>
#include <QtCore/qelapsedtimer.h> #include <QtCore/qelapsedtimer.h>
#include <QtCore/qtimer.h>
#include <QtCore/qt_windows.h> #include <QtCore/qt_windows.h>
@ -49,7 +50,7 @@ class tst_NoQtEventLoop : public QObject
private slots: private slots:
void consumeMouseEvents(); void consumeMouseEvents();
void consumeSocketEvents(); void consumeSocketEvents();
void deliverEventsInLivelock();
}; };
class Window : public QRasterWindow class Window : public QRasterWindow
@ -312,6 +313,34 @@ void tst_NoQtEventLoop::consumeSocketEvents()
QVERIFY(server.hasPendingConnections()); QVERIFY(server.hasPendingConnections());
} }
void tst_NoQtEventLoop::deliverEventsInLivelock()
{
int argc = 1;
char *argv[] = { const_cast<char *>("test"), 0 };
QGuiApplication app(argc, argv);
QTimer livelockTimer;
livelockTimer.start(0);
QTimer::singleShot(100, Qt::CoarseTimer, &livelockTimer, &QTimer::stop);
QElapsedTimer elapsedTimer;
elapsedTimer.start();
// Exec own message loop
MSG msg;
forever {
if (elapsedTimer.hasExpired(3000) || !livelockTimer.isActive())
break;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
QVERIFY(!livelockTimer.isActive());
}
#include <tst_noqteventloop.moc> #include <tst_noqteventloop.moc>
QTEST_APPLESS_MAIN(tst_NoQtEventLoop) QTEST_APPLESS_MAIN(tst_NoQtEventLoop)

View File

@ -124,6 +124,7 @@ QString graphicsApiName()
QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers; QRhi::Flags rhiFlags = QRhi::EnableDebugMarkers;
int sampleCount = 1; int sampleCount = 1;
QRhiSwapChain::Flags scFlags = 0; QRhiSwapChain::Flags scFlags = 0;
QRhi::BeginFrameFlags beginFrameFlags = 0;
QRhi::EndFrameFlags endFrameFlags = 0; QRhi::EndFrameFlags endFrameFlags = 0;
class Window : public QWindow class Window : public QWindow
@ -375,7 +376,7 @@ void Window::render()
// GPU/present, and that's what throttles the thread to the refresh rate. // GPU/present, and that's what throttles the thread to the refresh rate.
// (except for OpenGL where it happens either in endFrame or somewhere else // (except for OpenGL where it happens either in endFrame or somewhere else
// depending on the GL implementation) // depending on the GL implementation)
QRhi::FrameOpResult r = m_r->beginFrame(m_sc); QRhi::FrameOpResult r = m_r->beginFrame(m_sc, beginFrameFlags);
if (r == QRhi::FrameOpSwapChainOutOfDate) { if (r == QRhi::FrameOpSwapChainOutOfDate) {
resizeSwapChain(); resizeSwapChain();
if (!m_hasSwapChain) if (!m_hasSwapChain)

View File

@ -71,6 +71,7 @@
//#define READBACK_SWAPCHAIN //#define READBACK_SWAPCHAIN
//#define NO_VSYNC //#define NO_VSYNC
//#define USE_MIN_SWAPCHAIN_BUFFERS //#define USE_MIN_SWAPCHAIN_BUFFERS
//#define DECLARE_EXT_CONTENTS
struct { struct {
TriangleRenderer triRenderer; TriangleRenderer triRenderer;
@ -118,6 +119,10 @@ void preInit()
scFlags |= QRhiSwapChain::MinimalBufferCount; scFlags |= QRhiSwapChain::MinimalBufferCount;
#endif #endif
#ifdef DECLARE_EXT_CONTENTS
beginFrameFlags |= QRhi::ExternalContentsInPass;
#endif
// For OpenGL some of these are incorporated into the QSurfaceFormat by // For OpenGL some of these are incorporated into the QSurfaceFormat by
// examplefw.h after returning from here as that is out of the RHI's control. // examplefw.h after returning from here as that is out of the RHI's control.
} }