1 /*
2 * This file is part of libplacebo.
3 *
4 * libplacebo is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * libplacebo is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "gpu.h"
19 #include "formats.h"
20
pl_d3d11_buf_destroy(pl_gpu gpu,pl_buf buf)21 void pl_d3d11_buf_destroy(pl_gpu gpu, pl_buf buf)
22 {
23 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
24 struct d3d11_ctx *ctx = p->ctx;
25 struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
26
27 SAFE_RELEASE(buf_p->buf);
28 SAFE_RELEASE(buf_p->staging);
29 SAFE_RELEASE(buf_p->raw_srv);
30 SAFE_RELEASE(buf_p->raw_uav);
31 SAFE_RELEASE(buf_p->texel_srv);
32 SAFE_RELEASE(buf_p->texel_uav);
33
34 pl_d3d11_flush_message_queue(ctx, "After buffer destroy");
35
36 pl_free((void *) buf);
37 }
38
pl_d3d11_buf_create(pl_gpu gpu,const struct pl_buf_params * params)39 pl_buf pl_d3d11_buf_create(pl_gpu gpu, const struct pl_buf_params *params)
40 {
41 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
42 struct d3d11_ctx *ctx = p->ctx;
43
44 struct pl_buf *buf = pl_zalloc_obj(NULL, buf, struct pl_buf_d3d11);
45 buf->params = *params;
46 buf->params.initial_data = NULL;
47
48 struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
49
50 D3D11_BUFFER_DESC desc = { .ByteWidth = params->size };
51
52 if (params->uniform && !params->format &&
53 (params->storable || params->drawable))
54 {
55 // TODO: Figure out what to do with these
56 PL_ERR(gpu, "Uniform buffers cannot share any other buffer type");
57 goto error;
58 }
59
60 // TODO: Distinguish between uniform buffers and texel uniform buffers.
61 // Currently we assume that if uniform and format are set, it's a texel
62 // buffer and NOT a uniform buffer.
63 if (params->uniform && !params->format) {
64 desc.BindFlags |= D3D11_BIND_CONSTANT_BUFFER;
65 desc.ByteWidth = PL_ALIGN2(desc.ByteWidth, CBUF_ELEM);
66 }
67 if (params->uniform && params->format) {
68 desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
69 }
70 if (params->storable) {
71 desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS
72 | D3D11_BIND_SHADER_RESOURCE;
73 desc.ByteWidth = PL_ALIGN2(desc.ByteWidth, sizeof(float));
74 desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
75 }
76 if (params->drawable) {
77 desc.BindFlags |= D3D11_BIND_VERTEX_BUFFER;
78
79 // In FL9_x, a vertex buffer can't also be an index buffer, so index
80 // buffers are unsupported in FL9_x for now
81 if (p->fl > D3D_FEATURE_LEVEL_9_3)
82 desc.BindFlags |= D3D11_BIND_INDEX_BUFFER;
83 }
84
85 char *data = NULL;
86
87 // D3D11 doesn't allow partial constant buffer updates without special
88 // conditions. To support partial buffer updates, keep a mirror of the
89 // buffer data in system memory and upload the whole thing before the buffer
90 // is used.
91 //
92 // Note: We don't use a staging buffer for this because of Intel.
93 // https://github.com/mpv-player/mpv/issues/5293
94 // https://crbug.com/593024
95 if (params->uniform && !params->format && params->host_writable) {
96 data = pl_zalloc(buf, desc.ByteWidth);
97 buf_p->data = data;
98 }
99
100 D3D11_SUBRESOURCE_DATA srdata = { 0 };
101 if (params->initial_data) {
102 if (desc.ByteWidth != params->size) {
103 // If the size had to be rounded-up, uploading from
104 // params->initial_data is technically undefined behavior, so copy
105 // the initial data to an allocation first
106 if (!data)
107 data = pl_zalloc(buf, desc.ByteWidth);
108 srdata.pSysMem = data;
109 } else {
110 srdata.pSysMem = params->initial_data;
111 }
112
113 if (data)
114 memcpy(data, params->initial_data, params->size);
115 }
116
117 D3D(ID3D11Device_CreateBuffer(p->dev, &desc,
118 params->initial_data ? &srdata : NULL,
119 &buf_p->buf));
120
121 if (!buf_p->data)
122 pl_free(data);
123
124 // Create raw views for PL_DESC_BUF_STORAGE
125 if (params->storable) {
126 // A SRV is used for PL_DESC_ACCESS_READONLY
127 D3D11_SHADER_RESOURCE_VIEW_DESC sdesc = {
128 .Format = DXGI_FORMAT_R32_TYPELESS,
129 .ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX,
130 .BufferEx = {
131 .NumElements =
132 PL_ALIGN2(buf->params.size, sizeof(float)) / sizeof(float),
133 .Flags = D3D11_BUFFEREX_SRV_FLAG_RAW,
134 },
135 };
136 D3D(ID3D11Device_CreateShaderResourceView(p->dev,
137 (ID3D11Resource *) buf_p->buf, &sdesc, &buf_p->raw_srv));
138
139 // A UAV is used for all other access modes
140 D3D11_UNORDERED_ACCESS_VIEW_DESC udesc = {
141 .Format = DXGI_FORMAT_R32_TYPELESS,
142 .ViewDimension = D3D11_UAV_DIMENSION_BUFFER,
143 .Buffer = {
144 .NumElements =
145 PL_ALIGN2(buf->params.size, sizeof(float)) / sizeof(float),
146 .Flags = D3D11_BUFFER_UAV_FLAG_RAW,
147 },
148 };
149 D3D(ID3D11Device_CreateUnorderedAccessView(p->dev,
150 (ID3D11Resource *) buf_p->buf, &udesc, &buf_p->raw_uav));
151 }
152
153 // Create a typed SRV for PL_BUF_TEXEL_UNIFORM and PL_BUF_TEXEL_STORAGE
154 if (params->format) {
155 D3D11_SHADER_RESOURCE_VIEW_DESC sdesc = {
156 .Format = fmt_to_dxgi(params->format),
157 .ViewDimension = D3D11_SRV_DIMENSION_BUFFER,
158 };
159 D3D(ID3D11Device_CreateShaderResourceView(p->dev,
160 (ID3D11Resource *) buf_p->buf, &sdesc, &buf_p->texel_srv));
161
162 // Create a typed UAV for PL_BUF_TEXEL_STORAGE
163 if (params->storable) {
164 D3D11_UNORDERED_ACCESS_VIEW_DESC udesc = {
165 .Format = fmt_to_dxgi(buf->params.format),
166 .ViewDimension = D3D11_UAV_DIMENSION_BUFFER,
167 };
168 D3D(ID3D11Device_CreateUnorderedAccessView(p->dev,
169 (ID3D11Resource *) buf_p->buf, &udesc, &buf_p->texel_uav));
170 }
171 }
172
173
174 if (!buf_p->data) {
175 // Create the staging buffer regardless of whether params->host_readable
176 // is set or not, so that buf_copy can copy to system-memory-backed
177 // buffers
178 // TODO: Consider sharing a big staging buffer for this, rather than
179 // having one staging buffer per buffer
180 desc.BindFlags = 0;
181 desc.MiscFlags = 0;
182 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
183 desc.Usage = D3D11_USAGE_STAGING;
184 D3D(ID3D11Device_CreateBuffer(p->dev, &desc, NULL, &buf_p->staging));
185 }
186
187 pl_d3d11_flush_message_queue(ctx, "After buffer create");
188
189 return buf;
190
191 error:
192 pl_d3d11_buf_destroy(gpu, buf);
193 return NULL;
194 }
195
pl_d3d11_buf_write(pl_gpu gpu,pl_buf buf,size_t offset,const void * data,size_t size)196 void pl_d3d11_buf_write(pl_gpu gpu, pl_buf buf, size_t offset, const void *data,
197 size_t size)
198 {
199 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
200 struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
201
202 if (buf_p->data) {
203 memcpy(buf_p->data + offset, data, size);
204 buf_p->dirty = true;
205 } else {
206 ID3D11DeviceContext_UpdateSubresource(p->imm,
207 (ID3D11Resource *) buf_p->buf, 0, (&(D3D11_BOX) {
208 .left = offset,
209 .top = 0,
210 .front = 0,
211 .right = offset + size,
212 .bottom = 1,
213 .back = 1,
214 }), data, 0, 0);
215 }
216 }
217
pl_d3d11_buf_resolve(pl_gpu gpu,pl_buf buf)218 void pl_d3d11_buf_resolve(pl_gpu gpu, pl_buf buf)
219 {
220 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
221 struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
222
223 if (!buf_p->data || !buf_p->dirty)
224 return;
225
226 ID3D11DeviceContext_UpdateSubresource(p->imm, (ID3D11Resource *) buf_p->buf,
227 0, NULL, buf_p->data, 0, 0);
228 }
229
pl_d3d11_buf_read(pl_gpu gpu,pl_buf buf,size_t offset,void * dest,size_t size)230 bool pl_d3d11_buf_read(pl_gpu gpu, pl_buf buf, size_t offset, void *dest,
231 size_t size)
232 {
233 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
234 struct d3d11_ctx *ctx = p->ctx;
235 struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
236
237 // If there is a system-memory mirror of the buffer contents, use it
238 if (buf_p->data) {
239 memcpy(dest, buf_p->data + offset, size);
240 return true;
241 }
242
243 ID3D11DeviceContext_CopyResource(p->imm, (ID3D11Resource *) buf_p->staging,
244 (ID3D11Resource *) buf_p->buf);
245
246 D3D11_MAPPED_SUBRESOURCE lock;
247 D3D(ID3D11DeviceContext_Map(p->imm, (ID3D11Resource *) buf_p->staging, 0,
248 D3D11_MAP_READ, 0, &lock));
249
250 char *csrc = lock.pData;
251 memcpy(dest, csrc + offset, size);
252
253 ID3D11DeviceContext_Unmap(p->imm, (ID3D11Resource *) buf_p->staging, 0);
254
255 pl_d3d11_flush_message_queue(ctx, "After buffer read");
256
257 return true;
258
259 error:
260 return false;
261 }
262
pl_d3d11_buf_copy(pl_gpu gpu,pl_buf dst,size_t dst_offset,pl_buf src,size_t src_offset,size_t size)263 void pl_d3d11_buf_copy(pl_gpu gpu, pl_buf dst, size_t dst_offset, pl_buf src,
264 size_t src_offset, size_t size)
265 {
266 struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
267 struct d3d11_ctx *ctx = p->ctx;
268 struct pl_buf_d3d11 *src_p = PL_PRIV(src);
269 struct pl_buf_d3d11 *dst_p = PL_PRIV(dst);
270
271 // Handle system memory copies in case one or both of the buffers has a
272 // system memory mirror
273 if (src_p->data && dst_p->data) {
274 memcpy(dst_p->data + dst_offset, src_p->data + src_offset, size);
275 dst_p->dirty = true;
276 } else if (src_p->data) {
277 pl_d3d11_buf_write(gpu, dst, dst_offset, src_p->data + src_offset, size);
278 } else if (dst_p->data) {
279 if (pl_d3d11_buf_read(gpu, src, src_offset, dst_p->data + dst_offset, size)) {
280 dst_p->dirty = true;
281 } else {
282 PL_ERR(gpu, "Failed to read from GPU during buffer copy");
283 }
284 } else {
285 ID3D11DeviceContext_CopySubresourceRegion(p->imm,
286 (ID3D11Resource *) dst_p->buf, 0, dst_offset, 0, 0,
287 (ID3D11Resource *) src_p->buf, 0, (&(D3D11_BOX) {
288 .left = src_offset,
289 .top = 0,
290 .front = 0,
291 .right = src_offset + size,
292 .bottom = 1,
293 .back = 1,
294 }));
295 }
296
297 pl_d3d11_flush_message_queue(ctx, "After buffer copy");
298 }
299