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