Sample Code

Windows Driver Samples/ Kernel mode display-only miniport driver (KMDOD) sample/ C++/ blthw.cxx/

/******************************Module*Header*******************************\
* Module Name: blthw.cxx
*
* Sample display driver functions for a HW blt simulation. This file is 
* only provided to simulate how a real hardware-accelerated display-only 
* driver functions, and should not be used in a real driver.
*
* Copyright (c) 2011 Microsoft Corporation
\**************************************************************************/

#include "BDD.hxx"

typedef struct
{
    CONST DXGKRNL_INTERFACE*        DxgkInterface;
    DXGKARGCB_NOTIFY_INTERRUPT_DATA NotifyInterrupt;
} SYNC_NOTIFY_INTERRUPT;

KSYNCHRONIZE_ROUTINE SynchronizeVidSchNotifyInterrupt;

BOOLEAN SynchronizeVidSchNotifyInterrupt(_In_opt_ PVOID params)
{
    // This routine is non-paged code called at the device interrupt level (DIRQL)
    // to notify VidSch and schedule a DPC. It is meant as a demonstration of handling 
    // a real hardware interrupt, even though it is actually called from asynchronous 
    // present worker threads in this sample.
    SYNC_NOTIFY_INTERRUPT* pParam = reinterpret_cast<SYNC_NOTIFY_INTERRUPT*>(params);

    // The context is known to be non-NULL
    __analysis_assume(pParam != NULL);

    // Update driver information related to fences
    switch(pParam->NotifyInterrupt.InterruptType)
    {
    case DXGK_INTERRUPT_DISPLAYONLY_VSYNC:
    case DXGK_INTERRUPT_DISPLAYONLY_PRESENT_PROGRESS:
        break;
    default:
        NT_ASSERT(FALSE);
        return FALSE;
    }

    // Callback OS to report about the interrupt
    pParam->DxgkInterface->DxgkCbNotifyInterrupt(pParam->DxgkInterface->DeviceHandle,&pParam->NotifyInterrupt);

    // Now queue a DPC for this interrupt (to callback schedule at DCP level and let it do more work there)
    // DxgkCbQueueDpc can return FALSE if there is already a DPC queued
    // this is an acceptable condition
    pParam->DxgkInterface->DxgkCbQueueDpc(pParam->DxgkInterface->DeviceHandle);

    return TRUE;
}

#pragma code_seg("PAGE")

KSTART_ROUTINE HwContextWorkerThread;

struct DoPresentMemory
{
    PVOID                     DstAddr;
    UINT                      DstStride;
    ULONG                     DstBitPerPixel;
    UINT                      SrcWidth;
    UINT                      SrcHeight;
    BYTE*                     SrcAddr;
    LONG                      SrcPitch;
    ULONG                     NumMoves;             // in:  Number of screen to screen moves
    D3DKMT_MOVE_RECT*         Moves;               // in:  Point to the list of moves
    ULONG                     NumDirtyRects;        // in:  Number of direct rects
    RECT*                     DirtyRect;           // in:  Point to the list of dirty rects
    D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation;
    BOOLEAN                   SynchExecution;
    D3DDDI_VIDEO_PRESENT_SOURCE_ID  SourceID;
    HANDLE                    hAdapter;
    PMDL                      Mdl;
    BDD_HWBLT*                DisplaySource;
};

void
HwExecutePresentDisplayOnly(
    HANDLE Context);

NTSTATUS
StartHwBltPresentWorkerThread(
    _In_ PKSTART_ROUTINE StartRoutine,
    _In_ _When_(return==0, __drv_aliasesMem) PVOID StartContext)
/*++

  Routine Description:

    This routine creates the worker thread to execute a single present 
    command. Creating a new thread on every asynchronous present is not 
    efficient, but this file is only meant as a simulation, not an example 
    of implementation.

  Arguments:

    StartRoutine - start routine
    StartContext - start context

  Return Value:

    Status

--*/
{
    PAGED_CODE();

    OBJECT_ATTRIBUTES ObjectAttributes;
    InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
    HANDLE hWorkerThread = NULL;

    // copy data from the context which is need here, as it will be deleted in separate thread
    DoPresentMemory* ctx = reinterpret_cast<DoPresentMemory*>(StartContext);
    BDD_HWBLT* displaySource = ctx->DisplaySource;

    NTSTATUS Status = PsCreateSystemThread(
        &hWorkerThread,
        THREAD_ALL_ACCESS,
        &ObjectAttributes,
        NULL,
        NULL,
        StartRoutine,
        StartContext);

    if (!NT_SUCCESS(Status))
    {
        return Status;
    }


    // wait for thread to start - infinite wait -
    // need to make sure the tread is running before OS stars submitting the work items to it
    KeWaitForSingleObject(&displaySource->m_hThreadStartupEvent, Executive, KernelMode, FALSE, NULL);

    // Handle is passed to the parent object which must close it
    displaySource->SetPresentWorkerThreadInfo(hWorkerThread);

    // Resume context thread, this is done by setting the event the thread is waiting on
    KeSetEvent(&displaySource->m_hThreadSuspendEvent, 0, FALSE);

    return STATUS_PENDING;
}

