다음을 통해 공유


연습: WPF에서 호스팅할 Direct3D9 콘텐츠 만들기

이 연습에서는 WPF(Windows Presentation Foundation) 애플리케이션에서 호스팅하는 데 적합한 Direct3D9 콘텐츠를 만드는 방법을 보여 줍니다. WPF 애플리케이션에서 Direct3D9 콘텐츠를 호스팅하는 방법에 대한 자세한 내용은 WPF 및 Direct3D9 상호 운용성을 참조하세요.

이 연습에서는 다음 작업을 수행합니다.

  • Direct3D9 프로젝트를 만듭니다.

  • WPF 애플리케이션에서 호스팅할 Direct3D9 프로젝트를 구성합니다.

완료되면 WPF 애플리케이션에서 사용할 Direct3D9 콘텐츠가 포함된 DLL이 생성됩니다.

필수 구성 요소

이 연습을 완료하려면 다음과 같은 구성 요소가 필요합니다.

  • Visual Studio 2010

  • DirectX SDK 9 이상.

Direct3D9 프로젝트 만들기

첫 번째 단계는 Direct3D9 프로젝트를 만들고 구성하는 것입니다.

Direct3D9 프로젝트를 만들려면

  1. D3DContent라는 C++에서 새 Win32 프로젝트를 만듭니다.

    Win32 애플리케이션 마법사가 열리고 시작 화면이 표시됩니다.

  2. 다음을 클릭합니다.

    애플리케이션 설정 화면이 나타납니다.

  3. 애플리케이션 유형: 섹션에서 DLL 옵션을 선택합니다.

  4. Finish를 클릭합니다.

    D3DContent 프로젝트가 생성됩니다.

  5. 솔루션 탐색기에서 D3DContent 프로젝트를 마우스 오른쪽 단추로 클릭하고 속성을 선택합니다.

    D3DContent 속성 페이지 대화 상자가 열립니다.

  6. C/C++ 노드를 선택합니다.

  7. 추가 포함 디렉터리 필드에서 DirectX 포함 폴더의 위치를 지정합니다. 이 폴더의 기본 위치는 %ProgramFiles%\Microsoft DirectX SDK(버전)\Include입니다.

  8. 링커 노드를 두 번 클릭하여 확장합니다.

  9. 추가 라이브러리 디렉터리 필드에서 DirectX 라이브러리 폴더의 위치를 지정합니다. 이 폴더의 기본 위치는 %ProgramFiles%\Microsoft DirectX SDK(버전)\Lib\x86입니다.

  10. 입력 노드를 선택합니다.

  11. 추가 종속성 필드에서 d3d9.libd3dx9.lib 파일을 추가합니다.

  12. 솔루션 탐색기에서 D3DContent.def라는 새 모듈 정의 파일(.def)을 프로젝트에 추가합니다.

Direct3D9 콘텐츠 만들기

최상의 성능을 얻으려면 Direct3D9 콘텐츠에서 특정 설정을 사용해야 합니다. 다음 코드에서는 최상의 성능 특성을 가진 Direct3D9 표면을 만드는 방법을 보여 줍니다. 자세한 내용은 Direct3D9 및 WPF 상호 운용성을 위한 성능 고려 사항을 참조하세요.

