Advertisement

Direct3D9 Rendering without Shaders or EVR

Started by July 30, 2021 11:33 AM
4 comments, last by ceb7353 3 years, 4 months ago

Hello ! I'm trying to code a program which displays a 4k Video using Direct3D9 but without EVR....

For now, the video is displaying well but at some time during the rendering, the execution stops and blocks at "m_pD3D9Device->Present(...)"

I've tried a lot of things to solve the problem but none of these worked...

Could you please help me?

Here are the important lines of the code:

HEADER :

// -------------- Attributes --------------

wchar_t* m_MEDIA_FILE_PATH;
IMFSourceReader* m_pReader;
IMFMediaType* m_pVideoSourceMediaType, * m_pReadMediaType;
UINT32 m_VideoWidth, m_VideoHeight;
IDirect3DDevice9* m_pD3D9Device;
IDirect3DDeviceManager9* m_pDeviceManager;
IDirect3DSwapChain9* m_pSwapChain;
IDirect3DSurface9* m_pBackBuffer;
HANDLE m_hDevice;
UINT m_token;

CPP :


/* Main method.
 * Returns S_OK if successful or an error code if not
 */
HRESULT D3D9Player::LaunchPlayer()
{
    CHECK_HR(Initialize(), "Failed to initialize system.\n");
    InitializeDirect3D();

    //CHECK_HR(InitializeVideoProcessorContext(), "Failed to initialize video processor context.\n");
    CHECK_HR(StartSampleLoop(), "Failed to process main loop.\n");
    return S_OK;
}


/* Initialize MediaFoundation.
 * Returns S_OK if successful or an error code if not
 */
HRESULT D3D9Player::Initialize()
{
    CHECK_HR(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE), "COM initialisation failed.\n");
    CHECK_HR(MFStartup(MF_VERSION), "Media Foundation initialisation failed.\n");
    // Create a separate Window and thread to host the Video player.
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&D3D9PlayerServerThread, this, 0, NULL);
    Sleep(1000);
    return S_OK;
}


// Start the server thread
DWORD D3D9Player::D3D9PlayerServerThread(D3D9Player* mediaPlayer)
{
    return mediaPlayer->InitializeWindow();
}


/*
 * Initialize the Window where the video will be displayed.
 * The window can also be found instead of being created (see FindWindowEx).
 * Returns S_OK if successful or an error code if not
 */
DWORD D3D9Player::InitializeWindow() {

    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = DefWindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = CLASS_NAME;
    if (RegisterClass(&wc))
    {
        g_hwnd = CreateWindow(
            CLASS_NAME,
            WINDOW_NAME,
            WS_EX_TOPMOST | WS_POPUP,
            1920,
            0,
            1920,
            1080,
            NULL,
            NULL,
            GetModuleHandle(NULL),
            NULL
        );

        if (g_hwnd)
        {
            ShowWindow(g_hwnd, SW_SHOWDEFAULT);
            MSG msg = { 0 };
            while (true)
            {
                if (GetMessage(&msg, NULL, 0, 0))
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
        }
    }
    return 0;
}


HRESULT D3D9Player::InitializeDirect3D()
{
    IMFAttributes* pSourceReaderAttributes = NULL;
    CHECK_HR(MFCreateAttributes(&pSourceReaderAttributes, 2), "Failed to create attributes.\n");
    CHECK_HR(DXVA2CreateDirect3DDeviceManager9(&m_token, &m_pDeviceManager), "Failed to create device Manager.\n");
    CHECK_HR(pSourceReaderAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, m_pDeviceManager), "Failed to set SOURCE_READER_D3D_MANAGER attribute.\n");
    CHECK_HR(pSourceReaderAttributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE), "Failed to set SOURCE_READER_D3D_MANAGER attribute.\n");
    CHECK_HR(SetUpVideoSource(pSourceReaderAttributes), "Failed to set up Video Source.\n");
    CHECK_HR(InitializeD3D9Device(), "Failed to initialize D3D9Device.\n");
    CHECK_HR(m_pDeviceManager->ResetDevice(m_pD3D9Device, m_token), "Failed to set the device on the manager.\n");
    CHECK_HR(MFCreateVideoSampleAllocator(IID_IMFVideoSampleAllocator, (void**)&m_pSampleAllocator), "Failed to create video sample allocator.\n");
    CHECK_HR(m_pSampleAllocator->SetDirectXManager(m_pDeviceManager), "Failed to set Direct Manager on Video sample allocator.\n");
    CHECK_HR(m_pSampleAllocator->InitializeSampleAllocator(1, m_pReadMediaType), "Failed to initialize sample allocator.\n");
    CHECK_HR(m_pSampleAllocator->AllocateSample(&m_pD3DVideoSample), "Failed to allocate video sample.\n");
    CHECK_HR(GetD3DSurfaceFromSample(), "Failed to get D3DSurface from sample.\n");
    CHECK_HR(m_pDeviceManager->OpenDeviceHandle(&m_hDevice), "Failed to get the Handle Device from the pD3DManager.\n");
    CHECK_HR(m_pD3DVideoSample->GetBufferByIndex(0, &m_pDstBuffer), "Failed to get destination buffer.\n");
    CHECK_HR(m_pDstBuffer->QueryInterface(IID_PPV_ARGS(&m_p2DBuffer)), "Failed to get pointer to 2D buffer.\n");

    SAFE_RELEASE(pSourceReaderAttributes);
    return S_OK;
}