BDD_HWBLT::BDD_HWBLT():m_DevExt (NULL),
                m_SynchExecution(TRUE),
                m_hPresentWorkerThread(NULL),
                m_pPresentWorkerThread(NULL)
{
    PAGED_CODE();

    KeInitializeEvent(&m_hThreadStartupEvent, NotificationEvent, FALSE);
    KeInitializeEvent(&m_hThreadSuspendEvent, SynchronizationEvent, FALSE);

}


BDD_HWBLT::~BDD_HWBLT()
/*++

  Routine Description:

    This routine waits on present worker thread to exit before
    destroying the object

  Arguments:

    None

  Return Value:

    None

--*/
{
    PAGED_CODE();

    // make sure the worker thread has exited
    SetPresentWorkerThreadInfo(NULL);
}

#pragma warning(push)
#pragma warning(disable:26135) // The function doesn't lock anything

void
BDD_HWBLT::SetPresentWorkerThreadInfo(
    HANDLE hWorkerThread)
/*++

  Routine Description:

    The method is updating present worker information
    It is called in following cases:
     - In ExecutePresent to update worker thread information
     - In Dtor to wait on worker thread to exit

  Arguments:

    hWorkerThread - handle of the present worker thread

  Return Value:

    None

--*/
{

    PAGED_CODE();

    if (m_pPresentWorkerThread)
    {
        // Wait for thread to exit
        KeWaitForSingleObject(m_pPresentWorkerThread, Executive, KernelMode,
                              FALSE, NULL);
        // Dereference thread object
        ObDereferenceObject(m_pPresentWorkerThread);
        m_pPresentWorkerThread = NULL;

        NT_ASSERT(m_hPresentWorkerThread);
        ZwClose(m_hPresentWorkerThread);
        m_hPresentWorkerThread = NULL;
    }

    if (hWorkerThread)
    {
        // Make sure that thread's handle would be valid even if the thread exited
        ObReferenceObjectByHandle(hWorkerThread, THREAD_ALL_ACCESS, NULL,
                                  KernelMode, &m_pPresentWorkerThread, NULL);
        NT_ASSERT(m_pPresentWorkerThread);
        m_hPresentWorkerThread = hWorkerThread;
    }

}
#pragma warning(pop)

NTSTATUS
BDD_HWBLT::ExecutePresentDisplayOnly(
    _In_ BYTE*             DstAddr,
    _In_ UINT              DstBitPerPixel,
    _In_ BYTE*             SrcAddr,
    _In_ UINT              SrcBytesPerPixel,
    _In_ LONG              SrcPitch,
    _In_ ULONG             NumMoves,
    _In_ D3DKMT_MOVE_RECT* Moves,
    _In_ ULONG             NumDirtyRects,
    _In_ RECT*             DirtyRect,
    _In_ D3DKMDT_VIDPN_PRESENT_PATH_ROTATION Rotation)