Direct3D9 콘텐츠를 만들려면

  1. 솔루션 탐색기를 사용하여 다음과 같은 이름을 가진 프로젝트에 세 개의 C++ 클래스를 추가합니다.

    CRenderer(가상 소멸자 포함)

    CRendererManager

    CTriangleRenderer

  2. 코드 편집기에서 Renderer.h를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    
    #pragma once
    
    class CRenderer
    {
    public:
        virtual ~CRenderer();
    
        HRESULT CheckDeviceState();
        HRESULT CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples);
    
        virtual HRESULT Render() = 0;
    
        IDirect3DSurface9 *GetSurfaceNoRef() { return m_pd3dRTS; }
    
    protected:
        CRenderer();
    
        virtual HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter);
    
        IDirect3DDevice9   *m_pd3dDevice;
        IDirect3DDevice9Ex *m_pd3dDeviceEx;
    
        IDirect3DSurface9 *m_pd3dRTS;
    
    };
    
  3. 코드 편집기에서 Renderer.cpp를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    //+-----------------------------------------------------------------------------
    //
    //  CRenderer
    //
    //      An abstract base class that creates a device and a target render 
    //      surface. Derive from this class and override Init() and Render() 
    //      to do your own rendering. See CTriangleRenderer for an example.
    
    
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer ctor
    //
    //------------------------------------------------------------------------------
    CRenderer::CRenderer() : m_pd3dDevice(NULL), m_pd3dDeviceEx(NULL), m_pd3dRTS(NULL)
    {
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer dtor
    //
    //------------------------------------------------------------------------------
    CRenderer::~CRenderer()
    {
        SAFE_RELEASE(m_pd3dDevice);
        SAFE_RELEASE(m_pd3dDeviceEx);
        SAFE_RELEASE(m_pd3dRTS);
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::CheckDeviceState
    //
    //  Synopsis:
    //      Returns the status of the device. 9Ex devices are a special case because 
    //      TestCooperativeLevel() has been deprecated in 9Ex.
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRenderer::CheckDeviceState()
    {
        if (m_pd3dDeviceEx)
        {
            return m_pd3dDeviceEx->CheckDeviceState(NULL);
        }
        else if (m_pd3dDevice)
        {
            return m_pd3dDevice->TestCooperativeLevel();
        }
        else
        {
            return D3DERR_DEVICELOST;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::CreateSurface
    //
    //  Synopsis:
    //      Creates and sets the render target
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
    {
        HRESULT hr = S_OK;
    
        SAFE_RELEASE(m_pd3dRTS);
    
        IFC(m_pd3dDevice->CreateRenderTarget(
            uWidth,
            uHeight,
            fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
            static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
            0,
            m_pd3dDeviceEx ? FALSE : TRUE,  // Lockable RT required for good XP perf
            &m_pd3dRTS,
            NULL
            ));
    
        IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRenderer::Init
    //
    //  Synopsis:
    //      Creates the device
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
    {
        HRESULT hr = S_OK;
    
        D3DPRESENT_PARAMETERS d3dpp;
        ZeroMemory(&d3dpp, sizeof(d3dpp));
        d3dpp.Windowed = TRUE;
        d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
        d3dpp.BackBufferHeight = 1;
        d3dpp.BackBufferWidth = 1;
        d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    
        D3DCAPS9 caps;
        DWORD dwVertexProcessing;
        IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
        if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
        {
            dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
        }
        else
        {
            dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
        }
    
        if (pD3DEx)
        {
            IDirect3DDevice9Ex *pd3dDevice = NULL;
            IFC(pD3DEx->CreateDeviceEx(
                uAdapter,
                D3DDEVTYPE_HAL,
                hwnd,
                dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
                &d3dpp,
                NULL,
                &m_pd3dDeviceEx
                ));
    
            IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));  
        }
        else 
        {
            assert(pD3D);
    
            IFC(pD3D->CreateDevice(
                uAdapter,
                D3DDEVTYPE_HAL,
                hwnd,
                dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
                &d3dpp,
                &m_pd3dDevice
                ));
        }
    
    Cleanup:
        return hr;
    }
    
  4. 코드 편집기에서 RendererManager.h를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    
    #pragma once
    
    class CRenderer;
    
    class CRendererManager
    {
    public:
        static HRESULT Create(CRendererManager **ppManager);
        ~CRendererManager();
    
        HRESULT EnsureDevices();
    
        void SetSize(UINT uWidth, UINT uHeight);
        void SetAlpha(bool fUseAlpha);
        void SetNumDesiredSamples(UINT uNumSamples);
        void SetAdapter(POINT screenSpacePoint);
    
        HRESULT GetBackBufferNoRef(IDirect3DSurface9 **ppSurface);
    
        HRESULT Render();
    
    private:
        CRendererManager();
    
        void CleanupInvalidDevices();
        HRESULT EnsureRenderers();
        HRESULT EnsureHWND();
        HRESULT EnsureD3DObjects();
        HRESULT TestSurfaceSettings();
        void DestroyResources();
    
        IDirect3D9    *m_pD3D;
        IDirect3D9Ex  *m_pD3DEx;
    
        UINT m_cAdapters;
        CRenderer **m_rgRenderers;
        CRenderer *m_pCurrentRenderer;
    
        HWND m_hwnd;
    
        UINT m_uWidth;
        UINT m_uHeight;
        UINT m_uNumSamples;
        bool m_fUseAlpha;
        bool m_fSurfaceSettingsChanged;
    };
    
  5. 코드 편집기에서 RendererManager.cpp를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    
    //+-----------------------------------------------------------------------------
    //
    //  CRendererManager
    //
    //      Manages the list of CRenderers. Managed code pinvokes into this class
    //      and this class forwards to the appropriate CRenderer.
    //
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    const static TCHAR szAppName[] = TEXT("D3DImageSample");
    typedef HRESULT (WINAPI *DIRECT3DCREATE9EXFUNCTION)(UINT SDKVersion, IDirect3D9Ex**);
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager ctor
    //
    //------------------------------------------------------------------------------
    CRendererManager::CRendererManager() 
        : 
        m_pD3D(NULL),
        m_pD3DEx(NULL),
        m_cAdapters(0),
        m_hwnd(NULL),
        m_pCurrentRenderer(NULL),
        m_rgRenderers(NULL),
        m_uWidth(1024),
        m_uHeight(1024),
        m_uNumSamples(0),
        m_fUseAlpha(false),
        m_fSurfaceSettingsChanged(true)
    {
    
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager dtor
    //
    //------------------------------------------------------------------------------
    CRendererManager::~CRendererManager()
    {
        DestroyResources();
    
        if (m_hwnd)
        {
            DestroyWindow(m_hwnd);
            UnregisterClass(szAppName, NULL);
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::Create
    //
    //  Synopsis:
    //      Creates the manager
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::Create(CRendererManager **ppManager)
    {
        HRESULT hr = S_OK;
    
        *ppManager = new CRendererManager();
        IFCOOM(*ppManager);
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureRenderers
    //
    //  Synopsis:
    //      Makes sure the CRenderer objects exist
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureRenderers()
    {
        HRESULT hr = S_OK;
    
        if (!m_rgRenderers)
        {
            IFC(EnsureHWND());
    
            assert(m_cAdapters);    
            m_rgRenderers = new CRenderer*[m_cAdapters];
            IFCOOM(m_rgRenderers);
            ZeroMemory(m_rgRenderers, m_cAdapters * sizeof(m_rgRenderers[0]));
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                IFC(CTriangleRenderer::Create(m_pD3D, m_pD3DEx, m_hwnd, i, &m_rgRenderers[i]));
            }
    
            // Default to the default adapter 
            m_pCurrentRenderer = m_rgRenderers[0];
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureHWND
    //
    //  Synopsis:
    //      Makes sure an HWND exists if we need it
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureHWND()
    {
        HRESULT hr = S_OK;
    
        if (!m_hwnd)
        {
            WNDCLASS wndclass;
    
            wndclass.style = CS_HREDRAW | CS_VREDRAW;
            wndclass.lpfnWndProc = DefWindowProc;
            wndclass.cbClsExtra = 0;
            wndclass.cbWndExtra = 0;
            wndclass.hInstance = NULL;
            wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
            wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
            wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
            wndclass.lpszMenuName = NULL;
            wndclass.lpszClassName = szAppName;
    
            if (!RegisterClass(&wndclass))
            {
                IFC(E_FAIL);
            }
    
            m_hwnd = CreateWindow(szAppName,
                                TEXT("D3DImageSample"),
                                WS_OVERLAPPEDWINDOW,
                                0,                   // Initial X
                                0,                   // Initial Y
                                0,                   // Width
                                0,                   // Height
                                NULL,
                                NULL,
                                NULL,
                                NULL);
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::EnsureD3DObjects
    //
    //  Synopsis:
    //      Makes sure the D3D objects exist
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::EnsureD3DObjects()
    {
        HRESULT hr = S_OK;
    
        HMODULE hD3D = NULL;
        if (!m_pD3D)
        {
            hD3D = LoadLibrary(TEXT("d3d9.dll"));
            DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
            if (pfnCreate9Ex)
            {
                IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
                IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
            }
            else
            {
                m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
                if (!m_pD3D) 
                {
                    IFC(E_FAIL);
                }
            }
    
            m_cAdapters = m_pD3D->GetAdapterCount();
        }
    
    Cleanup:
        if (hD3D)
        {
            FreeLibrary(hD3D);
        }
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::CleanupInvalidDevices
    //
    //  Synopsis:
    //      Checks to see if any devices are bad and if so, deletes all resources
    //
    //      We could delete resources and wait for D3DERR_DEVICENOTRESET and reset
    //      the devices, but if the device is lost because of an adapter order 
    //      change then our existing D3D objects would have stale adapter 
    //      information. We'll delete everything to be safe rather than sorry.
    //
    //------------------------------------------------------------------------------
    void
    CRendererManager::CleanupInvalidDevices()
    {
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            if (FAILED(m_rgRenderers[i]->CheckDeviceState()))
            {
                DestroyResources();
                break;
            }
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::GetBackBufferNoRef
    //
    //  Synopsis:
    //      Returns the surface of the current renderer without adding a reference
    //
    //      This can return NULL if we're in a bad device state.
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CRendererManager::GetBackBufferNoRef(IDirect3DSurface9 **ppSurface)
    {
        HRESULT hr = S_OK;
    
        // Make sure we at least return NULL
        *ppSurface = NULL;
    
        CleanupInvalidDevices();
    
        IFC(EnsureD3DObjects());
    
        //
        // Even if we never render to another adapter, this sample creates devices
        // and resources on each one. This is a potential waste of video memory,
        // but it guarantees that we won't have any problems (e.g. out of video
        // memory) when switching to render on another adapter. In your own code
        // you may choose to delay creation but you'll need to handle the issues
        // that come with it.
        //
    
        IFC(EnsureRenderers());
    
        if (m_fSurfaceSettingsChanged)
        {
            if (FAILED(TestSurfaceSettings()))
            {
                IFC(E_FAIL);
            }
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                IFC(m_rgRenderers[i]->CreateSurface(m_uWidth, m_uHeight, m_fUseAlpha, m_uNumSamples));
            }
    
            m_fSurfaceSettingsChanged = false;
        }
    
        if (m_pCurrentRenderer)
        {
            *ppSurface = m_pCurrentRenderer->GetSurfaceNoRef();
        }
    
    Cleanup:
        // If we failed because of a bad device, ignore the failure for now and 
        // we'll clean up and try again next time.
        if (hr == D3DERR_DEVICELOST)
        {
            hr = S_OK;
        }
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::TestSurfaceSettings
    //
    //  Synopsis:
    //      Checks to see if our current surface settings are allowed on all
    //      adapters.
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::TestSurfaceSettings()
    {
        HRESULT hr = S_OK;
    
        D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
    
        // 
        // We test all adapters because because we potentially use all adapters.
        // But even if this sample only rendered to the default adapter, you
        // should check all adapters because WPF may move your surface to
        // another adapter for you!
        //
    
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            // Can we get HW rendering?
            IFC(m_pD3D->CheckDeviceType(
                i,
                D3DDEVTYPE_HAL,
                D3DFMT_X8R8G8B8,
                fmt,
                TRUE
                )); 
    
            // Is the format okay?
            IFC(m_pD3D->CheckDeviceFormat(
                i,
                D3DDEVTYPE_HAL,
                D3DFMT_X8R8G8B8,
                D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
                D3DRTYPE_SURFACE,
                fmt
                ));
    
            // D3DImage only allows multisampling on 9Ex devices. If we can't 
            // multisample, overwrite the desired number of samples with 0.
            if (m_pD3DEx && m_uNumSamples > 1)
            {   
                assert(m_uNumSamples <= 16);
    
                if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
                    i,
                    D3DDEVTYPE_HAL,
                    fmt,
                    TRUE,
                    static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
                    NULL
                    )))
                {
                    m_uNumSamples = 0;
                }
            }
            else
            {
                m_uNumSamples = 0;
            }
        }
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::DestroyResources
    //
    //  Synopsis:
    //      Delete all D3D resources
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::DestroyResources()
    {
        SAFE_RELEASE(m_pD3D);
        SAFE_RELEASE(m_pD3DEx);
    
        for (UINT i = 0; i < m_cAdapters; ++i)
        {
            delete m_rgRenderers[i];
        }
        delete [] m_rgRenderers;
        m_rgRenderers = NULL;
    
        m_pCurrentRenderer = NULL;
        m_cAdapters = 0;
    
        m_fSurfaceSettingsChanged = true;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetSize
    //
    //  Synopsis:
    //      Update the size of the surface. Next render will create a new surface.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetSize(UINT uWidth, UINT uHeight)
    {
        if (uWidth != m_uWidth || uHeight != m_uHeight)
        {
            m_uWidth = uWidth;
            m_uHeight = uHeight;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetAlpha
    //
    //  Synopsis:
    //      Update the format of the surface. Next render will create a new surface.
    //
    //------------------------------------------------------------------------------
    void
    CRendererManager::SetAlpha(bool fUseAlpha)
    {
        if (fUseAlpha != m_fUseAlpha)
        {
            m_fUseAlpha = fUseAlpha;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetNumDesiredSamples
    //
    //  Synopsis:
    //      Update the MSAA settings of the surface. Next render will create a 
    //      new surface.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetNumDesiredSamples(UINT uNumSamples)
    {
        if (m_uNumSamples != uNumSamples)
        {
            m_uNumSamples = uNumSamples;
            m_fSurfaceSettingsChanged = true;
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::SetAdapter
    //
    //  Synopsis:
    //      Update the current renderer. Next render will use the new renderer.
    //
    //------------------------------------------------------------------------------
    void 
    CRendererManager::SetAdapter(POINT screenSpacePoint)
    {
        CleanupInvalidDevices();
    
        //
        // After CleanupInvalidDevices, we may not have any D3D objects. Rather than
        // recreate them here, ignore the adapter update and wait for render to recreate.
        //
    
        if (m_pD3D && m_rgRenderers)
        {
            HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
    
            for (UINT i = 0; i < m_cAdapters; ++i)
            {
                if (hMon == m_pD3D->GetAdapterMonitor(i))
                {
                    m_pCurrentRenderer = m_rgRenderers[i];
                    break;
                }
            }
        }
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CRendererManager::Render
    //
    //  Synopsis:
    //      Forward to the current renderer
    //
    //------------------------------------------------------------------------------
    HRESULT
    CRendererManager::Render()
    {
        return m_pCurrentRenderer ? m_pCurrentRenderer->Render() : S_OK;
    }
    
  6. 코드 편집기에서 TriangleRenderer.h를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    #pragma once
    
    class CTriangleRenderer : public CRenderer
    {
    public:
        static HRESULT Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer);
        ~CTriangleRenderer();
    
        HRESULT Render();
    
    protected:
        HRESULT Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter);
    
    private:
        CTriangleRenderer();
    
        IDirect3DVertexBuffer9 *m_pd3dVB;
    };
    
  7. 코드 편집기에서 TriangleRenderer.cpp를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    
    //+-----------------------------------------------------------------------------
    //
    //  CTriangleRenderer
    //
    //      Subclass of CRenderer that renders a single, spinning triangle
    //
    //------------------------------------------------------------------------------
    
    #include "StdAfx.h"
    
    struct CUSTOMVERTEX
    {
        FLOAT x, y, z; 
        DWORD color;
    };
    
    #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE)
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer ctor
    //
    //------------------------------------------------------------------------------
    CTriangleRenderer::CTriangleRenderer() : CRenderer(), m_pd3dVB(NULL)
    {
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer dtor
    //
    //------------------------------------------------------------------------------
    CTriangleRenderer::~CTriangleRenderer()
    {
        SAFE_RELEASE(m_pd3dVB);
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Create
    //
    //  Synopsis:
    //      Creates the renderer
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CTriangleRenderer::Create(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter, CRenderer **ppRenderer)
    {
        HRESULT hr = S_OK;
        
        CTriangleRenderer *pRenderer = new CTriangleRenderer();
        IFCOOM(pRenderer);
    
        IFC(pRenderer->Init(pD3D, pD3DEx, hwnd, uAdapter));
    
        *ppRenderer = pRenderer;
        pRenderer = NULL;
    
    Cleanup:
        delete pRenderer;
    
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Init
    //
    //  Synopsis:
    //      Override of CRenderer::Init that calls base to create the device and 
    //      then creates the CTriangleRenderer-specific resources
    //
    //------------------------------------------------------------------------------
    HRESULT 
    CTriangleRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
    {
        HRESULT hr = S_OK;
        D3DXMATRIXA16 matView, matProj;
        D3DXVECTOR3 vEyePt(0.0f, 0.0f,-5.0f);
        D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);
        D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);
    
        // Call base to create the device and render target
        IFC(CRenderer::Init(pD3D, pD3DEx, hwnd, uAdapter));
    
        // Set up the VB
        CUSTOMVERTEX vertices[] =
        {
            { -1.0f, -1.0f, 0.0f, 0xffff0000, }, // x, y, z, color
            {  1.0f, -1.0f, 0.0f, 0xff00ff00, },
            {  0.0f,  1.0f, 0.0f, 0xff00ffff, },
        };
    
        IFC(m_pd3dDevice->CreateVertexBuffer(sizeof(vertices), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pd3dVB, NULL));
    
        void *pVertices;
        IFC(m_pd3dVB->Lock(0, sizeof(vertices), &pVertices, 0));
        memcpy(pVertices, vertices, sizeof(vertices));
        m_pd3dVB->Unlock();
    
        // Set up the camera
        D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);
        IFC(m_pd3dDevice->SetTransform(D3DTS_VIEW, &matView));
        D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f);
        IFC(m_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj));
    
        // Set up the global state
        IFC(m_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE));
        IFC(m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE));
        IFC(m_pd3dDevice->SetStreamSource(0, m_pd3dVB, 0, sizeof(CUSTOMVERTEX)));
        IFC(m_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX));
    
    Cleanup:
        return hr;
    }
    
    //+-----------------------------------------------------------------------------
    //
    //  Member:
    //      CTriangleRenderer::Render
    //
    //  Synopsis:
    //      Renders the rotating triangle
    //
    //------------------------------------------------------------------------------
    HRESULT
    CTriangleRenderer::Render()
    {
        HRESULT hr = S_OK;
        D3DXMATRIXA16 matWorld;
    
        IFC(m_pd3dDevice->BeginScene());
        IFC(m_pd3dDevice->Clear(
            0,
            NULL,
            D3DCLEAR_TARGET,
            D3DCOLOR_ARGB(128, 0, 0, 128),  // NOTE: Premultiplied alpha!
            1.0f,
            0
            ));
    
        // Set up the rotation
        UINT  iTime  = GetTickCount() % 1000;
        FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
        D3DXMatrixRotationY(&matWorld, fAngle);
        IFC(m_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld));
    
        IFC(m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1));
    
        IFC(m_pd3dDevice->EndScene());
    
    Cleanup:
        return hr;
    }
    
  8. 코드 편집기에서 stdafx.h를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    // stdafx.h : include file for standard system include files,
    // or project specific include files that are used frequently, but
    // are changed infrequently
    //
    
    #pragma once
    
    #define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
    // Windows Header Files:
    #include <windows.h>
    
    #include <d3d9.h>
    #include <d3dx9.h>
    
    #include <assert.h>
    
    #include "RendererManager.h"
    #include "Renderer.h"
    #include "TriangleRenderer.h"
    
    #define IFC(x) { hr = (x); if (FAILED(hr)) goto Cleanup; }
    #define IFCOOM(x) { if ((x) == NULL) { hr = E_OUTOFMEMORY; IFC(hr); } }
    #define SAFE_RELEASE(x) { if (x) { x->Release(); x = NULL; } }
    
    
  9. 코드 편집기에서 dllmain.cpp를 열고 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
    static CRendererManager *pManager = NULL;
    
    static HRESULT EnsureRendererManager()
    {
        return pManager ? S_OK : CRendererManager::Create(&pManager);
    }
    
    extern "C" HRESULT WINAPI SetSize(UINT uWidth, UINT uHeight)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetSize(uWidth, uHeight);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetAlpha(BOOL fUseAlpha)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetAlpha(!!fUseAlpha);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetNumDesiredSamples(UINT uNumSamples)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetNumDesiredSamples(uNumSamples);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI SetAdapter(POINT screenSpacePoint)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        pManager->SetAdapter(screenSpacePoint);
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI GetBackBufferNoRef(IDirect3DSurface9 **ppSurface)
    {
        HRESULT hr = S_OK;
    
        IFC(EnsureRendererManager());
    
        IFC(pManager->GetBackBufferNoRef(ppSurface));
    
    Cleanup:
        return hr;
    }
    
    extern "C" HRESULT WINAPI Render()
    {
        assert(pManager);
    
        return pManager->Render();
    }
    
    extern "C" void WINAPI Destroy()
    {
        delete pManager;
        pManager = NULL;
    }
    
  10. 코드 편집기에서 D3DContent.def를 엽니다.

  11. 자동으로 생성된 코드를 다음 코드로 바꿉니다.

    LIBRARY "D3DContent"
    
    EXPORTS
    
    SetSize
    SetAlpha
    SetNumDesiredSamples
    SetAdapter
    
    GetBackBufferNoRef
    Render
    Destroy
    
  12. 프로젝트를 빌드합니다.

다음 단계

참고 항목