HRESULT D3D9Player::InitializeD3D9Device() {
    IDirect3D9* pD3D9Object = NULL;
    D3DPRESENT_PARAMETERS presentDesc = { 0 };
    presentDesc.BackBufferWidth = m_VideoWidth;
    presentDesc.BackBufferHeight = m_VideoHeight;
    presentDesc.BackBufferFormat = D3DFMT_X8R8G8B8;
    presentDesc.BackBufferCount = 1;
    presentDesc.MultiSampleType = D3DMULTISAMPLE_NONE;
    presentDesc.MultiSampleQuality = 0;
    presentDesc.SwapEffect = D3DSWAPEFFECT_DISCARD;
    presentDesc.hDeviceWindow = g_hwnd;
    presentDesc.Windowed = TRUE;
    presentDesc.EnableAutoDepthStencil = FALSE;
    presentDesc.Flags = 0;
    pD3D9Object = Direct3DCreate9(D3D_SDK_VERSION);
    CHECK_HR(pD3D9Object->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentDesc, &m_pD3D9Device), "Failed to create Direct 3D Device.\n");

    return S_OK;
}


HRESULT D3D9Player::SetUpVideoSource(IMFAttributes* attributes)
{
    CHECK_HR(MFCreateSourceReaderFromURL(m_MEDIA_FILE_PATH, attributes, &m_pReader), "Failed to create Source Reader from file.\n");
    CHECK_HR(m_pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &m_pVideoSourceMediaType), "Error retrieving current media type from first video stream.\n");

    //DSP input MediaType
    CHECK_HR(MFCreateMediaType(&m_pReadMediaType), "Failed to create read media type.\n");
    CHECK_HR(m_pVideoSourceMediaType->CopyAllItems(m_pReadMediaType), "Fail to copy all attributes from videoSourceOutpuType to pVideoSourceOutType\n");
    CHECK_HR(m_pReadMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12), "Failed to get source media type count.\n"); // TODO the subtype can be changed, see compatibilities with the current codec
    CHECK_HR(m_pReader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE), "Failed to set the first video stream on the source reader.\n");
    CHECK_HR(m_pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, m_pReadMediaType), "Failed to set output media type on source reader.\n");
    std::cout << "Input media type defined as:" << std::endl << GetMediaTypeDescription(m_pReadMediaType) << std::endl << std::endl;

    MFGetAttributeSize(m_pReadMediaType, MF_MT_FRAME_SIZE, &m_VideoWidth, &m_VideoHeight);
    return S_OK;
}

/*
 * Retrieve the D3DSurface9 from the given IMFSample.
 * Return S_OK on success, or an error value otherwise.
 */
HRESULT D3D9Player::GetD3DSurfaceFromSample()
{
    IMFMediaBuffer* pBuffer = NULL;
    CHECK_HR(m_pD3DVideoSample->GetBufferByIndex(0, &pBuffer), "Failed to get buffer by Index.\n");
    CHECK_HR(MFGetService(pBuffer, MR_BUFFER_SERVICE, IID_PPV_ARGS(&m_pSurface)), "Failed to get 3DSurface via MR_BUFFER_SERVICE.\n");
    pBuffer->Release();

    return S_OK;
}


HRESULT D3D9Player::StartSampleLoop() {
    DWORD previousT = timeGetTime(), currentT = -1;
    IMFSample* sample = NULL;
    FLOAT sum = 0.;
    DWORD count = 0;
    FLOAT originalStartTime = timeGetTime(), totalDuration = -1.;
    DWORD flags;
    HRESULT hrRes = S_OK;
    IMFMediaBuffer* pBuffer = NULL;
    BYTE* pbBuffer = NULL;
    DWORD dwBuffer = 0;

    CHECK_HR(m_pD3D9Device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer), "Failed to get BackBuffer from the device.\n");
    hrRes=m_pD3D9Device->SetRenderTarget(0, m_pBackBuffer);
    std::cout << "Started processing frames" << std::endl;
    while (true)
    {
        DWORD loopStartTime = timeGetTime();
        // Statistics
        count++;
        currentT = timeGetTime();
        sum += (currentT - previousT);
        std::cout << "FRAME NUMBER " << count << " DURATION:" << currentT - previousT << std::endl << std::endl;
        previousT = currentT;

        CHECK_HR(m_pReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, nullptr, &flags, NULL, &sample), "Failed to process readSample.\n");
        if (flags & MF_SOURCE_READERF_ENDOFSTREAM || sample == nullptr) {
            totalDuration = timeGetTime() - originalStartTime;
            PROPVARIANT var = { 0 };
            var.vt = VT_I8;
            CHECK_HR(m_pReader->SetCurrentPosition(GUID_NULL, var), "Failed to set current position on source reader.\n");
            std::cout << "End of stream.\nAverage time to display one Frame: " << sum / count << "s\nTotal Render Time : " << totalDuration * 0.001 << "s" << std::endl;
            //return S_OK;
        }
        if (!sample)
        {
            std::cout << "Null video sample." << std::endl;
        }
        else
        {

            CHECK_HR(sample->ConvertToContiguousBuffer(&pBuffer), "Failed to get buffer from video sample.\n");
            CHECK_HR(pBuffer->Lock(&pbBuffer, NULL, &dwBuffer), "Failed to lock sample buffer.\n");
            {
                CHECK_HR(m_p2DBuffer->ContiguousCopyFrom(pbBuffer, dwBuffer), "Failed to copy data to p2DBuffer from pbBuffer.\n");
            }
            CHECK_HR(pBuffer->Unlock(), "Failed to unlock source buffer.\n");
            CHECK_HR(m_pDeviceManager->LockDevice(m_hDevice, &m_pD3D9Device, TRUE), "Failed to lock the device.\n");
            {
                CHECK_HR(m_pD3D9Device->BeginScene(), "Failed to begin the scene.\n");
                CHECK_HR(m_pD3D9Device->StretchRect(m_pSurface, NULL, m_pBackBuffer, NULL, D3DTEXF_NONE), "Failed to do the 		                StretchRect.\n");
                CHECK_HR(m_pD3D9Device->EndScene(), "Failed to begin the scene.\n");
                CHECK_HR(m_pD3D9Device->Present(NULL, NULL, g_hwnd, NULL), "Failed to present the scene.\n");
                Sleep(10);

            }
            CHECK_HR(m_pDeviceManager->UnlockDevice(m_hDevice, TRUE), "Failed to unlock th device.\n");
        }
        SAFE_RELEASE(sample);
        SAFE_RELEASE(pBuffer);

    }    
    std::cout << "Finished processing frames" << std::endl;

    return S_OK;
}

Check the HRESULT from your Present call - this smells like a Lost Device to me.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

Advertisement

You're not dealing with necessary result codes from LockDevice(). If I remember right (and it's been … 15 years? .. since D3D9 was a thing?) there are codes that say “you should release and re-create the device on this device manager, because shenanigans!”

The other things that cause things to lock up after a while:

  1. Deadlocks – perhaps something is trying to post messages to your window, and you're not polling/handling them?
  2. Resource exhaustion – is everything in the loop (including the decoder) correctly releasing all resources? (Generally prefer CComPtr<> instead of raw pointer)
  3. Driver bugs – less common call paths may cause drivers to run into software bugs (including race conditions, or resource exhaustion.)

enum Bool { True, False, FileNotFound };

@hplus0603 I checked the second point, it's all clear and I have no memory leaks. For the first point I don't see what else could interact with my window except my video data.

To verify the third point i should probably execute my program with an other computer …

@21st Century Moose This is the problem… I can't check the HRESULT from the Present() because the program stops WHILE making the Present (I checked this by putting the execution on Pause to see where the principal Thread stopped, and that's how I knew that it was the Present() that made all crash.) So I can only check the result of the Present() which precedes the "fateful Present()", and the HRESULT is SOK. And what I also do is calling TestCooperativeLevel() before each Present(), the HRESULT is always S_OK

This topic is closed to new replies.

Advertisement