728x90

D3DX11_IMAGE_LOAD_INFO LoadInfo;


HR(D3DX11CreateShaderResourceViewFromMemory(in_pDevice, BmpImage, BmpImageSize &LoadInfo, nullptr, &m_pSRV, nullptr));



bmp 파일의 크기를 약 30 ~ 40 MB를 D3DX11CreateShaderResourceViewFromMemory()에서 불러오니  


E_INVALIDARG 에러가 발생했다.


E_INVALIDARG 에러는 쉽게 말하면 잘못된 매개변수를 이용하여 함수를 호출했다는 뜻이다.


그래서 모든 매개변수의 값을 확인햇는데 문제가 없었다.


마지막으로 혹시나하고 LoadInfo의 값을 Default 값이 아닌 임의의 크기로 지정을 하니 해결되었다..


D3DX11_IMAGE_LOAD_INFO LoadInfo;

LoadInfo.Width = nTempWidth;

LoadInfo.Height = nTempHeight;

LoadInfo.Usage = D3D11_USAGE_DEFAULT;

LoadInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;

LoadInfo.CpuAccessFlags = 0;


HR(D3DX11CreateShaderResourceViewFromMemory(in_pDevice, BmpImage, BmpImageSize &LoadInfo, nullptr, &m_pSRV, nullptr));


이 bmp 파일의 Width는 38611, Height는 1080으로 FULL HD를 기준으로 약 모니터 20개의 크기의 텍스처를 불러오려니 

죽은 것일 것이다...

728x90
728x90

Device Lost는 DirectX를 개발하면 반드시 거쳐야하는 관문 중 하나이다. (OpenGL도 Device Lost가 있겠지... ?)


Device Lost란 쉽게 말하면 시스템의 리소스를 모두 잃어버려서 사용할 수 없게 되어버린 상태이다.


Device Lost가 발생하게 되는 이유는 Alt+Tab, 실시간 화면 크기 변경, 그래픽 카드가 죽어버리는 현상등 다양한 이유가 있다.


DirectX 11 에서는 디바이스 로스트가 거의 일어나지 않지만, 몇가지 예외적 상황에서는 디바이스 로스트가 일어난다.

 

예를들어 GPU가 Hang Up 상태가 되어 장시간 응답이 없을 경우 OS의 타임 아웃처리가 발동하여 GPU가 리셋 된다.

 

이경우에도 OS의 재부팅은 필요없지만 어플리케이션측에서 만든 DirectX 11 디바이스 나 오브젝트등은 모두 파기하고

새로이 만들어야 한다.

 

디바이스 로스트가 일어났는지는 ID3D11Device::GetDeivceRemovedReason() 함수를 호출하여 조사 할 수 있다.

 

반환값이 S_OK가 아닌 경우 몇가지 이유로인해 디바이스 로스트가 발생한다

 

 

  • DXGI_ERROR_DEVICE_HUNG : 어플리케이션에서 보낸 잘못된 형식의 명령으로 인해 디바이스가 실패했다.
  • DXGI_ERROR_DEVICE_RESET : 잘못된 형식의 명령으로 인해 디바이스가 실패했다.                                                                           디바이스가 커맨드 실행에 타당하지 않은 시간을 필요로하고있는  하드웨어 크래시/행 상태이다
  • DXGI_ERROR_DEVICE_REMOVED : 그래픽스 아답터(비디오 카드)의 플러그&플레이가 정지되었다.                                                               아답터가 이용불능 상태일 가능성이 높음

 

 

  • DXGI_ERROR_DRIVER_INTERVAL_ERROR : 드라이버가 정의되지 않은 오퍼레이션을 수행했다
  • DXGI_ERROR_INVALID_CALL  : 어플리케이션이 유효하지 않은 파라미터를 제공했다.

 

 

 

DXGI_ERROR_DEVICE_HUNG  DXGI_ERROR_DEVICE_RESET 일 경우에는 DirectX 11 디바이스나 

오브젝트를 재생성함으로 복구할수 도 있다

 

DXGI_ERROR_DEVICE_REMOVED , DXGI_ERROR_INVALID_CALL  는 어플리케이션측에서 어쩔수없는 부분이다

 


BOOL  CheckDeviceLost()

{
    HRESULT hr;

    hr = g_pd3dDevice->GetDeviceRemovedReason();     //디바이스 로스트가 일어났는지 조사

    switch(hr)
    {
        case S_OK :                                                        //정상
        break;

        case DXGI_ERROR_DEVICE_HUNG:
        case DXGI_ERROR_DEVICE_RESET:
            CleanupDevice();                                             // device Release
            InitDevice();                                                    //  device Init

            if(FAILED(hr))
                return FALSE;                                             //실패. 어플리케이션 종료
        break;

        case DXGI_ERROR_DEVICE_REMOVED:
        case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
        case DXGI_ERROR_INVALID_CALL:
        default:
            return FALSE;                                               //실패. 어플리케이션 종료
    }
    return TRUE;
}

 

 

더 자세한 부분은 MSDN을 참조하길..

http://msdn.microsoft.com/en-us/library/windows/desktop/bb509553(v=vs.85).aspx



출처 : http://blog.naver.com/PostView.nhn?blogId=rhkd2060&logNo=140203208989&parentCategoryNo=&categoryNo=20&viewDate=&isShowPopularPosts=true&from=search

728x90
728x90

모두가 알고있듯이 OMSetRenderTargets()의 사용법은 아래와 같다.


m_pImmediateContext->OMSetRenderTargets(1, &pRenderTarget_1, nullptr);


Rendering();


m_pImmediateContext->OMSetRenderTargets(1, &pRenderTarget_2, nullptr);


Rendering();



이 DeferredContext에서도 동일하게 OMSetRenderTargets() 함수가 있었기 때문에, 아무 생각 없이 아래와 같이 만들었었다.


m_pImmediateContext->OMSetRenderTargets(1, &pRenderTarget_1, nullptr);

for (int i = 0; i < MAX_DEFERRED_COUNT; ++i)

    m_pDeferredContext[i]->OMSetRenderTargets(1, &pRenderTarget_1, nullptr);


Rendering();


m_pImmediateContext->OMSetRenderTargets(1, &pRenderTarget_2, nullptr);

for (int i = 0; i < MAX_DEFERRED_COUNT; ++i)

    m_pDeferredContext[i]->OMSetRenderTargets(1, &pRenderTarget_2, nullptr);


Rendering();



그랬더니, 엄청난 메모리 증가량을 볼 수 있었다. ㅎㅎ....


결국 확인해 보니, DeferredContext에 대해서는 별도의 OMSetRenderTargets()를 설정하지 않아도 문제없이


여러 개의 RenderTarget에 그리는 것을 확인하였다.

728x90
728x90

Deferred Context를 이용하여 텍스처를 수정하고 렌더링하는 과정에서 


D3D11 WARNING: ID3D11Device::RemoveContext: Deferred Context removal has been triggered for the following reason (어쩌구 저쩌구...)


라는 엄청 긴 메세지와 함께 프로그램은 실행되고 있지만, DebugView에서 엄청나게 많은 메세지를 찍어대면서 몇개의 영상이 멈추었다.




그래서 인터넷에 찾아보니 ID3D11CommandList는 SAFE_RELEASE()를 매크로를 사용하지말고 그냥 Release() 함수를 사용해야만 한다고 한다.


// Deferred Execute

for (int i = 0; i < DEFERRED_COUNT; ++i)

{

    if (m_pDX11DisplayProperty->m_pRenderingCommandList[i] != nullptr)

    {

        DeviceContext->ExecuteCommandList(m_pDX11DisplayProperty->m_pRenderingCommandList[i], TRUE);

        //SAFE_RELEASE(m_pDX11DisplayProperty->m_pRenderingCommandList[i]);     // <-- 이렇게 사용하면 안됨.

        m_pDX11DisplayProperty->m_pRenderingCommandList[i]->Release();

    }


    //m_pUpdateClass[i].m_pKeyedMutex->ReleaseSync(uRelKey);

}


출처 : https://community.amd.com/thread/128535



위에 이야기는 테스트 결과로 봤을 때 잘못된 정보였다 !!!!


결과만 말하면 FinishCommandList()가 ExecuteCommandList()보다 많이 호출되어 CommandList의 데이터가 소모가 되지 않고 쌓이기 때문에 Memory Leak 처럼 보인 것이다.


항상 ExecuteCommandList()가 FinishCommandList() 보다 더 많이 호출될 수 있게 하도록 하자.

728x90
728x90

Windows에서 할 일 


CreateWindow()를 한 이후에 SetLayeredWindowAttributes()를 호출 할 것.


WS_EX_LAYERED를 꼭 넣어야 한다.


DWORD dwWindowStyle = WS_POPUPWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

hWnd = CreateWindow(this->m_lpszTitle, this->m_lpszTitle, dwWindowStyle,

  nStartX, nStartY, nWidth, nHeight, NULL, NULL, hInstance, NULL);


if (!hWnd)

{

    return FALSE;

}

 

::SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TOPMOST);

::SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);

::SetLayeredWindowAttributes(hWnd, 0x0000ff00, 255, LWA_COLORKEY);





DirectX9에서 할 일


CreateOffscreenPlainSurface()로 Surface를 생성.


Clear()를 할 때에는 SetLayeredWindowAttributes()에서 지정한 색상으로 할 것.


Present() 사용하지 말 것.


