<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Tue, May 2, 2017 at 4:33 PM, Frediano Ziglio <span dir="ltr"><<a href="mailto:fziglio@redhat.com" target="_blank">fziglio@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">Upon change of display mode the driver waits until all the<br>
queued drawables pushed to the host or discarded. This eliminates<br>
server warnings "rendering incorrect" in "get_drawable" when the<br>
drawable command was created by guest driver just before change<br>
of display mode and posted to the server during or after the change.<br>
<br>
This patch and comments are heavily based on a patch sent by<br>
Yuri Benditovich (with serialization code completely changed and<br>
added generation to discard pending commands on old surface).<br>
<br>
Signed-off-by: Frediano Ziglio <<a href="mailto:fziglio@redhat.com">fziglio@redhat.com</a>><br>
---<br>
</span> qxldod/QxlDod.cpp | 95 ++++++++++++++++++++++++++++++<wbr>++++++++++---------------<br>
 qxldod/QxlDod.h   | 20 ++++++++++--<br>
 2 files changed, 88 insertions(+), 27 deletions(-)<br>
<br>
Changes since v1:<br>
- fix code analizer warnings.<br>
<br>
diff --git a/qxldod/QxlDod.cpp b/qxldod/QxlDod.cpp<br>
index 1953688..5168941 100755<br>
<span class="">--- a/qxldod/QxlDod.cpp<br>
+++ b/qxldod/QxlDod.cpp<br>
@@ -3250,6 +3250,22 @@ NTSTATUS QxlDevice::QueryCurrentMode(<wbr>PVIDEO_MODE RequestedMode)<br>
     return Status;<br>
 }<br>
