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