/*++

  Routine Description:

    The method creates present worker thread and provides context
    for it filled with present commands

  Arguments:

    DstAddr - address of destination surface
    DstBitPerPixel - color depth of destination surface
    SrcAddr - address of source surface
    SrcBytesPerPixel - bytes per pixel of source surface
    SrcPitch - source surface pitch (bytes in a row)
    NumMoves - number of moves to be copied
    Moves - moves' data
    NumDirtyRects - number of rectangles to be copied
    DirtyRect - rectangles' data
    Rotation - roatation to be performed when executing copy
    CallBack - callback for present worker thread to report execution status

  Return Value:

    Status

--*/
{

    PAGED_CODE();

    NTSTATUS Status = STATUS_SUCCESS;

    SIZE_T sizeMoves = NumMoves*sizeof(D3DKMT_MOVE_RECT);
    SIZE_T sizeRects = NumDirtyRects*sizeof(RECT);
    SIZE_T size = sizeof(DoPresentMemory) + sizeMoves + sizeRects;

    DoPresentMemory* ctx = reinterpret_cast<DoPresentMemory*>
                                (new (PagedPool) BYTE[size]);

    if (!ctx)
    {
        return STATUS_NO_MEMORY;
    }

    RtlZeroMemory(ctx,size);

    const CURRENT_BDD_MODE* pModeCur = m_DevExt->GetCurrentMode(m_SourceId);

    ctx->DstAddr          = DstAddr;
    ctx->DstBitPerPixel   = DstBitPerPixel;
    ctx->DstStride        = pModeCur->DispInfo.Pitch;
    ctx->SrcWidth         = pModeCur->SrcModeWidth;
    ctx->SrcHeight        = pModeCur->SrcModeHeight;
    ctx->SrcAddr          = NULL;
    ctx->SrcPitch         = SrcPitch;
    ctx->Rotation         = Rotation;
    ctx->NumMoves         = NumMoves;
    ctx->Moves            = Moves;
    ctx->NumDirtyRects    = NumDirtyRects;
    ctx->DirtyRect        = DirtyRect;
    ctx->SourceID         = m_SourceId;
    ctx->hAdapter         = m_DevExt;
    ctx->Mdl              = NULL;
    ctx->DisplaySource    = this;

    // Alternate between synch and asynch execution, for demonstrating 
    // that a real hardware implementation can do either
    m_SynchExecution = !m_SynchExecution;

    ctx->SynchExecution   = m_SynchExecution;

    {
        // Map Source into kernel space, as Blt will be executed by system worker thread
        UINT sizeToMap = SrcBytesPerPixel*pModeCur->SrcModeWidth*pModeCur->SrcModeHeight;

        PMDL mdl = IoAllocateMdl((PVOID)SrcAddr, sizeToMap,  FALSE, FALSE, NULL);
        if(!mdl)
        {
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        KPROCESSOR_MODE AccessMode = static_cast<KPROCESSOR_MODE>(( SrcAddr <=
                        (BYTE* const) MM_USER_PROBE_ADDRESS)?UserMode:KernelMode);
        __try
        {
            // Probe and lock the pages of this buffer in physical memory.
            // We need only IoReadAccess.
            MmProbeAndLockPages(mdl, AccessMode, IoReadAccess);
        }
        #pragma prefast(suppress: __WARNING_EXCEPTIONEXECUTEHANDLER, "try/except is only able to protect against user-mode errors and these are the only errors we try to catch here");
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
            Status = GetExceptionCode();
            IoFreeMdl(mdl);
            return Status;
        }

        // Map the physical pages described by the MDL into system space.
        // Note: double mapping the buffer this way causes lot of system
        // overhead for large size buffers.
        ctx->SrcAddr = reinterpret_cast<BYTE*>
            (MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority ));

        if(!ctx->SrcAddr) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            MmUnlockPages(mdl);
            IoFreeMdl(mdl);
            return Status;
        }

        // Save Mdl to unmap and unlock the pages in worker thread
        ctx->Mdl = mdl;
    }

    BYTE* rects = reinterpret_cast<BYTE*>(ctx+1);

    // copy moves and update pointer
    if (Moves)
    {
        memcpy(rects,Moves,sizeMoves);
        ctx->Moves = reinterpret_cast<D3DKMT_MOVE_RECT*>(rects);
        rects += sizeMoves;
    }

    // copy dirty rects and update pointer
    if (DirtyRect)
    {
        memcpy(rects,DirtyRect,sizeRects);
        ctx->DirtyRect = reinterpret_cast<RECT*>(rects);
    }


    if (m_SynchExecution)
    {
        HwExecutePresentDisplayOnly((PVOID)ctx);
        return STATUS_SUCCESS;
    }
    else
    {
        // Create a worker thread to perform the present asynchronously
        // Ctx will be deleted in worker thread (on exit)
        return StartHwBltPresentWorkerThread(HwContextWorkerThread,(PVOID)ctx);
    }
}


void
ReportPresentProgress(
    _In_ HANDLE Adapter,
    _In_ D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId,
    _In_ BOOLEAN CompletedOrFailed)
