Problem z multisamplingiem w DirectX11


#1

Witam :slight_smile:

Dawno tu nie zaglądałem. Pojawił się pewien problem u mnie przy tworzeniu biblioteki umożliwiającej odtwarzanie video jako tekstura na dowolnym modelu. Używam w tym celu IMFSourceReader. Ale w zasadzie nie o tym chciałem pisać tylko o tym, że wyszedł przy testowaniu tej biblioteki pewien szkopuł.

Problem dotyczy multisamplowania. Gdy ustawiam w DXGI_SWAP_CHAIN_DESC sampleDesc.Count na większe od 1 i quality większe od 0, np. para 4/16 lub 8/32 to D3D11CreateDeviceAndSwapChain wywala błąd _com_error at memory location.

Oczywiście aplikacja mimo to działa dalej i niczego bym nie zauważył, gdyby nie fakt, że przy użyciu mojej biblioteki gdy film wyświetla się jako tekstura na modelu - strasznie laguje środowisko 3D ale co ciekawe film leci płynnie.

Męczyłem się z tym kilka tygodni bo nie wiedziałem w czym rzecz i winę niesłusznie zrzucałem na bibliotekę odtwarzającą video jako teksturę. Okazuje się, że gdy wyłączę multisampling tzn. ustawię sampleDesc.Count=1 i sampleDesc.Quality=0 to problem znika. Wszystko działa płynnie, a wywołanie D3D11CreateDeviceAndSwapChain nie zwraca już żadnego błędu.

Dziś odkryłem kolejną rzecz. Początkowo myślałem, że po prostu włączenie multisamplingu muli aplikację przez video-teksturę - zwyczajnie komputer nie daje rady ale okazuje się że nie. Wyłączyłem multisamling w aplikacji ustawiając sampleDesc.Count=1 i sampleDesc.Quality=0, natomiast włączyłem multisampling w ustawieniach karty graficznej. Na maxa czyli 8/32.

Wchodzę i co widzę?

  • film odtwarza pięknie jako teksturę,
  • krawędzie obiektów są wygładzone, a więc multisampling działa,
  • i w dodatku wszystko chodzi płynnie!

WTF!?!?!

Wiem już, że problem dotyczy ustawienia sampleDesc.Quality i sampleDesc.Count i wywołania D3D11CreateDeviceAndSwapChain czyli włączenia multisamplingu z poziomu aplikacji.

Co może być nie tak? Ktoś miał taki przypadek?
Dlaczego D3D11CreateDeviceAndSwapChain zwraca _com_error przy sampleDesc.Quality i sampleDesc.Count większych od 1?


#2

Cześć jestem nowy na tym forum, miałem podobny problem jakieś dwa dni temu, korzystasz z C++/CLI oraz aplikacji UWP ?

Przede wszystkim jeżeli tak jest to powinieneś sprawdzić jaki poziom Quality obsługuje Tobie karta graficzna przy danym multisamplingu.

np. tak:
UINT m4xMsaaQuality; dev->CheckMultisampleQualityLevels(DXGI_FORMAT_B8G8R8A8_UNORM,8, &m4xMsaaQuality);

następnie musisz ogarnąć.
Jaki format interfejsu obsługiwany jest twoją kartą graficzną:

	for (UINT i = 1; i < DXGI_FORMAT_MAX; i++)
	{
		DXGI_FORMAT inFormat = safe_cast<DXGI_FORMAT>(i);
		UINT formatSupport = 0;
		HRESULT hr = dev->CheckFormatSupport(inFormat, &formatSupport);

		if ((formatSupport & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE) &&
			(formatSupport & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET)
			)
		{
			m_supportInfo->SetFormatSupport(i, true);
		}
		else
		{
			m_supportInfo->SetFormatSupport(i, false);
		}
	}

przyda Ci się do tego klasa pomocnicza:

private ref class MultisamplingSupportInfo sealed
{
internal:
	MultisamplingSupportInfo()
	{
		ZeroMemory(&sampleSizeData, sizeof(sampleSizeData));
		ZeroMemory(&qualityFlagData, sizeof(qualityFlagData));
	};

public:
	unsigned int GetSampleSize(int i, int j) { return sampleSizeData[i][j]; };
	void         SetSampleSize(int i, int j, unsigned int value) { sampleSizeData[i][j] = value; };

	unsigned int GetQualityFlagsAt(int i, int j) { return qualityFlagData[i][j]; };
	void         SetQualityFlagsAt(int i, int j, unsigned int value) { qualityFlagData[i][j] = value; };

	bool         GetFormatSupport(int format) { return formatSupport[format]; };
	void         SetFormatSupport(int format, bool value) { formatSupport[format] = value; };

	unsigned int GetFormat() { return m_format; };
	void         SetFormat(unsigned int i) { m_format = (DXGI_FORMAT)i; };

	unsigned int GetLargestSampleSize() { return m_largestSampleSize; };
	unsigned int GetSmallestSampleSize() { return m_smallestSampleSize; };

	void SetLargestSampleSize(unsigned int value) { m_largestSampleSize = value; };
	void SetSmallestSampleSize(unsigned int value) { m_smallestSampleSize = value; };

private:
	unsigned int sampleSizeData[DXGI_FORMAT_MAX][MAX_SAMPLES_CHECK];
	unsigned int qualityFlagData[DXGI_FORMAT_MAX][MAX_SAMPLES_CHECK];

	bool formatSupport[DXGI_FORMAT_MAX];

	unsigned int m_largestSampleSize;
	unsigned int m_smallestSampleSize;

	DXGI_FORMAT m_format;
};

no jak już uda się wypełnić tablice: formatSupport[]

to wypada teraz zobaczyć, jaki sampleSize ustawiony jest dla danego formatu i znowu pętla:

`for (unsigned int i = 0; i < DXGI_FORMAT_MAX; i++){
       for (unsigned int j = 1; j < MAX_SAMPLES_CHECK; j++){
        UINT numQualityFlags;

        HRESULT test = dev->CheckMultisampleQualityLevels(
        (DXGI_FORMAT)i,
        j,
        &numQualityFlags
        );

        if (SUCCEEDED(test) && (numQualityFlags > 0)){
        	m_supportInfo->SetSampleSize(i, j, 1);
        	m_supportInfo->SetQualityFlagsAt(i, j, numQualityFlags);
        }
    }
}`

No i teraz jest najgorszy problem gdyż aby użyć Mx4SAA, powinieneś użyć : DXGI_SWAP_EFFECT_DISCARD jako swapEffect, no ale niestety miedzy UWP (czli WinRT) a Win32 są drobne różnice. Powołując sie na MSDN:

The recommended approach is to manually convert DX11 Discard swap chains to use flip models within UWP, using DXGI_SWAP_EFFECT_FLIP_DISCARD instead of DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL where possible.

no dobra koniec końców jakie jest rozwiązanie? Należy stworzyć swapchain i na podstawie tego backbuffer, który nie obsługuje multisamplingu czyli taki:

`DXGI_SWAP_CHAIN_DESC1 scd = { 0 };
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;    // how the swap chain should be used
scd.BufferCount =2;      
	
scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;              // the most common swap chain format
scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;    // the recommended flip mode
scd.SampleDesc.Count = 1;                             // disable anti-aliasing
scd.SampleDesc.Quality = 0;

CoreWindow^ Window = CoreWindow::GetForCurrentThread();    // get the window pointer

swapchain = nullptr;										   // create the swap chain
dxgiFactory->CreateSwapChainForCoreWindow(
dev.Get(),                                  // address of the device
reinterpret_cast<IUnknown*>(Window),        // address of the window
&scd,                                       // address of the swap chain description
nullptr,                                    // advanced
&swapchain);                                // address of the new swap chain pointer
	
// get a pointer directly to the back buffer
ComPtr<ID3D11Texture2D> backbuffer;
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (&backbuffer));`

tworzysz backbuffer:

`CD3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc(D3D11_RTV_DIMENSION_TEXTURE2DMS);			
// create a render target pointing to the back buffer
dev->CreateRenderTargetView(backbuffer.Get(), &renderTargetViewDesc, &rendertarget);`	

a następnie tworzysz surface , który jednak będzie obsługiwał multisampling

`CoreWindow^ Window = CoreWindow::GetForCurrentThread();
D3D11_TEXTURE2D_DESC offScreenSurfaceDesc;
ZeroMemory(&offScreenSurfaceDesc, sizeof(D3D11_TEXTURE2D_DESC));
offScreenSurfaceDesc.Width = Window->Bounds.Width;
offScreenSurfaceDesc.Height = Window->Bounds.Height;
offScreenSurfaceDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
offScreenSurfaceDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
offScreenSurfaceDesc.MipLevels = 1;
offScreenSurfaceDesc.ArraySize = 1;
offScreenSurfaceDesc.SampleDesc.Count = 8;
offScreenSurfaceDesc.SampleDesc.Quality = m4xMsaaQuality - 1;

dev->CreateTexture2D(
	&offScreenSurfaceDesc,
	nullptr,
	&m_offScreenSurface);`

a następnie podmienić pod backbuffer (renderTarget się nie zmieni)

unsigned int sub = D3D11CalcSubresource(0, 0, 1); devcon->ResolveSubresource( backbuffer.Get(), sub, m_offScreenSurface.Get(), sub, DXGI_FORMAT_B8G8R8A8_UNORM );

To by było tyle, jeżeli chciałbyś zobaczyć kompletny przykład to jest tutaj https://github.com/toch88/UWP_DirectX11_Multisampling_Enable wyświetla “Triangle” z włączonym 8 krotnym samplingiem. W tym przykładzie jest jeszcze jeden problem w momencie gdy pierwszy raz włączysz aplikacje - pojawi ci się w małym okienku, gdy będziesz chciał go powiększyć będziesz widział wyraźne schodki, jak włączysz i wyłączysz włączysz z maksymalizacją to będzie wszystko ok. muszę popracować z Resizem.

Co prawda jest początkujący w temacie, ale jest to jakiś punkt wyjścia, chyba w polskim “community” nie wiele jest ludzi którzy się tym zajmują, wolą gotowe rozwiązania jak Unity.