summaryrefslogtreecommitdiffstats
path: root/src/d3d11/gpu_buf.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/d3d11/gpu_buf.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/d3d11/gpu_buf.c b/src/d3d11/gpu_buf.c
new file mode 100644
index 0000000..955e6e1
--- /dev/null
+++ b/src/d3d11/gpu_buf.c
@@ -0,0 +1,310 @@
+/*
+ * This file is part of libplacebo.
+ *
+ * libplacebo is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libplacebo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gpu.h"
+#include "formats.h"
+
+void pl_d3d11_buf_destroy(pl_gpu gpu, pl_buf buf)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
+
+ SAFE_RELEASE(buf_p->buf);
+ SAFE_RELEASE(buf_p->staging);
+ SAFE_RELEASE(buf_p->raw_srv);
+ SAFE_RELEASE(buf_p->raw_uav);
+ SAFE_RELEASE(buf_p->texel_srv);
+ SAFE_RELEASE(buf_p->texel_uav);
+
+ pl_d3d11_flush_message_queue(ctx, "After buffer destroy");
+
+ pl_free((void *) buf);
+}
+
+pl_buf pl_d3d11_buf_create(pl_gpu gpu, const struct pl_buf_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ struct pl_buf_t *buf = pl_zalloc_obj(NULL, buf, struct pl_buf_d3d11);
+ buf->params = *params;
+ buf->params.initial_data = NULL;
+
+ struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
+
+ D3D11_BUFFER_DESC desc = { .ByteWidth = params->size };
+
+ if (params->uniform && !params->format &&
+ (params->storable || params->drawable))
+ {
+ // TODO: Figure out what to do with these
+ PL_ERR(gpu, "Uniform buffers cannot share any other buffer type");
+ goto error;
+ }
+
+ // TODO: Distinguish between uniform buffers and texel uniform buffers.
+ // Currently we assume that if uniform and format are set, it's a texel
+ // buffer and NOT a uniform buffer.
+ if (params->uniform && !params->format) {
+ desc.BindFlags |= D3D11_BIND_CONSTANT_BUFFER;
+ desc.ByteWidth = PL_ALIGN2(desc.ByteWidth, CBUF_ELEM);
+ }
+ if (params->uniform && params->format) {
+ desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
+ }
+ if (params->storable) {
+ desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS
+ | D3D11_BIND_SHADER_RESOURCE;
+ desc.ByteWidth = PL_ALIGN2(desc.ByteWidth, sizeof(float));
+ desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
+ }
+ if (params->drawable) {
+ desc.BindFlags |= D3D11_BIND_VERTEX_BUFFER;
+
+ // In FL9_x, a vertex buffer can't also be an index buffer, so index
+ // buffers are unsupported in FL9_x for now
+ if (p->fl > D3D_FEATURE_LEVEL_9_3)
+ desc.BindFlags |= D3D11_BIND_INDEX_BUFFER;
+ }
+
+ char *data = NULL;
+
+ // D3D11 doesn't allow partial constant buffer updates without special
+ // conditions. To support partial buffer updates, keep a mirror of the
+ // buffer data in system memory and upload the whole thing before the buffer
+ // is used.
+ //
+ // Note: We don't use a staging buffer for this because of Intel.
+ // https://github.com/mpv-player/mpv/issues/5293
+ // https://crbug.com/593024
+ if (params->uniform && !params->format && params->host_writable) {
+ data = pl_zalloc(buf, desc.ByteWidth);
+ buf_p->data = data;
+ }
+
+ D3D11_SUBRESOURCE_DATA srdata = { 0 };
+ if (params->initial_data) {
+ if (desc.ByteWidth != params->size) {
+ // If the size had to be rounded-up, uploading from
+ // params->initial_data is technically undefined behavior, so copy
+ // the initial data to an allocation first
+ if (!data)
+ data = pl_zalloc(buf, desc.ByteWidth);
+ srdata.pSysMem = data;
+ } else {
+ srdata.pSysMem = params->initial_data;
+ }
+
+ if (data)
+ memcpy(data, params->initial_data, params->size);
+ }
+
+ D3D(ID3D11Device_CreateBuffer(p->dev, &desc,
+ params->initial_data ? &srdata : NULL,
+ &buf_p->buf));
+
+ if (!buf_p->data)
+ pl_free(data);
+
+ // Create raw views for PL_DESC_BUF_STORAGE
+ if (params->storable) {
+ // A SRV is used for PL_DESC_ACCESS_READONLY
+ D3D11_SHADER_RESOURCE_VIEW_DESC sdesc = {
+ .Format = DXGI_FORMAT_R32_TYPELESS,
+ .ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX,
+ .BufferEx = {
+ .NumElements =
+ PL_ALIGN2(buf->params.size, sizeof(float)) / sizeof(float),
+ .Flags = D3D11_BUFFEREX_SRV_FLAG_RAW,
+ },
+ };
+ D3D(ID3D11Device_CreateShaderResourceView(p->dev,
+ (ID3D11Resource *) buf_p->buf, &sdesc, &buf_p->raw_srv));
+
+ // A UAV is used for all other access modes
+ D3D11_UNORDERED_ACCESS_VIEW_DESC udesc = {
+ .Format = DXGI_FORMAT_R32_TYPELESS,
+ .ViewDimension = D3D11_UAV_DIMENSION_BUFFER,
+ .Buffer = {
+ .NumElements =
+ PL_ALIGN2(buf->params.size, sizeof(float)) / sizeof(float),
+ .Flags = D3D11_BUFFER_UAV_FLAG_RAW,
+ },
+ };
+ D3D(ID3D11Device_CreateUnorderedAccessView(p->dev,
+ (ID3D11Resource *) buf_p->buf, &udesc, &buf_p->raw_uav));
+ }
+
+ // Create a typed SRV for PL_BUF_TEXEL_UNIFORM and PL_BUF_TEXEL_STORAGE
+ if (params->format) {
+ if (params->uniform) {
+ D3D11_SHADER_RESOURCE_VIEW_DESC sdesc = {
+ .Format = fmt_to_dxgi(params->format),
+ .ViewDimension = D3D11_SRV_DIMENSION_BUFFER,
+ .Buffer = {
+ .NumElements =
+ PL_ALIGN(buf->params.size, buf->params.format->texel_size)
+ / buf->params.format->texel_size,
+ },
+ };
+ D3D(ID3D11Device_CreateShaderResourceView(p->dev,
+ (ID3D11Resource *) buf_p->buf, &sdesc, &buf_p->texel_srv));
+ }
+
+ // Create a typed UAV for PL_BUF_TEXEL_STORAGE
+ if (params->storable) {
+ D3D11_UNORDERED_ACCESS_VIEW_DESC udesc = {
+ .Format = fmt_to_dxgi(buf->params.format),
+ .ViewDimension = D3D11_UAV_DIMENSION_BUFFER,
+ .Buffer = {
+ .NumElements =
+ PL_ALIGN(buf->params.size, buf->params.format->texel_size)
+ / buf->params.format->texel_size,
+ },
+ };
+ D3D(ID3D11Device_CreateUnorderedAccessView(p->dev,
+ (ID3D11Resource *) buf_p->buf, &udesc, &buf_p->texel_uav));
+ }
+ }
+
+
+ if (!buf_p->data) {
+ // Create the staging buffer regardless of whether params->host_readable
+ // is set or not, so that buf_copy can copy to system-memory-backed
+ // buffers
+ // TODO: Consider sharing a big staging buffer for this, rather than
+ // having one staging buffer per buffer
+ desc.BindFlags = 0;
+ desc.MiscFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.Usage = D3D11_USAGE_STAGING;
+ D3D(ID3D11Device_CreateBuffer(p->dev, &desc, NULL, &buf_p->staging));
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After buffer create");
+
+ return buf;
+
+error:
+ pl_d3d11_buf_destroy(gpu, buf);
+ return NULL;
+}
+
+void pl_d3d11_buf_write(pl_gpu gpu, pl_buf buf, size_t offset, const void *data,
+ size_t size)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
+
+ if (buf_p->data) {
+ memcpy(buf_p->data + offset, data, size);
+ buf_p->dirty = true;
+ } else {
+ ID3D11DeviceContext_UpdateSubresource(p->imm,
+ (ID3D11Resource *) buf_p->buf, 0, (&(D3D11_BOX) {
+ .left = offset,
+ .top = 0,
+ .front = 0,
+ .right = offset + size,
+ .bottom = 1,
+ .back = 1,
+ }), data, 0, 0);
+ }
+}
+
+void pl_d3d11_buf_resolve(pl_gpu gpu, pl_buf buf)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
+
+ if (!buf_p->data || !buf_p->dirty)
+ return;
+
+ ID3D11DeviceContext_UpdateSubresource(p->imm, (ID3D11Resource *) buf_p->buf,
+ 0, NULL, buf_p->data, 0, 0);
+}
+
+bool pl_d3d11_buf_read(pl_gpu gpu, pl_buf buf, size_t offset, void *dest,
+ size_t size)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_buf_d3d11 *buf_p = PL_PRIV(buf);
+
+ // If there is a system-memory mirror of the buffer contents, use it
+ if (buf_p->data) {
+ memcpy(dest, buf_p->data + offset, size);
+ return true;
+ }
+
+ ID3D11DeviceContext_CopyResource(p->imm, (ID3D11Resource *) buf_p->staging,
+ (ID3D11Resource *) buf_p->buf);
+
+ D3D11_MAPPED_SUBRESOURCE lock;
+ D3D(ID3D11DeviceContext_Map(p->imm, (ID3D11Resource *) buf_p->staging, 0,
+ D3D11_MAP_READ, 0, &lock));
+
+ char *csrc = lock.pData;
+ memcpy(dest, csrc + offset, size);
+
+ ID3D11DeviceContext_Unmap(p->imm, (ID3D11Resource *) buf_p->staging, 0);
+
+ pl_d3d11_flush_message_queue(ctx, "After buffer read");
+
+ return true;
+
+error:
+ return false;
+}
+
+void pl_d3d11_buf_copy(pl_gpu gpu, pl_buf dst, size_t dst_offset, pl_buf src,
+ size_t src_offset, size_t size)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_buf_d3d11 *src_p = PL_PRIV(src);
+ struct pl_buf_d3d11 *dst_p = PL_PRIV(dst);
+
+ // Handle system memory copies in case one or both of the buffers has a
+ // system memory mirror
+ if (src_p->data && dst_p->data) {
+ memcpy(dst_p->data + dst_offset, src_p->data + src_offset, size);
+ dst_p->dirty = true;
+ } else if (src_p->data) {
+ pl_d3d11_buf_write(gpu, dst, dst_offset, src_p->data + src_offset, size);
+ } else if (dst_p->data) {
+ if (pl_d3d11_buf_read(gpu, src, src_offset, dst_p->data + dst_offset, size)) {
+ dst_p->dirty = true;
+ } else {
+ PL_ERR(gpu, "Failed to read from GPU during buffer copy");
+ }
+ } else {
+ ID3D11DeviceContext_CopySubresourceRegion(p->imm,
+ (ID3D11Resource *) dst_p->buf, 0, dst_offset, 0, 0,
+ (ID3D11Resource *) src_p->buf, 0, (&(D3D11_BOX) {
+ .left = src_offset,
+ .top = 0,
+ .front = 0,
+ .right = src_offset + size,
+ .bottom = 1,
+ .back = 1,
+ }));
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After buffer copy");
+}