/*++

  Routine Description:

    This routine runs a fake interrupt routine in order to tell the OS about the present progress.

  Arguments:

    Adapter - Handle to the adapter (Device Extension)
    VidPnSourceId - Video present source id for the callback
    CompletedOrFailed - Present progress status for the source

  Return Value:

    None

--*/
{
    PAGED_CODE();

    BASIC_DISPLAY_DRIVER* pDevExt =
        reinterpret_cast<BASIC_DISPLAY_DRIVER*>(Adapter);

    SYNC_NOTIFY_INTERRUPT SyncNotifyInterrupt = {};
    SyncNotifyInterrupt.DxgkInterface = pDevExt->GetDxgkInterface();
    SyncNotifyInterrupt.NotifyInterrupt.InterruptType = DXGK_INTERRUPT_DISPLAYONLY_PRESENT_PROGRESS;
    SyncNotifyInterrupt.NotifyInterrupt.DisplayOnlyPresentProgress.VidPnSourceId = VidPnSourceId;

    SyncNotifyInterrupt.NotifyInterrupt.DisplayOnlyPresentProgress.ProgressId =
                            (CompletedOrFailed)?DXGK_PRESENT_DISPLAYONLY_PROGRESS_ID_COMPLETE:
                            DXGK_PRESENT_DISPLAYONLY_PROGRESS_ID_FAILED;

    // Execute the SynchronizeVidSchNotifyInterrupt function at the interrupt 
    // IRQL in order to fake a real present progress interrupt
    BOOLEAN bRet = FALSE;
    NT_VERIFY(NT_SUCCESS(pDevExt->GetDxgkInterface()->DxgkCbSynchronizeExecution(
                                    pDevExt->GetDxgkInterface()->DeviceHandle,
                                    (PKSYNCHRONIZE_ROUTINE)SynchronizeVidSchNotifyInterrupt,
                                    (PVOID)&SyncNotifyInterrupt,0,&bRet)));
    NT_ASSERT(bRet);
}


void
HwContextWorkerThread(
    HANDLE Context)
{
    PAGED_CODE();

    DoPresentMemory* ctx = reinterpret_cast<DoPresentMemory*>(Context);
    BDD_HWBLT* displaySource = ctx->DisplaySource;

    // Signal event to indicate that the tread has started
    KeSetEvent(&displaySource->m_hThreadStartupEvent, 0, FALSE);

    // Suspend context thread, do this by waiting on the suspend event
    KeWaitForSingleObject(&displaySource->m_hThreadSuspendEvent, Executive, KernelMode, FALSE, NULL);

    HwExecutePresentDisplayOnly(Context);
}


void
HwExecutePresentDisplayOnly(
    HANDLE Context)
/*++

  Routine Description:

    The routine executes present's commands and report progress to the OS

  Arguments:

    Context - Context with present's command

  Return Value:

    None

--*/
{
    PAGED_CODE();

    DoPresentMemory* ctx = reinterpret_cast<DoPresentMemory*>(Context);

    // Set up destination blt info
    BLT_INFO DstBltInfo;
    DstBltInfo.pBits = ctx->DstAddr;
    DstBltInfo.Pitch = ctx->DstStride;
    DstBltInfo.BitsPerPel = ctx->DstBitPerPixel;
    DstBltInfo.Offset.x = 0;
    DstBltInfo.Offset.y = 0;
    DstBltInfo.Rotation = ctx->Rotation;
    DstBltInfo.Width = ctx->SrcWidth;
    DstBltInfo.Height = ctx->SrcHeight;

    // Set up source blt info
    BLT_INFO SrcBltInfo;
    SrcBltInfo.pBits = ctx->SrcAddr;
    SrcBltInfo.Pitch = ctx->SrcPitch;
    SrcBltInfo.BitsPerPel = 32;
    SrcBltInfo.Offset.x = 0;
    SrcBltInfo.Offset.y = 0;
    SrcBltInfo.Rotation = D3DKMDT_VPPR_IDENTITY;
    if (ctx->Rotation == D3DKMDT_VPPR_ROTATE90 ||
        ctx->Rotation == D3DKMDT_VPPR_ROTATE270)
    {
        SrcBltInfo.Width = DstBltInfo.Height;
        SrcBltInfo.Height = DstBltInfo.Width;
    }
    else
    {
        SrcBltInfo.Width = DstBltInfo.Width;
        SrcBltInfo.Height = DstBltInfo.Height;
    }


    // Copy all the scroll rects from source image to video frame buffer.
    for (UINT i = 0; i < ctx->NumMoves; i++)
    {
        BltBits(&DstBltInfo,
        &SrcBltInfo,
        1, // NumRects
        &ctx->Moves[i].DestRect);
    }

    // Copy all the dirty rects from source image to video frame buffer.
    for (UINT i = 0; i < ctx->NumDirtyRects; i++)
    {

        BltBits(&DstBltInfo,
        &SrcBltInfo,
        1, // NumRects
        &ctx->DirtyRect[i]);
    }

    // Unmap unmap and unlock the pages.
    if (ctx->Mdl)
    {
        MmUnlockPages(ctx->Mdl);
        IoFreeMdl(ctx->Mdl);
    }

    if(ctx->SynchExecution)
    {
        // This code simulates Blt executed synchronously
        // nothing should be done here, just exit
        ;
    }
    else
    {
        // TRUE == completed
        // This code is emulates interrupt which HW should generate
        ReportPresentProgress(ctx->hAdapter,ctx->SourceID,TRUE);
    }

    delete [] reinterpret_cast<BYTE*>(ctx);
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS