d3d11_stream_buffer.cpp (4785B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) 3 4 #include "d3d11_stream_buffer.h" 5 #include "d3d11_device.h" 6 7 #include "common/align.h" 8 #include "common/assert.h" 9 #include "common/error.h" 10 #include "common/log.h" 11 12 Log_SetChannel(D3D11Device); 13 14 D3D11StreamBuffer::D3D11StreamBuffer() 15 { 16 } 17 18 D3D11StreamBuffer::D3D11StreamBuffer(ComPtr<ID3D11Buffer> buffer) : m_buffer(std::move(buffer)) 19 { 20 D3D11_BUFFER_DESC desc; 21 m_buffer->GetDesc(&desc); 22 m_size = desc.ByteWidth; 23 } 24 25 D3D11StreamBuffer::~D3D11StreamBuffer() 26 { 27 Destroy(); 28 } 29 30 bool D3D11StreamBuffer::Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max_size) 31 { 32 D3D11_FEATURE_DATA_D3D11_OPTIONS options = {}; 33 HRESULT hr = D3D11Device::GetD3DDevice()->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); 34 if (SUCCEEDED(hr)) [[likely]] 35 { 36 if (bind_flags & D3D11_BIND_CONSTANT_BUFFER) 37 { 38 // Older Intel drivers go absolutely bananas with CPU usage when using offset constant buffers. 39 // NVIDIA seems to be okay, I don't know about AMD. So let's be safe and limit it to feature level 12+. 40 m_use_map_no_overwrite = options.MapNoOverwriteOnDynamicConstantBuffer; 41 if (m_use_map_no_overwrite && D3D11Device::GetMaxFeatureLevel() < D3D_FEATURE_LEVEL_12_0) 42 { 43 WARNING_LOG("Ignoring MapNoOverwriteOnDynamicConstantBuffer on driver due to feature level."); 44 m_use_map_no_overwrite = false; 45 } 46 47 // should be 16 byte aligned 48 min_size = Common::AlignUpPow2(min_size, 16); 49 max_size = Common::AlignUpPow2(max_size, 16); 50 } 51 else if (bind_flags & D3D11_BIND_SHADER_RESOURCE) 52 m_use_map_no_overwrite = options.MapNoOverwriteOnDynamicBufferSRV; 53 else 54 m_use_map_no_overwrite = true; 55 56 if (!m_use_map_no_overwrite) 57 { 58 WARNING_LOG("Unable to use MAP_NO_OVERWRITE on buffer with bind flag {}, this may affect performance. " 59 "Update your driver/operating system.", 60 static_cast<unsigned>(bind_flags)); 61 } 62 } 63 else 64 { 65 WARNING_LOG("ID3D11Device::CheckFeatureSupport() failed: {}", Error::CreateHResult(hr).GetDescription()); 66 m_use_map_no_overwrite = false; 67 } 68 69 const u32 create_size = m_use_map_no_overwrite ? max_size : min_size; 70 const CD3D11_BUFFER_DESC desc(create_size, bind_flags, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE, 0, 0); 71 ComPtr<ID3D11Buffer> buffer; 72 hr = D3D11Device::GetD3DDevice()->CreateBuffer(&desc, nullptr, &buffer); 73 if (FAILED(hr)) [[unlikely]] 74 { 75 ERROR_LOG("Creating buffer failed: {}", Error::CreateHResult(hr).GetDescription()); 76 return false; 77 } 78 79 m_buffer = std::move(buffer); 80 m_size = create_size; 81 m_max_size = max_size; 82 m_position = 0; 83 84 return true; 85 } 86 87 void D3D11StreamBuffer::Destroy() 88 { 89 m_buffer.Reset(); 90 } 91 92 D3D11StreamBuffer::MappingResult D3D11StreamBuffer::Map(ID3D11DeviceContext1* context, u32 alignment, u32 min_size) 93 { 94 HRESULT hr; 95 DebugAssert(!m_mapped); 96 97 m_position = Common::AlignUp(m_position, alignment); 98 if ((m_position + min_size) >= m_size || !m_use_map_no_overwrite) 99 { 100 // wrap around 101 m_position = 0; 102 103 // grow buffer if needed 104 if (min_size > m_size) [[unlikely]] 105 { 106 Assert(min_size < m_max_size); 107 108 const u32 new_size = std::min(m_max_size, Common::AlignUp(std::max(m_size * 2, min_size), alignment)); 109 WARNING_LOG("Growing buffer from {} bytes to {} bytes", m_size, new_size); 110 111 D3D11_BUFFER_DESC new_desc; 112 m_buffer->GetDesc(&new_desc); 113 new_desc.ByteWidth = new_size; 114 115 hr = D3D11Device::GetD3DDevice()->CreateBuffer(&new_desc, nullptr, m_buffer.ReleaseAndGetAddressOf()); 116 if (FAILED(hr)) [[unlikely]] 117 { 118 ERROR_LOG("Creating buffer failed: {}", Error::CreateHResult(hr).GetDescription()); 119 Panic("Failed to grow buffer"); 120 } 121 122 m_size = new_size; 123 } 124 } 125 126 D3D11_MAPPED_SUBRESOURCE sr; 127 const D3D11_MAP map_type = (m_position == 0) ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; 128 hr = context->Map(m_buffer.Get(), 0, map_type, 0, &sr); 129 if (FAILED(hr)) [[unlikely]] 130 { 131 ERROR_LOG("Map failed: 0x{:08X} (alignment {}, minsize {}, size {}, position [], map type {})", 132 static_cast<unsigned>(hr), alignment, min_size, m_size, m_position, static_cast<u32>(map_type)); 133 Panic("Map failed"); 134 } 135 136 m_mapped = true; 137 return MappingResult{static_cast<char*>(sr.pData) + m_position, m_position, m_position / alignment, 138 (m_size - m_position) / alignment}; 139 } 140 141 void D3D11StreamBuffer::Unmap(ID3D11DeviceContext1* context, u32 used_size) 142 { 143 DebugAssert(m_mapped); 144 145 context->Unmap(m_buffer.Get(), 0); 146 m_position += used_size; 147 m_mapped = false; 148 }