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