[Spice-devel] [PATCH v3 6/6] qxl-wddm-dod: Non-forced memory allocations with VSync
Frediano Ziglio
fziglio at redhat.com
Mon Apr 10 12:45:57 UTC 2017
>
> In case of VSync is active (for the driver this means it shall take
> in account watchdog policy and ensure fast execution of PresentDisplayOnly
> callback) allocate bitmaps for drawable objects using non-forced requests.
> If immediate allocation is not possible, place entire bitmap into memory
> chunk allocated from the OS.
> If bitmap is allocated from device memory, but one of later
> chunks can't be allocated, allocate this and further chunks from
> OS memory. All these 'delayed' allocations placed into linked list
> which root entry is part of QXLOutput structure.
> From separate thread, before sending drawable objects down, review
> the list of delayed chunks and allocate device memory (forced) to
> all of them.
> The cost of solution is 2 pointers added to each drawable or cursor
> object.
> Cursor commands currently do not use them; in future we have an option
> to offload also cursor commands.
>
> Signed-off-by: Yuri Benditovich <yuri.benditovich at daynix.com>
> ---
> qxldod/QxlDod.cpp | 108
> ++++++++++++++++++++++++++++++++++++++++++++++++++----
> qxldod/QxlDod.h | 3 ++
> 2 files changed, 103 insertions(+), 8 deletions(-)
>
> diff --git a/qxldod/QxlDod.cpp b/qxldod/QxlDod.cpp
> index c832c93..7ff903a 100755
> --- a/qxldod/QxlDod.cpp
> +++ b/qxldod/QxlDod.cpp
> @@ -4224,6 +4224,13 @@ void QxlDevice::DrawableAddRes(QXLDrawable *drawable,
> Resource *res)
> AddRes(output, res);
> }
>
> +static FORCEINLINE PLIST_ENTRY DelayedList(QXLDrawable *pd)
> +{
> + QXLOutput *output;
> + output = (QXLOutput *)((UINT8 *)pd - sizeof(QXLOutput));
> + return &output->list;
> +}
> +
This hacky code should just not exist. This assumes that before
every Drawable there is a QXLOutput.
I know there are already similar code but would be better to
use a structure that contains a QXLOutput and a QXLDrawable and
use that.
> void QxlDevice::CursorCmdAddRes(QXLCursorCmd *cmd, Resource *res)
> {
> PAGED_CODE();
> @@ -4331,6 +4338,7 @@ QXLDrawable *QxlDevice::Drawable(UINT8 type, CONST RECT
> *area, CONST RECT *clip,
> drawable->surfaces_dest[1] = - 1;
> drawable->surfaces_dest[2] = -1;
> CopyRect(&drawable->bbox, area);
> + InitializeListHead(DelayedList(drawable));
>
Use a constructor?
> if (!SetClip(clip, drawable)) {
> DbgPrint(TRACE_LEVEL_VERBOSE, ("%s: set clip failed\n",
> __FUNCTION__));
> @@ -4425,7 +4433,7 @@ BOOLEAN QxlDevice::AttachNewBitmap(QXLDrawable
> *drawable, UINT8 *src, UINT8 *src
> Resource *image_res;
> InternalImage *internal;
> QXLDataChunk *chunk;
> - PLIST_ENTRY pDelayedList = NULL;
> + PLIST_ENTRY pDelayedList = bForce ? NULL : DelayedList(drawable);
> UINT8* dest, *dest_end;
>
> height = drawable->u.copy.src_area.bottom;
> @@ -4494,9 +4502,12 @@ BOOLEAN QxlDevice::AttachNewBitmap(QXLDrawable
> *drawable, UINT8 *src, UINT8 *src
> }
>
> for (; src != src_end; src -= pitch, alloc_size -= line_size) {
> - BOOLEAN b = PutBytesAlign(&chunk, &dest, &dest_end, src, line_size,
> alloc_size, pDelayedList);
> - if (!b) {
> - DbgPrint(TRACE_LEVEL_WARNING, ("%s: aborting copy of lines\n",
> __FUNCTION__));
> + if (!PutBytesAlign(&chunk, &dest, &dest_end, src, line_size,
> alloc_size, pDelayedList)) {
> + if (pitch < 0 && bForce) {
> + DbgPrint(TRACE_LEVEL_WARNING, ("%s: aborting copy of lines
> (forced)\n", __FUNCTION__));
> + } else {
> + DbgPrint(TRACE_LEVEL_WARNING, ("%s: unexpected aborting copy
> of lines (force %d, pitch %d)\n", __FUNCTION__, bForce, pitch));
> + }
Why the if on the pitch and not just this last version?
> return FALSE;
> }
> }
> @@ -4551,7 +4562,13 @@ QXLDrawable *QxlDevice::PrepareBltBits (
> UINT8* src_end = src - pSrc->Pitch;
> src += pSrc->Pitch * (height - 1);
>
> - if (!AttachNewBitmap(drawable, src, src_end, (INT)pSrc->Pitch, TRUE)) {
> + if (!AttachNewBitmap(drawable, src, src_end, (INT)pSrc->Pitch,
> !g_bSupportVSync)) {
> + PLIST_ENTRY pDelayedList = DelayedList(drawable);
> + // if some delayed chunks were allocated, free them
> + while (!IsListEmpty(pDelayedList)) {
> + QXL_DELAYED_CHUNK *pdc = (QXL_DELAYED_CHUNK
> *)RemoveHeadList(pDelayedList);
> + delete[] reinterpret_cast<BYTE*>(pdc);
> + }
destructor?
> ReleaseOutput(drawable->release_info.id);
Why ReleaseOutput is not releasing the linked list, which is
in it?
> drawable = NULL;
> } else {
> @@ -5179,11 +5196,76 @@ void QxlDevice::StopPresentThread()
> }
> }
>
> +QXLDataChunk *QxlDevice::MakeChunk(QXL_DELAYED_CHUNK *pdc)
> +{
> + PAGED_CODE();
> + QXLDataChunk *chunk = (QXLDataChunk *)AllocMem(MSPACE_TYPE_VRAM,
> pdc->chunk.data_size + sizeof(QXLDataChunk), TRUE);
> + if (chunk)
> + {
> + chunk->data_size = pdc->chunk.data_size;
> + chunk->next_chunk = 0;
> + RtlCopyMemory(chunk->data, pdc->chunk.data, chunk->data_size);
> + }
> + return chunk;
> +}
> +
> +ULONG QxlDevice::PrepareDrawable(QXLDrawable*& drawable)
> +{
> + PAGED_CODE();
> + ULONG n = 0;
> + BOOLEAN bFail;
> + PLIST_ENTRY pe = DelayedList(drawable);
> + QXLDataChunk *chunk, *lastchunk = NULL;
> +
> + bFail = !m_bActive;
> +
> + while (!IsListEmpty(pe)) {
> + QXL_DELAYED_CHUNK *pdc = (QXL_DELAYED_CHUNK *)RemoveHeadList(pe);
> + if (!lastchunk) {
> + lastchunk = (QXLDataChunk *)pdc->chunk.prev_chunk;
> + }
> + if (!bFail && !lastchunk) {
> + // bitmap was not allocated, this is single delayed chunk
> + QXL_ASSERT(IsListEmpty(pe));
> +
> + if (AttachNewBitmap(
> + drawable,
> + pdc->chunk.data,
> + pdc->chunk.data + pdc->chunk.data_size,
> + -(drawable->u.copy.src_area.right * 4),
> + TRUE)) {
> + ++n;
> + } else {
> + bFail = TRUE;
> + }
> + }
> + if (!bFail && lastchunk) {
> + // some chunks were not allocated
> + chunk = MakeChunk(pdc);
> + if (chunk) {
> + chunk->prev_chunk = PA(lastchunk, m_SurfaceMemSlot);
> + lastchunk->next_chunk = PA(chunk, m_SurfaceMemSlot);
> + lastchunk = chunk;
> + ++n;
> + } else {
> + bFail = TRUE;
> + }
> + }
> + delete[] reinterpret_cast<BYTE*>(pdc);
> + }
> + if (bFail) {
> + ReleaseOutput(drawable->release_info.id);
This will leak memory if is not the last chunk and some allocations
will fail (in stop flow).
> + drawable = NULL;
> + }
> + return n;
> +}
> +
> void QxlDevice::PresentThreadRoutine()
> {
> PAGED_CODE();
> int wait;
> int notify;
> + ULONG delayed = 0;
> QXLDrawable** drawables;
>
> DbgPrint(TRACE_LEVEL_INFORMATION, ("--->%s\n", __FUNCTION__));
> @@ -5206,13 +5288,23 @@ void QxlDevice::PresentThreadRoutine()
>
> if (drawables) {
> for (UINT i = 0; drawables[i]; ++i)
> - PushDrawable(drawables[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 {
> + } else {
> 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;
If you move the variable declaration/initialization inside the for
this is not needed.
> + }
> }
> }
>
> diff --git a/qxldod/QxlDod.h b/qxldod/QxlDod.h
> index 28373b8..c35ec3e 100755
> --- a/qxldod/QxlDod.h
> +++ b/qxldod/QxlDod.h
> @@ -474,6 +474,7 @@ ReleaseMutex(
> #define MAX_OUTPUT_RES 6
>
> typedef struct QXLOutput {
> + LIST_ENTRY list;
> UINT32 num_res;
> #ifdef DBG
> UINT32 type;
> @@ -612,6 +613,8 @@ private:
> BOOLEAN PutBytesAlign(QXLDataChunk **chunk_ptr, UINT8 **now_ptr,
> UINT8 **end_ptr, UINT8 *src, int size,
> size_t alloc_size, PLIST_ENTRY pDelayed);
> + QXLDataChunk *MakeChunk(QXL_DELAYED_CHUNK *pdc);
> + ULONG PrepareDrawable(QXLDrawable*& drawable);
> void AsyncIo(UCHAR Port, UCHAR Value);
> void SyncIo(UCHAR Port, UCHAR Value);
> NTSTATUS UpdateChildStatus(BOOLEAN connect);
Frediano
More information about the Spice-devel
mailing list