duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }