[Spice-devel] [PATCH qxl-wddm-dod v3 2/2] Synchronize display mode change and pushing drawables
Frediano Ziglio
fziglio at redhat.com
Fri May 5 11:07:27 UTC 2017
> On Fri, May 5, 2017 at 1:36 PM, Frediano Ziglio < fziglio at redhat.com > wrote:
> > >
>
> > > Upon change of display mode the driver waits until all the
>
> > > queued drawables pushed to the host or discarded. This eliminates
>
> > > server warnings "rendering incorrect" in "get_drawable" when the
>
> > > drawable command was created by guest driver just before change
>
> > > of display mode and posted to the server during or after the change.
>
> > >
>
> > > This patch and comments are heavily based on a patch sent by
>
> > > Yuri Benditovich (with serialization code completely changed and
>
> > > added generation to discard pending commands on old surface).
>
> > >
>
> > > Signed-off-by: Frediano Ziglio < fziglio at redhat.com >
>
> > > ---
>
> > > qxldod/QxlDod.cpp | 99
>
> > > +++++++++++++++++++++++++++++++++++++++++--------------
>
> > > qxldod/QxlDod.h | 20 +++++++++--
>
> > > 2 files changed, 92 insertions(+), 27 deletions(-)
>
> > >
>
> > > Changes since v2:
>
> > > - merged leak fix from Yuri Benditovich basing on "Move code for
> > > discarding
>
> > > drawable to separate procedure" patch.
>
> > >
>
> > > diff --git a/qxldod/QxlDod.cpp b/qxldod/QxlDod.cpp
>
> > > index 5ee54f5..07246fa 100755
>
> > > --- a/qxldod/QxlDod.cpp
>
> > > +++ b/qxldod/QxlDod.cpp
>
> > > @@ -3250,6 +3250,22 @@ NTSTATUS QxlDevice::QueryCurrentMode(PVIDEO_MODE
>
> > > RequestedMode)
>
> > > return Status;
>
> > > }
>
> > >
>
> > > +template<typename Closure>
>
> > > +class QxlGenericOperation: public QxlPresentOperation
>
> > > +{
>
> > > +public:
>
> > > + QxlGenericOperation(const Closure &_closure) : closure(_closure) {
>
> > > PAGED_CODE(); }
>
> > > + void Run() override { PAGED_CODE(); closure(); }
>
> > > +private:
>
> > > + Closure closure;
>
> > > +};
>
> > > +
>
> > > +template<typename Closure>
>
> > > +__forceinline QxlPresentOperation *BuildQxlOperation(Closure &&closure)
>
> > > +{
>
> > > + return new (NonPagedPoolNx) QxlGenericOperation<Closure>(closure);
>
> > About this line. Maybe should be PagedPool instead?
>
> Of course, PagedPool is enough as well as when allocating array of drawables.
> The change is not mandatory.
> When driver verifier is active for the driver, it tries to keep pageable
> allocations paged out
> to verify there are not accesses to them on dispatch irql, so in this case
> the cost of pageable
> allocation is higher.
But this should happen only during debugging, during normal usage the difference is
that paged pool is bigger than nonpaged pool so less prone to fail.
> > > +}
>
> > > +
>
> > > NTSTATUS QxlDevice::SetCurrentMode(ULONG Mode)
>
> > > {
>
> > > PAGED_CODE();
>
> > > @@ -3258,11 +3274,27 @@ NTSTATUS QxlDevice::SetCurrentMode(ULONG Mode)
>
> > > {
>
> > > if (Mode == m_ModeNumbers[idx])
>
> > > {
>
> > > - DestroyPrimarySurface();
>
> > > - CreatePrimarySurface(&m_ModeInfo[idx]);
>
> > > + if (!m_PresentThread)
>
> > > + break;
>
> > > DbgPrint(TRACE_LEVEL_INFORMATION, ("%s device %d: setting
>
> > > current mode %d (%d x %d)\n",
>
> > > __FUNCTION__, m_Id, Mode, m_ModeInfo[idx].VisScreenWidth,
>
> > > m_ModeInfo[idx].VisScreenHeight));
>
> > > +
>
> > > + // execute the operation in the worker thread to avoiding
>
> > > + // executing drawing commands while changing resolution
>
> > > + KEVENT finishEvent;
>
> > > + KeInitializeEvent(&finishEvent, SynchronizationEvent, FALSE);
>
> > > + ++m_DrawGeneration;
>
> > > + QxlPresentOperation *operation = BuildQxlOperation([=, this,
>
> > > &finishEvent]() {
>
> > > + PAGED_CODE();
>
> > > + DestroyPrimarySurface();
>
> > > + CreatePrimarySurface(&m_ModeInfo[idx]);
>
> > > + KeSetEvent(&finishEvent, IO_NO_INCREMENT, FALSE);
>
> > > + });
>
> > > + if (!operation)
>
> > > + return STATUS_NO_MEMORY;
>
> > > + PostToWorkerThread(operation);
>
> > > + WaitForObject(&finishEvent, NULL);
>
> > > return STATUS_SUCCESS;
>
> > > }
>
> > > }
>
> > > @@ -3892,6 +3924,34 @@ QxlDevice::ExecutePresentDisplayOnly(
>
> > > SrcBltInfo.Height = DstBltInfo.Height;
>
> > > }
>
> > >
>
> > > + uint16_t currentGeneration = m_DrawGeneration;
>
> > > + QxlPresentOperation *operation = BuildQxlOperation([=, this]() {
>
> > > + PAGED_CODE();
>
> > > + ULONG delayed = 0;
>
> > > +
>
> > > + for (UINT i = 0; pDrawables[i]; ++i)
>
> > > + {
>
> > > + ULONG n = PrepareDrawable(pDrawables[i]);
>
> > > + // only reason why drawables[i] is zeroed is stop flow
>
> > > + if (pDrawables[i]) {
>
> > > + delayed += n;
>
> > > + if (currentGeneration == m_DrawGeneration)
>
> > > + PushDrawable(pDrawables[i]);
>
> > > + else
>
> > > + DiscardDrawable(pDrawables[i]);
>
> > > + }
>
> > > + }
>
> > > + delete[] pDrawables;
>
> > > + if (delayed) {
>
> > > + DbgPrint(TRACE_LEVEL_WARNING, ("%s: %d delayed chunks\n",
>
> > > __FUNCTION__, delayed));
>
> > > + }
>
> > > + });
>
> > > + if (!operation) {
>
> > > + MmUnlockPages(ctx->Mdl);
>
> > > + IoFreeMdl(ctx->Mdl);
>
> > > + delete[] pDrawables;
>
> > > + return STATUS_NO_MEMORY;
>
> > > + }
>
> > >
>
> > > // Copy all the scroll rects from source image to video frame buffer.
>
> > > for (UINT i = 0; i < ctx->NumMoves; i++)
>
> > > @@ -3936,7 +3996,7 @@ QxlDevice::ExecutePresentDisplayOnly(
>
> > >
>
> > > pDrawables[nIndex] = NULL;
>
> > >
>
> > > - PostToWorkerThread(pDrawables);
>
> > > + PostToWorkerThread(operation);
>
> > >
>
> > > return STATUS_SUCCESS;
>
> > > }
>
> > > @@ -5164,6 +5224,10 @@ void QxlDevice::StopPresentThread()
>
> > > if (m_PresentThread)
>
> > > {
>
> > > DbgPrint(TRACE_LEVEL_INFORMATION, ("---> %s\n", __FUNCTION__));
>
> > > + // this cause pending drawing operation to be discarded instead
>
> > > + // of executed, there's no reason to execute them if we are
>
> > > + // destroying the device
>
> > > + ++m_DrawGeneration;
>
> > > PostToWorkerThread(NULL);
>
> > > NTSTATUS Status = ObReferenceObjectByHandle(
>
> > > m_PresentThread, 0, NULL, KernelMode, &pDispatcherObject, NULL);
>
> > > @@ -5247,14 +5311,12 @@ void QxlDevice::PresentThreadRoutine()
>
> > > PAGED_CODE();
>
> > > int wait;
>
> > > int notify;
>
> > > - ULONG delayed = 0;
>
> > > - QXLDrawable** drawables;
>
> > >
>
> > > DbgPrint(TRACE_LEVEL_INFORMATION, ("--->%s\n", __FUNCTION__));
>
> > >
>
> > > while (1)
>
> > > {
>
> > > - // Pop a drawables list from the ring
>
> > > + // Pop an operation from the ring
>
> > > // No need for a mutex, only one consumer thread
>
> > > SPICE_RING_CONS_WAIT(m_PresentRing, wait);
>
> > > while (wait) {
>
> > > @@ -5262,35 +5324,22 @@ void QxlDevice::PresentThreadRoutine()
>
> > > DoWaitForObject(&m_PresentEvent, NULL, NULL);
>
> > > SPICE_RING_CONS_WAIT(m_PresentRing, wait);
>
> > > }
>
> > > - drawables = *SPICE_RING_CONS_ITEM(m_PresentRing);
>
> > > + QxlPresentOperation *operation =
>
> > > *SPICE_RING_CONS_ITEM(m_PresentRing);
>
> > > SPICE_RING_POP(m_PresentRing, notify);
>
> > > if (notify) {
>
> > > KeSetEvent(&m_PresentThreadReadyEvent, 0, FALSE);
>
> > > }
>
> > >
>
> > > - if (drawables) {
>
> > > - for (UINT i = 0; drawables[i]; ++i)
>
> > > - {
>
> > > - ULONG n = PrepareDrawable(drawables[i]);
>
> > > - // only reason why drawables[i] is zeroed is stop flow
>
> > > - if (drawables[i]) {
>
> > > - delayed += n;
>
> > > - PushDrawable(drawables[i]);
>
> > > - }
>
> > > - }
>
> > > - delete[] drawables;
>
> > > - } else {
>
> > > + if (!operation) {
>
> > > DbgPrint(TRACE_LEVEL_WARNING, ("%s is being terminated\n",
>
> > > __FUNCTION__));
>
> > > break;
>
> > > }
>
> > > - if (delayed) {
>
> > > - DbgPrint(TRACE_LEVEL_WARNING, ("%s: %d delayed chunks\n",
>
> > > __FUNCTION__, delayed));
>
> > > - delayed = 0;
>
> > > - }
>
> > > + operation->Run();
>
> > > + delete operation;
>
> > > }
>
> > > }
>
> > >
>
> > > -void QxlDevice::PostToWorkerThread(QXLDrawable** drawables)
>
> > > +void QxlDevice::PostToWorkerThread(QxlPresentOperation *operation)
>
> > > {
>
> > > PAGED_CODE();
>
> > > // Push drawables into PresentRing and notify worker thread
>
> > > @@ -5300,7 +5349,7 @@ void QxlDevice::PostToWorkerThread(QXLDrawable**
>
> > > drawables)
>
> > > WaitForObject(&m_PresentThreadReadyEvent, NULL);
>
> > > SPICE_RING_PROD_WAIT(m_PresentRing, wait);
>
> > > }
>
> > > - *SPICE_RING_PROD_ITEM(m_PresentRing) = drawables;
>
> > > + *SPICE_RING_PROD_ITEM(m_PresentRing) = operation;
>
> > > SPICE_RING_PUSH(m_PresentRing, notify);
>
> > > if (notify) {
>
> > > KeSetEvent(&m_PresentEvent, 0, FALSE);
>
> > > diff --git a/qxldod/QxlDod.h b/qxldod/QxlDod.h
>
> > > index b524577..695b83a 100755
>
> > > --- a/qxldod/QxlDod.h
>
> > > +++ b/qxldod/QxlDod.h
>
> > > @@ -506,8 +506,20 @@ typedef struct DpcCbContext {
>
> > > #define MAX(x, y) (((x) >= (y)) ? (x) : (y))
>
> > > #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
>
> > >
>
> > > +// operation to be run by the presentation thread
>
> > > +class QxlPresentOperation
>
> > > +{
>
> > > +public:
>
> > > + QxlPresentOperation() {}
>
> > > + QxlPresentOperation(const QxlPresentOperation&) = delete;
>
> > > + void operator=(const QxlPresentOperation&) = delete;
>
> > > + // execute the operation
>
> > > + virtual void Run()=0;
>
> > > + virtual ~QxlPresentOperation() {}
>
> > > +};
>
> > > +
>
> > > #include "start-packed.h"
>
> > > -SPICE_RING_DECLARE(QXLPresentOnlyRing, QXLDrawable**, 1024);
>
> > > +SPICE_RING_DECLARE(QXLPresentOnlyRing, QxlPresentOperation*, 1024);
>
> > > #include "end-packed.h"
>
> > >
>
> > > class QxlDevice :
>
> > > @@ -620,7 +632,7 @@ private:
>
> > > static void PresentThreadRoutineWrapper(HANDLE dev) {
>
> > > ((QxlDevice *)dev)->PresentThreadRoutine();
>
> > > }
>
> > > - void PostToWorkerThread(QXLDrawable** drawables);
>
> > > + void PostToWorkerThread(QxlPresentOperation *operation);
>
> > >
>
> > > static LONG GetMaxSourceMappingHeight(RECT* DirtyRects, ULONG
>
> > > NumDirtyRects);
>
> > >
>
> > > @@ -675,6 +687,10 @@ private:
>
> > > QXLPHYSICAL* m_monitor_config_pa;
>
> > >
>
> > > QXLPresentOnlyRing m_PresentRing[1];
>
> > > + // generation, updated when resolution change
>
> > > + // this is used to detect if a draw command is obsoleted
>
> > > + // and should not be executed
>
> > > + uint16_t m_DrawGeneration;
>
> > > HANDLE m_PresentThread;
>
> > > BOOLEAN m_bActive;
>
> > > };
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20170505/0b437117/attachment-0001.html>
More information about the Spice-devel
mailing list