BitBlt()을 이용하여 윈도우 DC에 RenderTarget DC를 그릴 것.


소스 코드 : 

Banner_dx9_test.zip


728x90
728x90

DirectX 11에서는 Resource에 데이터를 넣는 함수는 4개가 존재한다.


UpdateSubresource(), Map() ~ Unmap(), CopyResource(), CopySubresourceRegion()


이 함수들은 자주 사용되는 만큼 어느 정도는 알고 사용해야 할 것 같아서 정리한다.




우선 함수의 속도를 보면 UpdateSubresource()는 웬만하면 매 프레임마다 사용하지 않는 것이 좋다는 생각이 들 것이다.



CPU -> GPU로 데이터를 복사할 때 사용하는 함수는 UpdateSubresource()와 Map() ~ Unmap()을 사용하게 된다. 


Map() ~ Unmap()의 경우 개발자가 직접 Lock() ~ Unlock()을 하며 memcpy()를 이용해서 Resource에 데이터를 직접 넣는 방법이고, UpdateSubresource()는 함수 호출만으로 내부에서 Lock() ~ Unlock()을 해주기 때문에 나의 경우 UpdateSubresource()를 더 즐겨 사용하는 편이다.


그리고 기본적으로 Lock() ~ Unlock()이 걸리게 되므로, 하나의 Resource에 Thread를 이용하여 동시에 CPU -> GPU로 복사는 불가능하다.



GPU -> GPU로 데이터를 복사할 때 사용하는 함수는 CopyResource()와 CopySubresourceRegion()가 있다. 


이 함수의 차이는 CopyResource()는 Full Image를 그대로 복사하고, CopySubresourceRegion()는 영역을 지정하여 복사가 가능한 것이다.


이 두개의 함수는 기본적으로 비동기 함수이다. 그렇기 때문에 하나의 Resource에 Thread를 이용하여 GPU -> GPU 복사는 얼마든지 가능하다.


테스트를 해보았을 때에는 Frame Rate는 거의 차이가 없었다.



국내 사이트에서는 자료가 없기에 해외 사이트를 검색하여 얻은 자료를 토대로 테스트 결과를 정리해보았다.


참조 사이트 : https://eatplayhate.me/2013/09/29/d3d11-texture-update-costs/

728x90
728x90

책들을 봐도 잘 이해가 안되는 것 같아서 따로 찾아보다가 좋은 예시를 찾았다...


큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 이다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에 ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정한다.


[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}


결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 된다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 한다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘어오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 된다.

예를 들어, 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있다.


728x90
728x90

DirectCompute에는 다양한 메모리 모형이 존재한다. 이것들을 한번 살펴보자.


레지스터 기반 메모리

계산 셰이더는 다른 프로그래밍 가능 셰이더 단계들과 동일한 하드웨어(프로그래밍이 가능한 처리기)에서 실행된다. 따라서 계산 셰이더 단계에서도 다른 프로그램 가능 셰이더 단계들에서와 동일한 일반적인 처리 패러다임이 적용되며, 공통 셰이더 코어를 구현한다. 계산 셰이더 단계는 다른 파이프라인 단계들과 비슷한 레지스터 기반 처리 개념을 사용한다.

계산 셰이더는 입력 특성 레지스터들(v#)과 텍스처 레지스터들(t#), 상수 버퍼 레지스터들(cb#), 순서 없는 접근 레지스터들(u#), 임시 레지스터들(r#, x#)을 지원한다.


임시 레지스터들은 셰이더 프로그램의 실행 도중 중간 계산 결과를 담는 데 주로 쓰인다.

임시 레지스터는 현재 실행 중인 스레드에서만 접근할 수 있으며, 일반적으로 접근이 극도로 빠르다.

공통 셰이더 코어에는 임시 레지스터를 최대 4096개까지(r#, x# 합쳐서) 지정할 수 있다.

일반적으로 셰이더 코어에 적재된 자료는 최대한 이 임시 레지스터들에 저장된다.


장치 메모리

레지스터 기반 메모리는 아주 빠르긴 하지만 용량이 매우 작다. 또한, 사용할 자료를 먼저 셰이더 코어에 적재한 후에야 레지스터들을 사용할 수 있으며, 스레드의 셰이더 프로그램 실행이 끝나면 레지스터 내용이 초기화된다. 계산 파이프라인을 본격적으로 활용하려면 레지스터 기반 메모리에 담을 수 있는 것보다 훨씬 큰 자원이 필요하며, 또한 셰이더 프로그램의 실행들 사이에서 계속 유지되는 자원도 필요하다. 그런 자원들은 장치 메모리에 담는다. 그리고 그런 자원들을 다른 종류의 메모리에 저장되는 자원과 구분해서 흔히 장치 메모리 자원이라고 부른다. 

728x90

+ Recent posts