<br>
+template<typename Closure><br>
+class QxlGenericOperation: public QxlPresentOperation<br>
+{<br>
+public:<br>
</span>+    QxlGenericOperation(const Closure &_closure) : closure(_closure) { PAGED_CODE(); }<br>
+    void Run() override { PAGED_CODE(); closure(); }<br>
<span class="">+private:<br>
+    Closure closure;<br>
+};<br>
+<br>
+template<typename Closure><br>
</span>+__forceinline QxlPresentOperation *BuildQxlOperation(Closure &&closure)<br>
<span class="">+{<br>
+    return new (NonPagedPoolNx) QxlGenericOperation<Closure>(<wbr>closure);<br>
+}<br>
+<br>
 NTSTATUS QxlDevice::SetCurrentMode(<wbr>ULONG Mode)<br>
 {<br>
     PAGED_CODE();<br>
</span>@@ -3258,11 +3274,27 @@ NTSTATUS QxlDevice::SetCurrentMode(<wbr>ULONG Mode)<br>
<span class="">     {<br>
         if (Mode == m_ModeNumbers[idx])<br>
         {<br>
-            DestroyPrimarySurface();<br>
-            CreatePrimarySurface(&m_<wbr>ModeInfo[idx]);<br>
+            if (!m_PresentThread)<br>
+                break;<br>
             DbgPrint(TRACE_LEVEL_<wbr>INFORMATION, ("%s device %d: setting current mode %d (%d x %d)\n",<br>
                 __FUNCTION__, m_Id, Mode, m_ModeInfo[idx].<wbr>VisScreenWidth,<br>
                 m_ModeInfo[idx].<wbr>VisScreenHeight));<br>
+<br>
+            // execute the operation in the worker thread to avoiding<br>
+            // executing drawing commands while changing resolution<br>
+            KEVENT finishEvent;<br>
+            KeInitializeEvent(&<wbr>finishEvent, SynchronizationEvent, FALSE);<br>
+            ++m_DrawGeneration;<br>
+            QxlPresentOperation *operation = BuildQxlOperation([=, this, &finishEvent]() {<br>
</span>+                PAGED_CODE();<br>
<span class="">+                DestroyPrimarySurface();<br>
+                CreatePrimarySurface(&m_<wbr>ModeInfo[idx]);<br>
+                KeSetEvent(&finishEvent, IO_NO_INCREMENT, FALSE);<br>
+            });<br>
+            if (!operation)<br>
+                return STATUS_NO_MEMORY;<br>
+            PostToWorkerThread(operation);<br>
+            WaitForObject(&finishEvent, NULL);<br>
             return STATUS_SUCCESS;<br>
         }<br>
     }<br>
</span>@@ -3892,6 +3924,30 @@ QxlDevice::<wbr>ExecutePresentDisplayOnly(<br>
<span class="">         SrcBltInfo.Height = DstBltInfo.Height;<br>
     }<br>
<br>
+    uint16_t currentGeneration = m_DrawGeneration;<br>
+    QxlPresentOperation *operation = BuildQxlOperation([=, this]() {<br>
</span>+        PAGED_CODE();<br>
<span class="">+        ULONG delayed = 0;<br>
+<br>
+        for (UINT i = 0; pDrawables[i]; ++i)<br>
+        {<br>
+            ULONG n = PrepareDrawable(pDrawables[i])<wbr>;<br>
+            // only reason why drawables[i] is zeroed is stop flow<br>
+            if (pDrawables[i]) {<br>
+                delayed += n;<br>
+                if (currentGeneration == m_DrawGeneration)<br>
+                    PushDrawable(pDrawables[i]);<br>
+            }<br>
+        }<br>
+        delete[] pDrawables;<br>
+        if (delayed) {<br>
+            DbgPrint(TRACE_LEVEL_WARNING, ("%s: %d delayed chunks\n", __FUNCTION__, delayed));<br>
+        }<br>
+    });<br>
+    if (!operation) {<br>
+        delete[] pDrawables;<br>
+        return STATUS_NO_MEMORY;<br>
+    }<br>
<br>
     // Copy all the scroll rects from source image to video frame buffer.<br>
     for (UINT i = 0; i < ctx->NumMoves; i++)<br>
</span>@@ -3936,7 +3992,7 @@ QxlDevice::<wbr>ExecutePresentDisplayOnly(<br>
<span class=""><br>
     pDrawables[nIndex] = NULL;<br>
<br>
-    PostToWorkerThread(pDrawables)<wbr>;<br>
+    PostToWorkerThread(operation);<br>
<br>
     return STATUS_SUCCESS;<br>
 }<br>
</span>@@ -5157,6 +5213,10 @@ void QxlDevice::StopPresentThread()<br>
<span class="">     if (m_PresentThread)<br>
     {<br>
         DbgPrint(TRACE_LEVEL_<wbr>INFORMATION, ("---> %s\n", __FUNCTION__));<br>
+        // this cause pending drawing operation to be discarded instead<br>
+        // of executed, there's no reason to execute them if we are<br>
+        // destroying the device<br>
+        ++m_DrawGeneration;<br>
         PostToWorkerThread(NULL);<br>
         NTSTATUS Status = ObReferenceObjectByHandle(<br>
             m_PresentThread, 0, NULL, KernelMode, &pDispatcherObject, NULL);<br>
</span>@@ -5240,14 +5300,12 @@ void QxlDevice::<wbr>PresentThreadRoutine()<br>
<span class="">     PAGED_CODE();<br>
     int wait;<br>
     int notify;<br>
-    ULONG delayed = 0;<br>
-    QXLDrawable** drawables;<br>
<br>
     DbgPrint(TRACE_LEVEL_<wbr>INFORMATION, ("--->%s\n", __FUNCTION__));<br>
<br>
     while (1)<br>
     {<br>
-        // Pop a drawables list from the ring<br>
+        // Pop an operation from the ring<br>
         // No need for a mutex, only one consumer thread<br>
         SPICE_RING_CONS_WAIT(m_<wbr>PresentRing, wait);<br>
         while (wait) {<br>
</span>@@ -5255,35 +5313,22 @@ void QxlDevice::<wbr>PresentThreadRoutine()<br>
<div><div class="h5">             DoWaitForObject(&m_<wbr>PresentEvent, NULL, NULL);<br>
             SPICE_RING_CONS_WAIT(m_<wbr>PresentRing, wait);<br>
         }<br>
-        drawables = *SPICE_RING_CONS_ITEM(m_<wbr>PresentRing);<br>
+        QxlPresentOperation *operation = *SPICE_RING_CONS_ITEM(m_<wbr>PresentRing);<br>
         SPICE_RING_POP(m_PresentRing, notify);<br>
         if (notify) {<br>
             KeSetEvent(&m_<wbr>PresentThreadReadyEvent, 0, FALSE);<br>
         }<br>
<br>
-        if (drawables) {<br>
-            for (UINT i = 0; drawables[i]; ++i)<br>
-            {<br>
-                ULONG n = PrepareDrawable(drawables[i]);<br>
-                // only reason why drawables[i] is zeroed is stop flow<br>
-                if (drawables[i]) {<br>
-                    delayed += n;<br>
-                    PushDrawable(drawables[i]);<br>
-                }<br>
-            }<br>
-            delete[] drawables;<br>
-        } else {<br>
+        if (!operation) {<br>
             DbgPrint(TRACE_LEVEL_WARNING, ("%s is being terminated\n", __FUNCTION__));<br>
             break;<br>
         }<br>
-        if (delayed) {<br>
-            DbgPrint(TRACE_LEVEL_WARNING, ("%s: %d delayed chunks\n", __FUNCTION__, delayed));<br>
-            delayed = 0;<br>
-        }<br>
+        operation->Run();<br>
+        delete operation;<br>
     }<br>
 }<br>
<br>
-void QxlDevice::PostToWorkerThread(<wbr>QXLDrawable** drawables)<br>
+void QxlDevice::PostToWorkerThread(<wbr>QxlPresentOperation *operation)<br>
 {<br>
     PAGED_CODE();<br>
     // Push drawables into PresentRing and notify worker thread<br>
</div></div>@@ -5293,7 +5338,7 @@ void QxlDevice::PostToWorkerThread(<wbr>QXLDrawable** drawables)<br>
<div class="HOEnZb"><div class="h5">         WaitForObject(&m_<wbr>PresentThreadReadyEvent, NULL);<br>
         SPICE_RING_PROD_WAIT(m_<wbr>PresentRing, wait);<br>
     }<br>
-    *SPICE_RING_PROD_ITEM(m_<wbr>PresentRing) = drawables;<br>
+    *SPICE_RING_PROD_ITEM(m_<wbr>PresentRing) = operation;<br>
     SPICE_RING_PUSH(m_PresentRing, notify);<br>
     if (notify) {<br>
         KeSetEvent(&m_PresentEvent, 0, FALSE);<br>
diff --git a/qxldod/QxlDod.h b/qxldod/QxlDod.h<br>
index e865032..2ce3ce5 100755<br>
--- a/qxldod/QxlDod.h<br>
+++ b/qxldod/QxlDod.h<br>
@@ -506,8 +506,20 @@ typedef struct DpcCbContext {<br>
 #define MAX(x, y) (((x) >= (y)) ? (x) : (y))<br>
 #define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))<br>
<br>
+// operation to be run by the presentation thread<br>
+class QxlPresentOperation<br>
+{<br>
+public:<br>
+    QxlPresentOperation() {}<br>
+    QxlPresentOperation(const QxlPresentOperation&) = delete;<br>
+    void operator=(const QxlPresentOperation&) = delete;<br>
+    // execute the operation<br>
+    virtual void Run()=0;<br>
+    virtual ~QxlPresentOperation() {}<br>
+};<br>
+<br>
 #include "start-packed.h"<br>
-SPICE_RING_DECLARE(<wbr>QXLPresentOnlyRing, QXLDrawable**, 1024);<br>
+SPICE_RING_DECLARE(<wbr>QXLPresentOnlyRing, QxlPresentOperation*, 1024);<br>
 #include "end-packed.h"<br>
<br>
 class QxlDevice  :<br>
@@ -619,7 +631,7 @@ private:<br>
     static void PresentThreadRoutineWrapper(<wbr>HANDLE dev) {<br>
         ((QxlDevice *)dev)->PresentThreadRoutine()<wbr>;<br>
     }<br>
-    void PostToWorkerThread(<wbr>QXLDrawable** drawables);<br>
+    void PostToWorkerThread(<wbr>QxlPresentOperation *operation);<br>
<br>
     static LONG GetMaxSourceMappingHeight(<wbr>RECT* DirtyRects, ULONG NumDirtyRects);<br>
<br>
@@ -674,6 +686,10 @@ private:<br>
     QXLPHYSICAL* m_monitor_config_pa;<br>
<br>
     QXLPresentOnlyRing m_PresentRing[1];<br>
+    // generation, updated when resolution change<br>
+    // this is used to detect if a draw command is obsoleted<br>
+    // and should not be executed<br>
+    uint16_t m_DrawGeneration;<br>
     HANDLE m_PresentThread;<br>
     BOOLEAN m_bActive;<br>
 };<br>
--<br>
2.9.3<br>
<br></div></div></blockquote><div><br></div><div>ACK. Verified in WHCK setup.</div><div>There are 2 memory leaks (one is real as I'm able to reproduce the flow), I'm submitting the patch for them.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div class="HOEnZb"><div class="h5">
______________________________<wbr>_________________<br>
Spice-devel mailing list<br>
<a href="mailto:Spice-devel@lists.freedesktop.org">Spice-devel@lists.freedesktop.<wbr>org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/spice-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/spice-devel</a><br>
</div></div></blockquote></div><br></div></div>