[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 10:36:33 UTC 2017


> 
> 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?

> +}
> +
>  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;
>  };

Frediano


More information about the Spice-devel mailing list