//-------------------------------------------------------------------------------------- // File: MSAAHelper.cpp // // Helper for managing MSAA render targets (DirectX 12 version) // // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. //------------------------------------------------------------------------------------- #include "pch.h" #include "MSAAHelper.h" #include "DirectXHelpers.h" #include #include #include #include #include using namespace DirectX; using namespace DX; using Microsoft::WRL::ComPtr; #ifdef __MINGW32__ #define DX_CONSTEXPR const #else #define DX_CONSTEXPR constexpr #endif MSAAHelper::MSAAHelper(DXGI_FORMAT backBufferFormat, DXGI_FORMAT depthBufferFormat, unsigned int sampleCount) noexcept(false) : m_clearColor{}, m_rtvDescriptorHeap{}, m_dsvDescriptorHeap{}, m_backBufferFormat(backBufferFormat), m_depthBufferFormat(depthBufferFormat), m_sampleCount(0), m_targetSampleCount(sampleCount), m_width(0), m_height(0) { if (sampleCount < 2 || sampleCount > D3D12_MAX_MULTISAMPLE_SAMPLE_COUNT) { throw std::out_of_range("MSAA sample count invalid."); } } void MSAAHelper::SetDevice(_In_ ID3D12Device* device) { if (device == m_device.Get()) return; if (m_device) { ReleaseDevice(); } { D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { m_backBufferFormat, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE }; if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) { throw std::exception(); } DX_CONSTEXPR UINT required = D3D12_FORMAT_SUPPORT1_RENDER_TARGET | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RESOLVE | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RENDERTARGET; if ((formatSupport.Support1 & required) != required) { #ifdef _DEBUG char buff[128] = {}; sprintf_s(buff, "MSAAHelper: Device does not support MSAA for requested backbuffer format (%d)!\n", m_backBufferFormat); OutputDebugStringA(buff); #endif throw std::exception(); } } if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) { D3D12_FEATURE_DATA_FORMAT_SUPPORT formatSupport = { m_depthBufferFormat, D3D12_FORMAT_SUPPORT1_NONE, D3D12_FORMAT_SUPPORT2_NONE }; if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &formatSupport, sizeof(formatSupport)))) { throw std::exception(); } DX_CONSTEXPR UINT required = D3D12_FORMAT_SUPPORT1_DEPTH_STENCIL | D3D12_FORMAT_SUPPORT1_MULTISAMPLE_RENDERTARGET; if ((formatSupport.Support1 & required) != required) { #ifdef _DEBUG char buff[128] = {}; sprintf_s(buff, "MSAAHelper: Device does not support MSAA for requested depth/stencil format (%d)!\n", m_depthBufferFormat); OutputDebugStringA(buff); #endif throw std::exception(); } } for (m_sampleCount = m_targetSampleCount; m_sampleCount > 1; m_sampleCount--) { D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS levels = { m_backBufferFormat, m_sampleCount, D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE, 0u }; if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &levels, sizeof(levels)))) continue; if (levels.NumQualityLevels > 0) break; } if (m_sampleCount < 2) { #ifdef _DEBUG OutputDebugStringA("MSAAHelper: Device does not support MSAA!\n"); #endif throw std::exception(); } // Create descriptor heaps for render target views and depth stencil views. D3D12_DESCRIPTOR_HEAP_DESC rtvDescriptorHeapDesc = {}; rtvDescriptorHeapDesc.NumDescriptors = 1; rtvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ThrowIfFailed(device->CreateDescriptorHeap(&rtvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_rtvDescriptorHeap.ReleaseAndGetAddressOf()))); m_rtvDescriptorHeap->SetName(L"MSAAHelper"); if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) { D3D12_DESCRIPTOR_HEAP_DESC dsvDescriptorHeapDesc = {}; dsvDescriptorHeapDesc.NumDescriptors = 1; dsvDescriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; ThrowIfFailed(device->CreateDescriptorHeap(&dsvDescriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(m_dsvDescriptorHeap.ReleaseAndGetAddressOf()))); m_dsvDescriptorHeap->SetName(L"MSAAHelper"); } m_device = device; } void MSAAHelper::SizeResources(size_t width, size_t height) { if (width == m_width && height == m_height) return; if (m_width > UINT32_MAX || m_height > UINT32_MAX) { throw std::out_of_range("Invalid width/height"); } if (!m_device) return; m_width = m_height = 0; const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT); // Create an MSAA render target D3D12_RESOURCE_DESC msaaRTDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_backBufferFormat, static_cast(width), static_cast(height), 1, // This render target view has only one texture. 1, // Use a single mipmap level m_sampleCount ); msaaRTDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; D3D12_CLEAR_VALUE msaaOptimizedClearValue = {}; msaaOptimizedClearValue.Format = m_backBufferFormat; memcpy(msaaOptimizedClearValue.Color, m_clearColor, sizeof(float) * 4); ThrowIfFailed(m_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &msaaRTDesc, D3D12_RESOURCE_STATE_RESOLVE_SOURCE, &msaaOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(m_msaaRenderTarget.ReleaseAndGetAddressOf()) )); m_msaaRenderTarget->SetName( L"MSAA Render Target" ); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; rtvDesc.Format = m_backBufferFormat; rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; #if defined(_MSC_VER) || !defined(_WIN32) auto hCPU = m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); #else D3D12_CPU_DESCRIPTOR_HANDLE hCPU; std::ignore = m_rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(&hCPU); #endif m_device->CreateRenderTargetView( m_msaaRenderTarget.Get(), &rtvDesc, hCPU); if (m_depthBufferFormat != DXGI_FORMAT_UNKNOWN) { // Create an MSAA depth stencil view D3D12_RESOURCE_DESC depthStencilDesc = CD3DX12_RESOURCE_DESC::Tex2D( m_depthBufferFormat, static_cast(width), static_cast(height), 1, // This depth stencil view has only one texture. 1, // Use a single mipmap level. m_sampleCount ); depthStencilDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; depthOptimizedClearValue.Format = m_depthBufferFormat; depthOptimizedClearValue.DepthStencil.Depth = 1.0f; depthOptimizedClearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &depthStencilDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &depthOptimizedClearValue, IID_GRAPHICS_PPV_ARGS(m_msaaDepthStencil.ReleaseAndGetAddressOf()) )); D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.Format = m_depthBufferFormat; dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; #if defined(_MSC_VER) || !defined(_WIN32) hCPU = m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); #else std::ignore = m_dsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(&hCPU); #endif m_device->CreateDepthStencilView( m_msaaDepthStencil.Get(), &dsvDesc, hCPU); } m_width = width; m_height = height; } void MSAAHelper::ReleaseDevice() { m_rtvDescriptorHeap.Reset(); m_dsvDescriptorHeap.Reset(); m_msaaRenderTarget.Reset(); m_msaaDepthStencil.Reset(); m_device.Reset(); m_width = m_height = 0; } void MSAAHelper::Prepare(_In_ ID3D12GraphicsCommandList* commandList, D3D12_RESOURCE_STATES beforeState) { const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( m_msaaRenderTarget.Get(), beforeState, D3D12_RESOURCE_STATE_RENDER_TARGET); commandList->ResourceBarrier(1, &barrier); } void MSAAHelper::Resolve(_In_ ID3D12GraphicsCommandList* commandList, _In_ ID3D12Resource* backBuffer, D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { const D3D12_RESOURCE_BARRIER barriers[2] = { CD3DX12_RESOURCE_BARRIER::Transition(m_msaaRenderTarget.Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_RESOLVE_SOURCE), CD3DX12_RESOURCE_BARRIER::Transition(backBuffer, beforeState, D3D12_RESOURCE_STATE_RESOLVE_DEST) }; commandList->ResourceBarrier((beforeState != D3D12_RESOURCE_STATE_RESOLVE_DEST) ? 2u : 1u, barriers); commandList->ResolveSubresource(backBuffer, 0, m_msaaRenderTarget.Get(), 0, m_backBufferFormat); if (afterState != D3D12_RESOURCE_STATE_RESOLVE_DEST) { const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( backBuffer, D3D12_RESOURCE_STATE_RESOLVE_DEST, afterState); commandList->ResourceBarrier(1, &barrier); } } void MSAAHelper::Transition(_In_ ID3D12GraphicsCommandList* commandList, D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState) { if (beforeState != afterState) { const D3D12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition( m_msaaRenderTarget.Get(), beforeState, afterState); commandList->ResourceBarrier(1, &barrier); } } void MSAAHelper::SetWindow(const RECT& output) { // Determine the render target size in pixels. const auto width = size_t(std::max(output.right - output.left, 1)); const auto height = size_t(std::max(output.bottom - output.top, 1)); SizeResources(width, height); }