/****************************************************************************/ // nschdisp.c // // Scheduler Display Driver code. // // Copyright (C) 1997-2000 Microsoft Corporation /****************************************************************************/ #include #define hdrstop #define TRC_FILE "nschdisp" #include #include #include #include #include #include #include #include #include #include #include #define DC_INCLUDE_DATA #include #undef DC_INCLUDE_DATA #include #include /****************************************************************************/ // SCH_InitShm // // Alloc-time SHM init. /****************************************************************************/ void RDPCALL SCH_InitShm(void) { DC_BEGIN_FN("SCH_InitShm"); pddShm->sch.baCompressionEst = SCH_BA_INIT_EST; pddShm->sch.MPPCCompressionEst = SCH_MPPC_INIT_EST; DC_END_FN(); } /****************************************************************************/ // SCHEnoughOutputAccumulated // // Determine if there's enough output accumulated to make it worth sending // to the WD. /****************************************************************************/ __inline BOOL RDPCALL SCHEnoughOutputAccumulated(void) { BOOL rc = FALSE; UINT32 EstimatedTotal; DC_BEGIN_FN("SCHEnoughOutputAccumulated"); // We want to flush through to the WD if any of the following are true. // - new cursor shape (helps snappy feel) // - the estimated compressed size of the pending orders will fit into // a large order buffer (estimated to 7/8 of buffer size to increase // the chances of really fitting into the buffer after running through // jittery compression algorithms). EstimatedTotal = pddShm->oa.TotalOrderBytes + (BA_GetTotalBounds() * pddShm->sch.baCompressionEst / SCH_UNCOMP_BYTES) + (pddShm->pm.paletteChanged * (UINT32)FIELDOFFSET(TS_UPDATE_PALETTE_PDU, data.palette[0]) + (PM_NUM_8BPP_PAL_ENTRIES * sizeof(TS_COLOR))); // If we're using the MPPC compressor, take into account the predicted // compression ratio. if (pddShm->sch.schSlowLink) EstimatedTotal = EstimatedTotal * pddShm->sch.MPPCCompressionEst / SCH_UNCOMP_BYTES; if (EstimatedTotal >= (pddShm->sch.LargePackingSize * 7 / 8)) { INC_INCOUNTER(IN_SCH_OUTPUT); TRC_NRM((TB,"Enough output bytes - %u", EstimatedTotal)); rc = TRUE; } else if (CM_DDGetCursorStamp() != ddLastSentCursorStamp) { // If we're not shadowing, we optimize to only flush due to // cursor-shape-change when user input happened recently. if (NULL != pddShm->pShadowInfo || ddSchInputKickMode) { INC_INCOUNTER(IN_SCH_NEW_CURSOR); TRC_NRM((TB,"Changed cursor")); rc = TRUE; } else { TRC_NRM((TB,"Avoided changing cursor; not in InputKickMode")); } } DC_END_FN(); return rc; } /****************************************************************************/ // SCH_DDOutputAvailable // // Called to decide whether to send output to the WD. /****************************************************************************/ NTSTATUS RDPCALL SCH_DDOutputAvailable(PDD_PDEV ppdev, BOOL mustSend) { NTSTATUS status; TSHARE_DD_OUTPUT_IN outputIn; TSHARE_DD_OUTPUT_OUT outputOut; ULONG bytesReturned; BOOL IoctlNow, schedOnly; DC_BEGIN_FN("SCH_DDOutputAvailable"); INC_INCOUNTER(IN_SCH_OUT_ALL); ADD_INCOUNTER(IN_SCH_MUSTSEND, mustSend); TRC_DBG((TB, "Orders %d, mustSend? %s, scheduler mode %s (%d), %s", pddShm->oa.TotalOrderBytes, (mustSend ? "TRUE" : "FALSE"), ddSchCurrentMode == SCH_MODE_ASLEEP ? "Asleep" : ddSchCurrentMode == SCH_MODE_NORMAL ? "Normal" : ddSchCurrentMode == SCH_MODE_TURBO ? "Turbo" : "Unknown", ddSchCurrentMode, pddShm->sch.schSlowLink ? "slow link" : "fast link")); // This routine contains part of the key scheduling algorithm. // The intent is to IOCTL to the WD if any of the following are true: // - we have been told that we must send pending data immediately // - there is enough output to make it worthwhile // - the current SCH state is ASLEEP // If the scheduler is ASLEEP and it's a slow link, then we wake the // scheduler up but we don't do an actual send for performance reasons. if (mustSend || SCHEnoughOutputAccumulated()) { IoctlNow = TRUE; schedOnly = FALSE; TRC_DBG((TB, "Send data 'cos enough")); } else if (ddSchCurrentMode == SCH_MODE_ASLEEP) { INC_INCOUNTER(IN_SCH_ASLEEP); IoctlNow = TRUE; schedOnly = pddShm->sch.schSlowLink; TRC_DBG((TB, "Send data 'cos asleep: sched only: %d", schedOnly)); } else { IoctlNow = FALSE; schedOnly = FALSE; } // If we have decided to send something, do so now. Most often we have // nothing to do. if (!IoctlNow) { INC_INCOUNTER(IN_SCH_DO_NOTHING); status = STATUS_SUCCESS; } else { outputIn.forceSend = mustSend; outputIn.pFrameBuf = ppdev->pFrameBuf; outputIn.frameBufWidth = ddFrameBufX; outputIn.frameBufHeight = ddFrameBufY; outputIn.pShm = pddShm; outputIn.schedOnly = schedOnly; // Note the current cursor stamp for future reference. ddLastSentCursorStamp = CM_DDGetCursorStamp(); TRC_DBG((TB, "Send IOCtl to WD, bounds %d, orders %d, mustSend? %s", BA_GetTotalBounds(), pddShm->oa.TotalOrderBytes, (mustSend)? "TRUE":"FALSE")); // If we are not shadowing, then all output will be completely flushed // on this call. if (pddShm->pShadowInfo == NULL) { status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_OUTPUT_AVAILABLE, &outputIn, sizeof(TSHARE_DD_OUTPUT_IN), &outputOut, sizeof(TSHARE_DD_OUTPUT_OUT), &bytesReturned); } // else we are shadowing and may require multiple flush calls else { #ifdef DC_DEBUG unsigned NumRepetitions = 0; #endif do { TRC_DBG((TB, "Send IOCtl to WD, bounds %d, orders %d, mustSend? %s", BA_GetTotalBounds(), pddShm->oa.TotalOrderBytes, (mustSend)? "TRUE":"FALSE")); // The primary stack will update this to indicate how many bytes // were copied into the shadow data buffer. This will subsequently // be used by the shadow stack(s) to send the data to its client. pddShm->pShadowInfo->messageSize = 0; #ifdef DC_HICOLOR pddShm->pShadowInfo->messageSizeEx = 0; #endif status = EngFileIoControl(ddWdHandle, IOCTL_WDTS_DD_OUTPUT_AVAILABLE, &outputIn, sizeof(TSHARE_DD_OUTPUT_IN), &outputOut, sizeof(TSHARE_DD_OUTPUT_OUT), &bytesReturned); pddShm->pShadowInfo->messageSize = 0; #ifdef DC_HICOLOR pddShm->pShadowInfo->messageSizeEx = 0; #endif #ifdef DC_DEBUG // If we have a locked-up shadow session looping in sending // output, break out. We should only have to call to the // WD a few times, so make the check 250 to be safe. NumRepetitions++; if (NumRepetitions == 250) { TRC_ASSERT((NumRepetitions != 250), (TB,"We seem to be in an infinite output loop " "on shadow output flushing; TotalOrders=%u, " "TotalBounds=%u", pddShm->oa.TotalOrderBytes, pddShm->ba.totalArea)); } #endif } while ((pddShm->oa.TotalOrderBytes || BA_GetTotalBounds()) && (status == STATUS_SUCCESS) && !schedOnly); } // Update the new scheduler mode. ddSchCurrentMode = outputOut.schCurrentMode; ddSchInputKickMode = outputOut.schInputKickMode; TRC_DBG((TB, "New Scheduler mode is %s (%d)", ddSchCurrentMode == SCH_MODE_ASLEEP ? "Asleep" : ddSchCurrentMode == SCH_MODE_NORMAL ? "Normal" : ddSchCurrentMode == SCH_MODE_TURBO ? "Turbo" : "Unknown", ddSchCurrentMode)); } DC_END_FN(); return status; }