#include "precomp.h" // // OA.C // Order Accumulation, both cpi32 and display driver sides // // Copyright(c) Microsoft 1997- // // // OA_DDProcessRequest - see oa.h // BOOL OA_DDProcessRequest ( UINT fnEscape, LPOSI_ESCAPE_HEADER pRequest, DWORD cbRequest ) { BOOL rc; DebugEntry(OA_DDProcessRequest); // // Get the request number. // switch (fnEscape) { case OA_ESC_FLOW_CONTROL: { if (cbRequest != sizeof(OA_FLOW_CONTROL)) { ERROR_OUT(("OA_DDProcessRequest: Invalid size %d for OA_ESC_FLOW_CONTROL", cbRequest)); rc = FALSE; DC_QUIT; } // // Save new order accum throughput value // g_oaFlow = ((LPOA_FLOW_CONTROL)pRequest)->oaFlow; rc = TRUE; } break; default: { ERROR_OUT(("Unrecognized OA escape")); rc = FALSE; } break; } DC_EXIT_POINT: DebugExitBOOL(OA_DDProcessRequest, rc); return(rc); } // // // OA_DDAddOrder(..) // // Adds an order to the queue for transmission. // // If the new order is completetly covered by the current SDA then // it is spoilt. // // If the order is opaque and overlaps earlier orders it may clip // or spoil them. // // Called by the GDI interception code. // // void OA_DDAddOrder(LPINT_ORDER pNewOrder, void FAR * pExtraInfo) { RECT SDARects[BA_NUM_RECTS*2]; UINT cBounds; UINT spoilingBounds; UINT totalBounds; UINT i; RECT SrcRect; RECT tmpRect; BOOL gotBounds = FALSE; int dx; int dy; RECT IntersectedSrcRect; RECT InvalidDstRect; LPINT_ORDER pTmpOrder; LPEXTTEXTOUT_ORDER pExtTextOut; LPOA_SHARED_DATA lpoaShared; LPOA_FAST_DATA lpoaFast; DebugEntry(OA_DDAddOrder); lpoaShared = OA_SHM_START_WRITING; lpoaFast = OA_FST_START_WRITING; // // Accumulate order accumulation rate. We are interested in how // quickly orders are being added to the buffer, so that we can tell // DCS scheduling whether frequent sends are advisable // SHM_CheckPointer(&lpoaFast->ordersAccumulated); lpoaFast->ordersAccumulated++; // // If the order is a private one, then we just add it to the Order // List and return immediately. // // Private Orders are used to send bitmap cache information (bitmap // bits and color tables). // // Private Orders never spoil any others and must never be spoilt. // if (pNewOrder->OrderHeader.Common.fOrderFlags & OF_PRIVATE) { TRACE_OUT(("Add private order (%lx)", pNewOrder)); OADDAppendToOrderList(lpoaShared, pNewOrder); DC_QUIT; } // // If this order is spoilable and its is completely enclosed by the // current screen data area, we can spoil it. Unless... // // PM - Performance // // We have observed in usability testing that clipping orders always // degrades the end-user's perceived performance. This is because the // orders flow much faster than the screendata and tend to relate to // text, which is what the user really wants to see. For example, text // overwriting a bitmap will be delayed because we want to send the // bitmap as screendata. // // Also, word documents tend to contain sections of screendata due to // mismatched fonts, intelliquotes, spelling annotation, current line // memblit. Nothing we can do about this, but if we page down two or // three times, or down and up again we get an accumulation of the // screendata on all the pages spoiling the orders and the end result // is that we have to wait longer than we would if we had not spoiled // the orders. // // So, what we can do instead is leave the text orders in and overwrite // them with screendata when it gets through. However, to make this // really effective what we also do is convert any transparent text // (as WEB browsers tend to use) into opaque text on a default // background. // // if ((pNewOrder->OrderHeader.Common.fOrderFlags & OF_SPOILABLE) != 0) { // // Get the driver's current bounds. // BA_CopyBounds(SDARects, &cBounds, FALSE); gotBounds = TRUE; for (i = 0; i < cBounds ; i++) { if ( OADDCompleteOverlapRect(&pNewOrder->OrderHeader.Common.rcsDst, &(SDARects[i])) ) { // // The destination of the order is completely covered by // the SDA. Check for a text order. // pExtTextOut = (LPEXTTEXTOUT_ORDER)pNewOrder->abOrderData; if (pExtTextOut->type == ORD_EXTTEXTOUT_TYPE) { // // The order is going to be completely overwritten so // we can play around with it all we like. // Just make it opaque so the user can read it while // waiting for the screendata to follow on. // pExtTextOut->fuOptions |= ETO_OPAQUE; // // pExtTextOut->rectangle is a TSHR_RECT32 // pExtTextOut->rectangle.left = pNewOrder->OrderHeader.Common.rcsDst.left; pExtTextOut->rectangle.top = pNewOrder->OrderHeader.Common.rcsDst.top; pExtTextOut->rectangle.right = pNewOrder->OrderHeader.Common.rcsDst.right; pExtTextOut->rectangle.bottom = pNewOrder->OrderHeader.Common.rcsDst.bottom; TRACE_OUT(("Converted text order to opaque")); break; } else { TRACE_OUT(("Spoiling order by SDA")); OA_DDFreeOrderMem(pNewOrder); DC_QUIT; } } } } // // Pass the order onto the Bitmap Cache Controller to try to cache the // src bitmap. // if (ORDER_IS_MEMBLT(pNewOrder) || ORDER_IS_MEM3BLT(pNewOrder)) { if (!SBC_DDCacheMemScreenBlt(pNewOrder, pExtraInfo)) { // // The memory to screen blt cannot be handled as an order (the // source bitmap could not cached). Add the destination of the // blt into the SDA and discard the order. // TRACE_OUT(("Failed to cache mem->screen blt")); TRACE_OUT(("Add rect to SDA (%d,%d)(%d,%d)", pNewOrder->OrderHeader.Common.rcsDst.left, pNewOrder->OrderHeader.Common.rcsDst.top, pNewOrder->OrderHeader.Common.rcsDst.right, pNewOrder->OrderHeader.Common.rcsDst.bottom )); RECT_FROM_TSHR_RECT16(&tmpRect, pNewOrder->OrderHeader.Common.rcsDst); OA_DDFreeOrderMem(pNewOrder); BA_AddScreenData(&tmpRect); DC_QUIT; } else { TRACE_OUT(("Cached MEMBLT targetted at (%d,%d)(%d,%d)", pNewOrder->OrderHeader.Common.rcsDst.left, pNewOrder->OrderHeader.Common.rcsDst.top, pNewOrder->OrderHeader.Common.rcsDst.right, pNewOrder->OrderHeader.Common.rcsDst.bottom )); } } if (ORDER_IS_SCRBLT(pNewOrder)) { // // // Handle Screen to Screen (SS) bitblts. // // The basic plan // -------------- // // If the source of a screen to screen blt intersects with the // current SDA then we have to do some additional work because all // orders are always executed before the SDA is copied. This means // that the data within the SDA will not be available at the time // we want to do the SS blt. // // In this situation we adjust the SS blt to remove all overlap // from the src rectangle. The destination rectangle is adjusted // accordingly. The area removed from the destination rectangle is // added into the SDA. // // TRACE_OUT(("Handle SS blt(%lx)", pNewOrder)); // // Make the order non-spoilable because we don't want the adding // of screen data to delete the order. // pNewOrder->OrderHeader.Common.fOrderFlags &= ~OF_SPOILABLE; // // Calculate the src rect. // SrcRect.left = ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nXSrc; SrcRect.right = SrcRect.left + ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nWidth - 1; SrcRect.top = ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nYSrc; SrcRect.bottom = SrcRect.top + ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nHeight - 1; // // // ORIGINAL SCRBLT SCHEME // ---------------------- // // If the source rectangle intersects the current Screen Data Area // (SDA) then the src rectangle is modified so that no there is no // intersection with the SDA, and the dst rectangle adjusted // accordingly (this is the theory - in practice the operation // remains the same and we just adjust the dst clip rectangle). // The destination area that is removed is added into the SDA. // // The code works, but can result in more screen data being sent // than is required. // // e.g. // // Operation: // // SSSSSS DDDDDD // SSSSSS -> DDDDDD // SSSSSS DDDDDD // SxSSSS DDDDDD // // S - src rect // D - dst rect // x - SDA overlap // // The bottom edge of the blt is trimmed off, and the corresponding // destination area added into the SDA. // // SSSSSS DDDDDD // SSSSSS -> DDDDDD // SSSSSS DDDDDD // xxxxxx // // // // NEW SCRBLT SCHEME // ------------------ // // The new scheme does not modify the blt rectangles, and just // maps the SDA overlap to the destination rect and adds that // area back into the SDA. // // e.g. (as above) // // Operation: // // SSSSSS DDDDDD // SSSSSS -> DDDDDD // SSSSSS DDDDDD // SxSSSS DDDDDD // // S - src rect // D - dst rect // x - SDA overlap // // The blt operation remains the same, but the overlap area is // mapped to the destination rectangle and added into the SDA. // // SSSSSS DDDDDD // SSSSSS -> DDDDDD // SSSSSS DDDDDD // SxSSSS DxDDDD // // // This scheme results in a smaller SDA area. However, this scheme // does blt potentially invalid data to the destination - which // may briefly be visible at the remote machine (because orders // are replayed before Screen Data). This has not (yet) proved to // be a problem. // // The main benefit of the new scheme is when scrolling an area // that includes a small SDA. // // new old // scheme scheme // // AAAAAAAA AAAAAAAA AAAAAAAA // AAAAAAAA AAAxAAAA xxxxxxxx // AAAAAAAA scroll up 3 times -> AAAxAAAA xxxxxxxx // AAAAAAAA AAAxAAAA xxxxxxxx // AAAxAAAA AAAxAAAA xxxxxxxx // // // if (!gotBounds) { // // Get the driver's current bounds. // BA_CopyBounds(SDARects, &cBounds, FALSE); } // // Now get any bounds which the share core is currently processing. // We have to include these bounds when we are doing the above // processing to avoid a situation where the core grabs the screen // data from the source of a ScrBlt after the source has been // updated by another order. // // e.g. If there is no driver SDA, but the core is processing the // area marked 'c'... // // If we ignore the core SDA, we queue a ScrBlt order which does // the following. // // SSSSSS DDDDDD // SccccS -> DDDDDD // SccccS DDDDDD // SSSSSS DDDDDD // // However, if another order (marked 'N') is accumulated before // the core grabs the SDA, we end up with the shadow doing the // following // // SSSSSS DDDDDD // ScNNcS -> DDNNDD // ScNNcS DDNNDD // SSSSSS DDDDDD // // i.e. the new order gets copied to the destination of the ScrBlt. // So, the ScrBlt order must be processed as // // SSSSSS DDDDDD // SccccS -> DxxxxD // SccccS DxxxxD // SSSSSS DDDDDD // // BA_QuerySpoilingBounds(&SDARects[cBounds], &spoilingBounds); totalBounds = cBounds + spoilingBounds; // // // This is the new SCRBLT handler. // // for (i = 0; i < totalBounds ; i++) { if ( (SrcRect.left >= SDARects[i].left) && (SrcRect.right <= SDARects[i].right) && (SrcRect.top >= SDARects[i].top) && (SrcRect.bottom <= SDARects[i].bottom) ) { // // The src of the SS blt is completely within the SDA. We // must add in the whole destination rectangle into the SDA // and spoil the SS blt. // TRACE_OUT(("SS blt src within SDA - spoil it")); RECT_FROM_TSHR_RECT16(&tmpRect, pNewOrder->OrderHeader.Common.rcsDst); OA_DDFreeOrderMem(pNewOrder); BA_AddScreenData(&tmpRect); DC_QUIT; } // // Intersect the src rect with the SDA rect. // IntersectedSrcRect.left = max( SrcRect.left, SDARects[i].left ); IntersectedSrcRect.right = min( SrcRect.right, SDARects[i].right ); IntersectedSrcRect.top = max( SrcRect.top, SDARects[i].top ); IntersectedSrcRect.bottom = min( SrcRect.bottom, SDARects[i].bottom ); dx = ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nLeftRect - ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nXSrc; dy = ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nTopRect - ((LPSCRBLT_ORDER)&pNewOrder->abOrderData)->nYSrc; InvalidDstRect.left = IntersectedSrcRect.left + dx; InvalidDstRect.right = IntersectedSrcRect.right + dx; InvalidDstRect.top = IntersectedSrcRect.top + dy; InvalidDstRect.bottom = IntersectedSrcRect.bottom + dy; // // Intersect the invalid destination rectangle with the // destination clip rectangle. // InvalidDstRect.left = max( InvalidDstRect.left, pNewOrder->OrderHeader.Common.rcsDst.left ); InvalidDstRect.right = min( InvalidDstRect.right, pNewOrder->OrderHeader.Common.rcsDst.right ); InvalidDstRect.top = max( InvalidDstRect.top, pNewOrder->OrderHeader.Common.rcsDst.top ); InvalidDstRect.bottom = min( InvalidDstRect.bottom, pNewOrder->OrderHeader.Common.rcsDst.bottom ); if ( (InvalidDstRect.left <= InvalidDstRect.right) && (InvalidDstRect.top <= InvalidDstRect.bottom) ) { // // Add the invalid area into the SDA. // BA_AddScreenData(&InvalidDstRect); } } // for (i = 0; i < totalBounds ; i++) // // Make the order spoilable again (this assumes that all SS blts // are spoilable. // pNewOrder->OrderHeader.Common.fOrderFlags |= OF_SPOILABLE; } // if (ORDER_IS_SCRBLT(pNewOrder)) else if ((pNewOrder->OrderHeader.Common.fOrderFlags & OF_DESTROP) != 0) { // // This is the case where the output of the order depends on the // existing contents of the target area (e.g. an invert). // // What we have to do here is to add any parts of the destination // of this order which intersect the SDA which the share core is // processing to the driver SDA. The reason for this is the same // as the SCRBLT case - the share core may grab the data from the // screen after we have applied this order (e.g. after we have // inverted an area of the screen), then send the order as well // (re-inverting the area of the screen). // // Note that we only have to worry about the SDA which the share // core is processing - we can ignore the driver's SDA. // TRACE_OUT(("Handle dest ROP (%#.8lx)", pNewOrder)); BA_QuerySpoilingBounds(SDARects, &spoilingBounds); for (i = 0; i < spoilingBounds ; i++) { // // Intersect the dest rect with the share core SDA rect. // InvalidDstRect.left = max( SDARects[i].left, pNewOrder->OrderHeader.Common.rcsDst.left ); InvalidDstRect.right = min( SDARects[i].right, pNewOrder->OrderHeader.Common.rcsDst.right ); InvalidDstRect.top = max( SDARects[i].top, pNewOrder->OrderHeader.Common.rcsDst.top ); InvalidDstRect.bottom = min( SDARects[i].bottom, pNewOrder->OrderHeader.Common.rcsDst.bottom ); if ( (InvalidDstRect.left <= InvalidDstRect.right) && (InvalidDstRect.top <= InvalidDstRect.bottom) ) { // // Add the invalid area into the SDA. // TRACE_OUT(("Adding to SDA (%d,%d) (%d,%d)", InvalidDstRect.left, InvalidDstRect.top, InvalidDstRect.right, InvalidDstRect.bottom)); BA_AddScreenData(&InvalidDstRect); } } } // // Add the new order to the end of the Order List. // OADDAppendToOrderList(lpoaShared, pNewOrder); TRACE_OUT(("Append order(%lx) to list", pNewOrder)); // // Now see if this order spoils any existing orders // if ((pNewOrder->OrderHeader.Common.fOrderFlags & OF_SPOILER) != 0) { // // Its a spoiler, so try to spoil with it. // // We have to pass in the bounding rectangle of the order, and the // first order to try to spoil to OADDSpoilFromOrder. The first // order to try to spoil is the one before the new order. // RECT_FROM_TSHR_RECT16(&tmpRect, pNewOrder->OrderHeader.Common.rcsDst); pTmpOrder = COM_BasedListPrev(&lpoaShared->orderListHead, pNewOrder, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); OADDSpoilFromOrder(lpoaShared, pTmpOrder, &tmpRect); } // // This is where the Win95 product would call DCS_TriggerEarlyTimer. // DC_EXIT_POINT: OA_FST_STOP_WRITING; OA_SHM_STOP_WRITING; DebugExitVOID(OA_DDAddOrder); } // // // FUNCTION: OA_DDAllocOrderMem // // DESCRIPTION: // // Allocates memory for an internal order structure from our own private // Order Heap. // // Allocates any Additional Order Memory from global memory. A pointer to // the Additional Order Memory is stored within the allocated order's // header (pOrder->OrderHeader.pAdditionalOrderData). // // // PARAMETERS: // // cbOrderDataLength - length in bytes of the order data to be allocated // from the Order Heap. // // cbAdditionalOrderDataLength - length in bytes of additional order data // to be allocated from Global Memory. If this parameter is zero no // additional order memory is allocated. // // // RETURNS: // // A pointer to the allocated order memory. NULL if the memory allocation // failed. // // // LPINT_ORDER OA_DDAllocOrderMem(UINT cbOrderDataLength, UINT cbAdditionalOrderDataLength) { LPINT_ORDER pOrder = NULL; LPINT_ORDER pFirstOrder; LPINT_ORDER pTailOrder; RECT tferRect; int targetSize; UINT moveOffset; UINT moveBytes; LPINT_ORDER pColorTableOrder = NULL; LPBYTE pNextOrderPos; LPOA_SHARED_DATA lpoaShared; DebugEntry(OA_DDAllocOrderMem); lpoaShared = OA_SHM_START_WRITING; // // PM Performance // // Although turning order accumulation off does clear the pipe, ready // for us to get the screendata over the wire as soon as we can, it // actually hinders end-user responsiveness because they see a longer // interval when nothing is happening, rather than getting feedback // that we are busy and the whole thing taking longer! // // So, what we do when we fill up the order buffer is we discard half // the orders in the buffer, adding them to the screendata. In this // way we will always keep between 50 and 100% of the orders for the // final updates to the window, which hopefully will be what the user // really wants to see. // // If the orders keep coming then we will keep on accumulating some, // sending them, discarding others until things quiet down, at which // point we will flush out our order buffer. // // When we come to flush the order buffer we also spoil the early ones // against screendata, so that we only have the final set of orders to // replay. We control the size of this final non-spoiled set depending // on whether we are running over a high or low speed connection. // Also, if we did not encounter any back pressure during the session // then we do not purge any orders at all, preferring to send // everything we possibly can as orders. // // Note that this approach assumes that we do not spoil all orders // against screendata on the fly because that leads to us generally // sending out-of-data orders followed by up-to-date screendata, which // is exactly what we do not want to see. // // // // First check that we have not already exceeded our high water mark // recommended by flow control. If we have then purge half the queue // so we have space to accumulate the later, more valuable, orders // // Note that this does not guarantee that we will have less orders // accumulated than the limit set by flow control. However, if enough // orders are generated, we will come through this branch on each order // and finally reduce to below the imposed limit. // SHM_CheckPointer(&lpoaShared->totalOrderBytes); if (g_oaPurgeAllowed && (lpoaShared->totalOrderBytes > (DWORD)(g_oaFlow == OAFLOW_FAST ? OA_FAST_HEAP : OA_SLOW_HEAP))) { RECT aRects[BA_NUM_RECTS]; UINT numRects; UINT i; WARNING_OUT(("Purging orders; total 0x%08x is greater than heap 0x%08x", lpoaShared->totalOrderBytes, (g_oaFlow == OAFLOW_FAST ? OA_FAST_HEAP : OA_SLOW_HEAP))); // // // If we need to make room for the new order then purge half the // current queue. We do this so we end up with the most recent // orders on the queue, rather than the oldest. // SHM_CheckPointer(&lpoaShared->totalOrderBytes); targetSize = lpoaShared->totalOrderBytes / 2; TRACE_OUT(("Target size %ld", targetSize)); // // Iterate through the list until we have found the first order // beyond the limit to be destroyed. Once we have got this order, // we can shuffle the list over the useless orders. // SHM_CheckPointer(&lpoaShared->orderListHead); pOrder = COM_BasedListFirst(&lpoaShared->orderListHead, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); pTailOrder = (LPINT_ORDER)COM_BasedPrevListField(&lpoaShared->orderListHead); // // If we hit this condition, we have to have at least one order // pending, so these both must be non NULL. // SHM_CheckPointer(pOrder); SHM_CheckPointer(pTailOrder); TRACE_OUT(("Order 0x%08lx, tail 0x%08lx", pOrder, pTailOrder)); // // Disable spoiling of existing orders by screen data while we do // the purge otherwise we may try to spoil an order which we are // purging ! // g_baSpoilByNewSDAEnabled = FALSE; while ((pOrder != NULL) && (targetSize > 0)) { // // Can't check at end; COM_BasedListNext may return NULL and // SHM_CheckPointer doesn't like that. // SHM_CheckPointer(pOrder); // // Check to see if this is an internal color table order. If // it is, the OF_INTERNAL flag will be set. // // MemBlt orders rely on being preceeded by a color table order // to set up the colors correctly. If we purge all the color // table orders, the following Mem(3)Blts will get the wrong // colors. So, we have to keep track of the last color table // order to be purged and then add it back into the order heap // later. // if ((pOrder->OrderHeader.Common.fOrderFlags & OF_INTERNAL) != 0) { TRACE_OUT(("Found color table order at %#.8lx", pOrder)); pColorTableOrder = pOrder; } else { // // Add the order to the Screen Data Area // TRACE_OUT(("Purging orders. Add rect to SDA (%d,%d)(%d,%d)", pOrder->OrderHeader.Common.rcsDst.left, pOrder->OrderHeader.Common.rcsDst.top, pOrder->OrderHeader.Common.rcsDst.right, pOrder->OrderHeader.Common.rcsDst.bottom)); RECT_FROM_TSHR_RECT16(&tferRect, pOrder->OrderHeader.Common.rcsDst); BA_AddScreenData(&tferRect); } // // Keep track of how much data still needs removing. // targetSize -= INT_ORDER_SIZE(pOrder); lpoaShared->totalHeapOrderBytes -= INT_ORDER_SIZE(pOrder); lpoaShared->totalOrderBytes -= MAX_ORDER_SIZE(pOrder); // // If the order is a Mem(3)Blt, we have to tell SBC that we are // getting rid of it. // if (ORDER_IS_MEMBLT(pOrder) || ORDER_IS_MEM3BLT(pOrder)) { SBC_DDOrderSpoiltNotification(pOrder); } // // Get the next order to be removed. // pOrder = COM_BasedListNext(&lpoaShared->orderListHead, pOrder, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); } TRACE_OUT(("Stopped at order %#.8lx", pOrder)); // // Orders have been transferred to SDA, so now we have to // - move the last purged color table order (if there is one) to // the start of the order heap // - shuffle up the heap // - reset the pointers. // // pOrder points to the first non-purged order. // if (pOrder != NULL) { SHM_CheckPointer(&lpoaShared->orderHeap); SHM_CheckPointer(&lpoaShared->orderListHead); pNextOrderPos = lpoaShared->orderHeap; // // If we purged (at least) one color table order, move the last // color table order to the start of the order heap. // if (pColorTableOrder != NULL) { TRACE_OUT(("Moving color table from %#.8lx to start", pColorTableOrder)); RtlMoveMemory(pNextOrderPos, pColorTableOrder, INT_ORDER_SIZE(pColorTableOrder)); pColorTableOrder = (LPINT_ORDER)pNextOrderPos; lpoaShared->totalHeapOrderBytes += INT_ORDER_SIZE(pColorTableOrder); lpoaShared->totalOrderBytes += MAX_ORDER_SIZE(pColorTableOrder); pNextOrderPos += INT_ORDER_SIZE(pColorTableOrder); // // Chain the order into the start of the order list. Just // do the pointers to and from the list head for now, we // will do the rest later. // lpoaShared->orderListHead.next = PTRBASE_TO_OFFSET(pColorTableOrder, &lpoaShared->orderListHead); pColorTableOrder->OrderHeader.list.prev = PTRBASE_TO_OFFSET(&lpoaShared->orderListHead, pColorTableOrder); } // // Move the heap up to the top of the buffer. The following // diagram illustrates how the order heap is split up at the // moment. // // OA_SHM_NEXTORDER // |<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>| // // moveOffset moveBytes // |<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>|<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>| // // ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» // º ³ ³ ³ º // º ³ purged ³ remaining ³ unused º // º ³ orders ³ orders ³ º // º ³ ³ ³ ³ º // ÈÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ // ^ ³ ^ ^ // ³ ³ ³ ³ // ³ ³ ³ ³ // ³ ³ ³ ÀÄÄ pOrder // ³ ³ ³ // ³ ³ ÀÄÄÄ pNextOrderPos // ³ ³ // ³ ÀÄÄÄÄÄ color table order // ³ // ÀÄÄÄÄÄÄÄ lpoaShared->orderHeap (pColorTableOrder) // // If there is no color table order, pNextOrderPos is equal to // lpoaShared->orderHeap. // // moveOffset is the number of bytes to move the remaining // orders by. // // moveBytes is the number of bytes to be moved. // // moveOffset = (UINT)((UINT_PTR)pOrder - (UINT_PTR)pNextOrderPos); moveBytes = lpoaShared->nextOrder - moveOffset - (pNextOrderPos - lpoaShared->orderHeap); TRACE_OUT(("Moving %d bytes", moveBytes)); RtlMoveMemory(pNextOrderPos, pOrder, moveBytes); // // Update the head and tail pointers to reflect their new // positions. // pFirstOrder = (LPINT_ORDER)pNextOrderPos; pTailOrder = (LPINT_ORDER)((LPBYTE)pTailOrder - moveOffset); SHM_CheckPointer(pFirstOrder); SHM_CheckPointer(pTailOrder); TRACE_OUT(("New first unpurged %#.8lx, tail %#.8lx", pFirstOrder, pTailOrder)); // // Since the offsets are relative to the order pointer, we only // need to modify the start and end offsets. // // Unfortunately, the possibility of a color table order at the // start of the heap complicates the chaining of pFirstOrder. // If there is a color table order, we chain pFirstOrder to the // color table order, otherwise we chain it to the start of the // order list. // lpoaShared->orderListHead.prev = PTRBASE_TO_OFFSET(pTailOrder, &lpoaShared->orderListHead); pTailOrder->OrderHeader.list.next = PTRBASE_TO_OFFSET(&lpoaShared->orderListHead, pTailOrder); if (pColorTableOrder != NULL) { pColorTableOrder->OrderHeader.list.next = PTRBASE_TO_OFFSET(pFirstOrder, pColorTableOrder); pFirstOrder->OrderHeader.list.prev = PTRBASE_TO_OFFSET(pColorTableOrder, pFirstOrder); } else { lpoaShared->orderListHead.next = PTRBASE_TO_OFFSET(pFirstOrder, &lpoaShared->orderListHead); pFirstOrder->OrderHeader.list.prev = PTRBASE_TO_OFFSET(&lpoaShared->orderListHead, pFirstOrder); } // // Sort out where the next order to be allocated will go // lpoaShared->nextOrder -= moveOffset; } else { // // No orders left - this happens if we've had lots of spoiling. // We have now cleared out all the valid orders so let's // re-initialise the heap for next time. // OA_DDResetOrderList(); } // // Now re-enable the spoiling of orders by SDA. // g_baSpoilByNewSDAEnabled = TRUE; WARNING_OUT(("Purged orders, total is now 0x%08x", lpoaShared->totalOrderBytes)); // // Lastly, spoil the remaining orders by the screen data. // If we've gotten this far, there's a lot of data being sent // and/or we're slow. So nuke 'em. // BA_CopyBounds(aRects, &numRects, FALSE); for (i = 0; i < numRects; i++) { OA_DDSpoilOrdersByRect(aRects+i); } WARNING_OUT(("Spoiled remaining orders by SDA, total is now 0x%08x", lpoaShared->totalOrderBytes)); TRACE_OUT(("Next 0x%08lx", lpoaShared->nextOrder)); TRACE_OUT(("Head 0x%08lx", lpoaShared->orderListHead.next)); TRACE_OUT(("Tail 0x%08lx", lpoaShared->orderListHead.prev)); TRACE_OUT(("Total heap bytes 0x%08lx", lpoaShared->totalHeapOrderBytes)); TRACE_OUT(("Total order bytes 0x%08lx", lpoaShared->totalOrderBytes)); } pOrder = OADDAllocOrderMemInt(lpoaShared, cbOrderDataLength, cbAdditionalOrderDataLength); if ( pOrder != NULL ) { // // Update the count of total order data. // SHM_CheckPointer(&lpoaShared->totalHeapOrderBytes); lpoaShared->totalHeapOrderBytes += sizeof(INT_ORDER_HEADER) + cbOrderDataLength; SHM_CheckPointer(&lpoaShared->totalAdditionalOrderBytes); lpoaShared->totalAdditionalOrderBytes += cbAdditionalOrderDataLength; } TRACE_OUT(("Alloc order, addr %lx, size %u", pOrder, cbOrderDataLength)); OA_SHM_STOP_WRITING; DebugExitPVOID(OA_DDAllocOrderMem, pOrder); return(pOrder); } // // // FUNCTION: OA_DDFreeOrderMem // // // DESCRIPTION: // // Frees order memory from our own private heap. // Frees any Additional Order Memory associated with this order. // // // PARAMETERS: // // pOrder - pointer to the order to be freed. // // // RETURNS: // // Nothing. // // void OA_DDFreeOrderMem(LPINT_ORDER pOrder) { LPOA_SHARED_DATA lpoaShared; DebugEntry(OA_DDFreeOrderMem); ASSERT(pOrder); lpoaShared = OA_SHM_START_WRITING; TRACE_OUT(("Free order %lx", pOrder)); // // Update the data totals. // SHM_CheckPointer(&lpoaShared->totalHeapOrderBytes); lpoaShared->totalHeapOrderBytes -= (sizeof(INT_ORDER_HEADER) + pOrder->OrderHeader.Common.cbOrderDataLength); SHM_CheckPointer(&lpoaShared->totalAdditionalOrderBytes); lpoaShared->totalAdditionalOrderBytes -= pOrder->OrderHeader.cbAdditionalOrderDataLength; // // Do the work. // OADDFreeOrderMemInt(lpoaShared, pOrder); OA_SHM_STOP_WRITING; DebugExitVOID(OA_DDFreeOrderMem); } // // // FUNCTION: OA_DDResetOrderList // // // DESCRIPTION: // // Frees all Orders and Additional Order Data in the Order List. // Frees up the Order Heap memory. // // // PARAMETERS: // // None. // // // RETURNS: // // Nothing. // // void OA_DDResetOrderList(void) { LPOA_SHARED_DATA lpoaShared; DebugEntry(OA_DDResetOrderList); lpoaShared = OA_SHM_START_WRITING; // // First free all the orders on the list. // OADDFreeAllOrders(lpoaShared); // // Ensure that the list pointers are NULL. // SHM_CheckPointer(&lpoaShared->orderListHead); if ((lpoaShared->orderListHead.next != 0) || (lpoaShared->orderListHead.prev != 0)) { ERROR_OUT(("Non-NULL list pointers (%lx)(%lx)", lpoaShared->orderListHead.next, lpoaShared->orderListHead.prev)); SHM_CheckPointer(&lpoaShared->orderListHead); COM_BasedListInit(&lpoaShared->orderListHead); } OA_SHM_STOP_WRITING; DebugExitVOID(OA_DDResetOrderList); } // // OA_DDSyncUpdatesNow // // Called when a sync operation is required. // // Discards all outstanding orders. // void OA_DDSyncUpdatesNow(void) { DebugEntry(OA_SyncUpdatesNow); OADDFreeAllOrders(g_poaData[g_asSharedMemory->displayToCore.currentBuffer]); DebugExitVOID(OA_DDSyncUpdatesNow); } // // // OA_DDRemoveListOrder(..) // // Removes the specified order from the Order List by marking it as spoilt. // // Returns: // Pointer to the order following the removed order. // // LPINT_ORDER OA_DDRemoveListOrder(LPINT_ORDER pCondemnedOrder) { LPINT_ORDER pSaveOrder; LPOA_SHARED_DATA lpoaShared; DebugEntry(OA_DDRemoveListOrder); TRACE_OUT(("Remove list order (%lx)", pCondemnedOrder)); lpoaShared = OA_SHM_START_WRITING; SHM_CheckPointer(pCondemnedOrder); // // Check for a valid order. // if (pCondemnedOrder->OrderHeader.Common.fOrderFlags & OF_SPOILT) { TRACE_OUT(("Invalid order")); DC_QUIT; } // // Get the offset value of this order. // SHM_CheckPointer(&lpoaShared->orderHeap); // // Mark the order as spoilt. // pCondemnedOrder->OrderHeader.Common.fOrderFlags |= OF_SPOILT; // // Update the count of bytes currently in the Order List. // SHM_CheckPointer(&lpoaShared->totalOrderBytes); lpoaShared->totalOrderBytes -= (UINT)MAX_ORDER_SIZE(pCondemnedOrder); // // SAve the order so we can remove it from the linked list after having // got the next element in the chain. // pSaveOrder = pCondemnedOrder; // // Return the next order in the list. // SHM_CheckPointer(&lpoaShared->orderListHead); pCondemnedOrder = COM_BasedListNext(&lpoaShared->orderListHead, pCondemnedOrder, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); if (pSaveOrder == pCondemnedOrder) { ERROR_OUT(("Order list has gone circular !")); } // // Delete the unwanted order from the linked list. // COM_BasedListRemove(&pSaveOrder->OrderHeader.list); // // Check that the list is still consistent with the total number of // order bytes. // if ( (lpoaShared->orderListHead.next != 0) && (lpoaShared->orderListHead.prev != 0) && (lpoaShared->totalOrderBytes == 0) ) { ERROR_OUT(("List head wrong: %ld %ld", lpoaShared->orderListHead.next, lpoaShared->orderListHead.prev)); COM_BasedListInit(&lpoaShared->orderListHead); pCondemnedOrder = NULL; } DC_EXIT_POINT: OA_SHM_STOP_WRITING; DebugExitPVOID(OA_DDRemoveListOrder, pCondemnedOrder); return(pCondemnedOrder); } // // OA_DDSpoilOrdersByRect - see oa.h // void OA_DDSpoilOrdersByRect(LPRECT pRect) { LPOA_SHARED_DATA lpoaShared; LPINT_ORDER pOrder; DebugEntry(OA_DDSpoilOrdersByRect); lpoaShared = OA_SHM_START_WRITING; // // We want to start spoiling from the newest order i.e. the one at the // end of the order list. // pOrder = COM_BasedListLast(&lpoaShared->orderListHead, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); if (pOrder != NULL) { OADDSpoilFromOrder(lpoaShared, pOrder, pRect); } OA_SHM_STOP_WRITING; DebugExitVOID(OA_DDSpoilOrdersByRect); } // // // OADDAppendToOrderList(..) // // Commits an allocated order to the end of the Order List. The order must // NOT be freed once it has been added. The whole list must be invalidated // to free the committed orders. // // void OADDAppendToOrderList(LPOA_SHARED_DATA lpoaShared, LPINT_ORDER pNewOrder) { DebugEntry(OADDAppendToOrderList); // // Chain entry is already set up so all we do is keep track of // committed orders. // // // Store the total number of order bytes used. // SHM_CheckPointer(&lpoaShared->totalOrderBytes); lpoaShared->totalOrderBytes += (UINT)MAX_ORDER_SIZE(pNewOrder); DebugExitVOID(OADDAppendToOrderList); } // // // FUNCTION: OADDAllocOrderMemInt // // DESCRIPTION: // // Allocates memory for an internal order structure from our order heap. // // // PARAMETERS: // // cbOrderDataLength - length in bytes of the order data to be allocated // from the Order Heap. // // cbAdditionalOrderDataLength - length in bytes of additional order data // to be allocated. If this parameter is zero no additional order memory // is allocated. // // // RETURNS: // // A pointer to the allocated order memory. NULL if the memory allocation // failed. // // // LPINT_ORDER OADDAllocOrderMemInt ( LPOA_SHARED_DATA lpoaShared, UINT cbOrderDataLength, UINT cbAdditionalOrderDataLength ) { LPINT_ORDER pOrder = NULL; UINT cbOrderSize; DebugEntry(OADDAllocOrderMemInt); // // If the additional data will take us over our Additional Data Limit // then fail the memory allocation. // SHM_CheckPointer(&lpoaShared->totalAdditionalOrderBytes); if ((lpoaShared->totalAdditionalOrderBytes + cbAdditionalOrderDataLength) > MAX_ADDITIONAL_DATA_BYTES) { TRACE_OUT(("Hit Additional Data Limit, current %lu addint %u", lpoaShared->totalAdditionalOrderBytes, cbAdditionalOrderDataLength)); DC_QUIT; } // // Calculate the number of bytes we need to allocate (including the // order header). Round up to the nearest 4 bytes to keep the 4 byte // alignment for the next order. // cbOrderSize = sizeof(INT_ORDER_HEADER) + cbOrderDataLength; cbOrderSize = (cbOrderSize + 3) & 0xFFFFFFFC; // // Make sure we don't overrun our heap limit // SHM_CheckPointer(&lpoaShared->nextOrder); if (lpoaShared->nextOrder + cbOrderSize > OA_HEAP_MAX) { TRACE_OUT(("Heap limit hit")); DC_QUIT; } // // Construct a far pointer to the allocated memory, and fill in the // length field in the Order Header. // SHM_CheckPointer(&lpoaShared->orderHeap); pOrder = (LPINT_ORDER)(lpoaShared->orderHeap + lpoaShared->nextOrder); pOrder->OrderHeader.Common.cbOrderDataLength = (TSHR_UINT16)cbOrderDataLength; // // Update the order header to point to the next section of free heap. // SHM_CheckPointer(&lpoaShared->nextOrder); lpoaShared->nextOrder += cbOrderSize; // // Allocate any Additional Order Memory from Global Memory. // if (cbAdditionalOrderDataLength > 0) { // // Make sure we don't overrun our heap limit // SHM_CheckPointer(&lpoaShared->nextOrder); if (lpoaShared->nextOrder + cbAdditionalOrderDataLength > OA_HEAP_MAX) { TRACE_OUT(("Heap limit hit for additional data")); // // Clear the allocated order and quit. // SHM_CheckPointer(&lpoaShared->nextOrder); lpoaShared->nextOrder -= cbOrderSize; pOrder = NULL; DC_QUIT; } // // Store the space for the additional data. // SHM_CheckPointer(&lpoaShared->nextOrder); pOrder->OrderHeader.additionalOrderData = lpoaShared->nextOrder; pOrder->OrderHeader.cbAdditionalOrderDataLength = (WORD)cbAdditionalOrderDataLength; // // Update the next order pointer to point to the next 4-byte // boundary. // SHM_CheckPointer(&lpoaShared->nextOrder); lpoaShared->nextOrder += cbAdditionalOrderDataLength + 3; lpoaShared->nextOrder &= 0xFFFFFFFC; } else { pOrder->OrderHeader.additionalOrderData = 0; pOrder->OrderHeader.cbAdditionalOrderDataLength = 0; } // // Create the chain entry. // SHM_CheckPointer(&lpoaShared->orderListHead); COM_BasedListInsertBefore(&lpoaShared->orderListHead, &pOrder->OrderHeader.list); DC_EXIT_POINT: DebugExitPVOID(OADDAllocOrderMemInit, pOrder); return(pOrder); } // // // FUNCTION: OADDFreeOrderMemInt // // // DESCRIPTION: // // Frees order memory from our orders heap. Frees any Additional Order // Memory associated with this order. This must NOT be used on an order // that has been committed to the order list. // // // PARAMETERS: // // pOrder - pointer to the order to be freed. // // // RETURNS: // // Nothing. // // void OADDFreeOrderMemInt(LPOA_SHARED_DATA lpoaShared, LPINT_ORDER pOrder) { LPINT_ORDER pOrderTail; DebugEntry(OADDFreeOrderMemInt); // // The order heap is real a misnomer. We know that the memory is only // allocated in a purely sequential manner and deallocated as one large // lump of memory. // // So we do not need to implement a full memory heap allocation // mechanism. Instead, we just need to keep track of where the // previous high water mark was before this order was freed. // // // Find the tail of the current chain. // pOrderTail = COM_BasedListLast(&lpoaShared->orderListHead, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); SHM_CheckPointer(pOrderTail); // // We wont necessarily be freeing the last item in the order heap. // if (pOrder == pOrderTail) { // // This is the last item in the heap, so we can set the pointer to // the next order to be used back to the start of the order being // freed. // SHM_CheckPointer(&lpoaShared->nextOrder); lpoaShared->nextOrder = (LONG)((LPBYTE)pOrder - (LPBYTE)(lpoaShared->orderHeap)); } else { // // This is not the last item in the heap - we must not reset the // pointer to the next item to be used. // TRACE_OUT(("Not resetting next order (not last item in heap)")); } // // Delete the item from the chain. // COM_BasedListRemove(&pOrder->OrderHeader.list); DebugExitVOID(OADDFreeOrderMemInt); } // // OADDFreeAllOrders // // Free the all the individual orders on the orders list, without // discarding the list itself. // void OADDFreeAllOrders(LPOA_SHARED_DATA lpoaShared) { DebugEntry(OADDFreeAllOrders); // // Simply clear the list head. // COM_BasedListInit(&lpoaShared->orderListHead); SHM_CheckPointer(&lpoaShared->orderListHead); lpoaShared->totalHeapOrderBytes = 0; lpoaShared->totalOrderBytes = 0; lpoaShared->totalAdditionalOrderBytes = 0; lpoaShared->nextOrder = 0; DebugExitVOID(OADDFreeAllOrders); } // // // OADDOrderIsValid(..) // // Determines if a pointer points to a valid order. // // Returns: // TRUE if valid order, FALSE if invalid. // // BOOL OADDOrderIsValid(LPINT_ORDER pOrder) { BOOL rc; DebugEntry(OADDOrderIsValid); // // Check the order is not already spoilt // rc = ((pOrder->OrderHeader.Common.fOrderFlags & OF_SPOILT) == 0); DebugExitBOOL(OADDOrderIsValid, rc); return(rc); } BOOL OADDCompleteOverlapRect(LPTSHR_RECT16 prcsSrc, LPRECT prcsOverlap) { // // Return TRUE if the source is completely enclosed by the overlap // rectangle. // return( (prcsSrc->left >= prcsOverlap->left) && (prcsSrc->right <= prcsOverlap->right) && (prcsSrc->top >= prcsOverlap->top) && (prcsSrc->bottom <= prcsOverlap->bottom) ); } // // Name: OADDSpoilFromOrder // // Purpose: Remove any orders from the order heap which should be spoiled // by a given rectangle.. // // Returns: Nothing // // Params: IN pTargetOrder - Pointer to the first order to try to // spoil. // IN pRect - Pointer to the spoiling rectangle. // // Operation: pTargetOrder may be spoiled by this function, so be careful // on return. // void OADDSpoilFromOrder ( LPOA_SHARED_DATA lpoaShared, LPINT_ORDER pTargetOrder, LPRECT pSpoilRect ) { UINT nonProductiveScanDepth = 0; UINT scanExitDepth; BOOL reachedBlocker = FALSE; DebugEntry(OADDSpoilFromOrder); TRACE_OUT(("Spoiling rect is {%d, %d, %d, %d}", pSpoilRect->left, pSpoilRect->top, pSpoilRect->right, pSpoilRect->bottom)); // // Work out how deep we will scan if the spoiling is non-productive. // We go further for bigger orders over PSTN. (ie Irrespective of the // bandwidth we do not want to do much work when the app is blasting // out a lot of single pel orders!) // if (((pSpoilRect->right - pSpoilRect->left) < FULL_SPOIL_WIDTH) && ((pSpoilRect->bottom - pSpoilRect->top) < FULL_SPOIL_HEIGHT)) { TRACE_OUT(("Small order so reducing spoil depth")); scanExitDepth = OA_FAST_SCAN_DEPTH; } else { // // Use the current default scan depth (this is based on the // current network throughput). // scanExitDepth = (g_oaFlow == OAFLOW_FAST) ? OA_FAST_SCAN_DEPTH : OA_SLOW_SCAN_DEPTH; } // // Loop backwards from the base order until we have one of the // following occurs. // - We spoil all the preceeding orders. // - We reach a blocker which we can't spoil. // - We find scanExitDepth orders which we can't spoil. // while ((pTargetOrder != NULL) && !reachedBlocker && (nonProductiveScanDepth < scanExitDepth)) { // // We do not exit immediately when we reach a blocker because it is // possible that we will spoil it. If we do spoil it, then we can // quite happily try spoiling the orders which preceed it. // // So, just set a flag here which we will reset if we spoil the // order. // reachedBlocker = ((pTargetOrder->OrderHeader.Common.fOrderFlags & OF_BLOCKER) != 0); // // Only try to spoil spoilable orders. // if ((pTargetOrder->OrderHeader.Common.fOrderFlags & OF_SPOILABLE) != 0) { if (OADDCompleteOverlapRect( &pTargetOrder->OrderHeader.Common.rcsDst, pSpoilRect)) { // // The order can be spoilt. If the order is a MemBlt or a // Mem3Blt, we have to notify SBC to allow it to free up // associated data. // if (ORDER_IS_MEMBLT(pTargetOrder) || ORDER_IS_MEM3BLT(pTargetOrder)) { SBC_DDOrderSpoiltNotification(pTargetOrder); } TRACE_OUT(("Spoil by order (%hd, %hd) (%hd, %hd)", pTargetOrder->OrderHeader.Common.rcsDst.left, pTargetOrder->OrderHeader.Common.rcsDst.top, pTargetOrder->OrderHeader.Common.rcsDst.right, pTargetOrder->OrderHeader.Common.rcsDst.bottom)); pTargetOrder = OA_DDRemoveListOrder(pTargetOrder); // // Reset the blocker flag - we spoiled the order, so if it // was a blocker we can now try to spoil earlier orders. // reachedBlocker = FALSE; } else { nonProductiveScanDepth++; } } else { nonProductiveScanDepth++; } // // Get the previous order in the list. We have to be careful // because we may have just removed the last item in the list, in // which case pTargetOrder will be NULL. // if (pTargetOrder == NULL) { pTargetOrder = COM_BasedListLast(&lpoaShared->orderListHead, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); } else { pTargetOrder = COM_BasedListPrev(&lpoaShared->orderListHead, pTargetOrder, FIELD_OFFSET(INT_ORDER, OrderHeader.list)); } } DebugExitVOID(OADDSpoilFromOrder); }