summaryrefslogtreecommitdiffstats
path: root/src/d3d11/gpu_tex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/d3d11/gpu_tex.c')
-rw-r--r--src/d3d11/gpu_tex.c745
1 files changed, 745 insertions, 0 deletions
diff --git a/src/d3d11/gpu_tex.c b/src/d3d11/gpu_tex.c
new file mode 100644
index 0000000..d63fc17
--- /dev/null
+++ b/src/d3d11/gpu_tex.c
@@ -0,0 +1,745 @@
+/*
+ * 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"
+
+static inline UINT tex_subresource(pl_tex tex)
+{
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+ return tex_p->array_slice >= 0 ? tex_p->array_slice : 0;
+}
+
+static bool tex_init(pl_gpu gpu, pl_tex tex)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ // View formats may be omitted when they match the texture format, but for
+ // simplicity's sake we always set it. It will match the texture format for
+ // textures created with tex_create, but it can be different for video
+ // textures wrapped with pl_d3d11_wrap.
+ DXGI_FORMAT fmt = fmt_to_dxgi(tex->params.format);
+
+ if (tex->params.sampleable || tex->params.storable) {
+ D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc = {
+ .Format = fmt,
+ };
+ switch (pl_tex_params_dimension(tex->params)) {
+ case 1:
+ if (tex_p->array_slice >= 0) {
+ srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1DARRAY;
+ srvdesc.Texture1DArray.MipLevels = 1;
+ srvdesc.Texture1DArray.FirstArraySlice = tex_p->array_slice;
+ srvdesc.Texture1DArray.ArraySize = 1;
+ } else {
+ srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
+ srvdesc.Texture1D.MipLevels = 1;
+ }
+ break;
+ case 2:
+ if (tex_p->array_slice >= 0) {
+ srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
+ srvdesc.Texture2DArray.MipLevels = 1;
+ srvdesc.Texture2DArray.FirstArraySlice = tex_p->array_slice;
+ srvdesc.Texture2DArray.ArraySize = 1;
+ } else {
+ srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+ srvdesc.Texture2D.MipLevels = 1;
+ }
+ break;
+ case 3:
+ // D3D11 does not have Texture3D arrays
+ srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+ srvdesc.Texture3D.MipLevels = 1;
+ break;
+ }
+ D3D(ID3D11Device_CreateShaderResourceView(p->dev, tex_p->res, &srvdesc,
+ &tex_p->srv));
+ }
+
+ if (tex->params.renderable) {
+ D3D11_RENDER_TARGET_VIEW_DESC rtvdesc = {
+ .Format = fmt,
+ };
+ switch (pl_tex_params_dimension(tex->params)) {
+ case 1:
+ if (tex_p->array_slice >= 0) {
+ rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1DARRAY;
+ rtvdesc.Texture1DArray.FirstArraySlice = tex_p->array_slice;
+ rtvdesc.Texture1DArray.ArraySize = 1;
+ } else {
+ rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
+ }
+ break;
+ case 2:
+ if (tex_p->array_slice >= 0) {
+ rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+ rtvdesc.Texture2DArray.FirstArraySlice = tex_p->array_slice;
+ rtvdesc.Texture2DArray.ArraySize = 1;
+ } else {
+ rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+ }
+ break;
+ case 3:
+ // D3D11 does not have Texture3D arrays
+ rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
+ rtvdesc.Texture3D.WSize = -1;
+ break;
+ }
+ D3D(ID3D11Device_CreateRenderTargetView(p->dev, tex_p->res, &rtvdesc,
+ &tex_p->rtv));
+ }
+
+ if (p->fl >= D3D_FEATURE_LEVEL_11_0 && tex->params.storable) {
+ D3D11_UNORDERED_ACCESS_VIEW_DESC uavdesc = {
+ .Format = fmt,
+ };
+ switch (pl_tex_params_dimension(tex->params)) {
+ case 1:
+ if (tex_p->array_slice >= 0) {
+ uavdesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1DARRAY;
+ uavdesc.Texture1DArray.FirstArraySlice = tex_p->array_slice;
+ uavdesc.Texture1DArray.ArraySize = 1;
+ } else {
+ uavdesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE1D;
+ }
+ break;
+ case 2:
+ if (tex_p->array_slice >= 0) {
+ uavdesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
+ uavdesc.Texture2DArray.FirstArraySlice = tex_p->array_slice;
+ uavdesc.Texture2DArray.ArraySize = 1;
+ } else {
+ uavdesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
+ }
+ break;
+ case 3:
+ // D3D11 does not have Texture3D arrays
+ uavdesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D;
+ uavdesc.Texture3D.WSize = -1;
+ break;
+ }
+ D3D(ID3D11Device_CreateUnorderedAccessView(p->dev, tex_p->res, &uavdesc,
+ &tex_p->uav));
+ }
+
+ return true;
+error:
+ return false;
+}
+
+void pl_d3d11_tex_destroy(pl_gpu gpu, pl_tex tex)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ SAFE_RELEASE(tex_p->srv);
+ SAFE_RELEASE(tex_p->rtv);
+ SAFE_RELEASE(tex_p->uav);
+ SAFE_RELEASE(tex_p->res);
+ SAFE_RELEASE(tex_p->staging);
+
+ pl_d3d11_flush_message_queue(ctx, "After texture destroy");
+
+ pl_free((void *) tex);
+}
+
+pl_tex pl_d3d11_tex_create(pl_gpu gpu, const struct pl_tex_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_d3d11);
+ tex->params = *params;
+ tex->params.initial_data = NULL;
+ tex->sampler_type = PL_SAMPLER_NORMAL;
+
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ DXGI_FORMAT dxfmt = fmt_to_dxgi(params->format);
+
+ D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
+ D3D11_BIND_FLAG bind_flags = 0;
+
+ if (params->format->emulated) {
+ tex_p->texel_fmt = pl_find_fmt(gpu, params->format->type, 1, 0,
+ params->format->host_bits[0],
+ PL_FMT_CAP_TEXEL_UNIFORM);
+
+ if (!tex_p->texel_fmt) {
+ PL_ERR(gpu, "Failed picking texel format for emulated texture!");
+ goto error;
+ }
+
+ tex->params.storable = true;
+ }
+
+ if (p->fl >= D3D_FEATURE_LEVEL_11_0) {
+ // On >=FL11_0, blit emulation needs image storage
+ tex->params.storable |= params->blit_src || params->blit_dst;
+
+ // Blit emulation can use a sampler for linear filtering during stretch
+ if ((tex->params.format->caps & PL_FMT_CAP_LINEAR) && params->blit_src)
+ tex->params.sampleable = true;
+ } else {
+ // On <FL11_0, blit emulation uses a render pass
+ tex->params.sampleable |= params->blit_src;
+ tex->params.renderable |= params->blit_dst;
+ }
+
+ if (tex->params.sampleable)
+ bind_flags |= D3D11_BIND_SHADER_RESOURCE;
+ if (tex->params.renderable)
+ bind_flags |= D3D11_BIND_RENDER_TARGET;
+ if (p->fl >= D3D_FEATURE_LEVEL_11_0 && tex->params.storable)
+ bind_flags |= D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
+
+ // Apparently IMMUTABLE textures are efficient, so try to infer whether we
+ // can use one
+ if (params->initial_data && !params->format->emulated &&
+ !tex->params.renderable && !tex->params.storable && !params->host_writable)
+ {
+ usage = D3D11_USAGE_IMMUTABLE;
+ }
+
+ // In FL9_x, resources with only D3D11_BIND_SHADER_RESOURCE can't be copied
+ // from GPU-accessible memory to CPU-accessible memory. The only other bind
+ // flag we set on this FL is D3D11_BIND_RENDER_TARGET, so set it.
+ if (p->fl <= D3D_FEATURE_LEVEL_9_3 && tex->params.host_readable)
+ bind_flags |= D3D11_BIND_RENDER_TARGET;
+
+ // In FL9_x, when using DEFAULT or IMMUTABLE, BindFlags cannot be zero
+ if (p->fl <= D3D_FEATURE_LEVEL_9_3 && !bind_flags)
+ bind_flags |= D3D11_BIND_SHADER_RESOURCE;
+
+ D3D11_SUBRESOURCE_DATA data;
+ D3D11_SUBRESOURCE_DATA *pdata = NULL;
+ if (params->initial_data && !params->format->emulated) {
+ data = (D3D11_SUBRESOURCE_DATA) {
+ .pSysMem = params->initial_data,
+ .SysMemPitch = params->w * params->format->texel_size,
+ };
+ if (params->d)
+ data.SysMemSlicePitch = data.SysMemPitch * params->h;
+ pdata = &data;
+ }
+
+ switch (pl_tex_params_dimension(*params)) {
+ case 1:;
+ D3D11_TEXTURE1D_DESC desc1d = {
+ .Width = params->w,
+ .MipLevels = 1,
+ .ArraySize = 1,
+ .Format = dxfmt,
+ .Usage = usage,
+ .BindFlags = bind_flags,
+ };
+ D3D(ID3D11Device_CreateTexture1D(p->dev, &desc1d, pdata, &tex_p->tex1d));
+ tex_p->res = (ID3D11Resource *)tex_p->tex1d;
+
+ // Create a staging texture with CPU access for pl_tex_download()
+ if (params->host_readable) {
+ desc1d.BindFlags = 0;
+ desc1d.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc1d.Usage = D3D11_USAGE_STAGING;
+
+ D3D(ID3D11Device_CreateTexture1D(p->dev, &desc1d, NULL,
+ &tex_p->staging1d));
+ tex_p->staging = (ID3D11Resource *) tex_p->staging1d;
+ }
+ break;
+ case 2:;
+ D3D11_TEXTURE2D_DESC desc2d = {
+ .Width = params->w,
+ .Height = params->h,
+ .MipLevels = 1,
+ .ArraySize = 1,
+ .SampleDesc.Count = 1,
+ .Format = dxfmt,
+ .Usage = usage,
+ .BindFlags = bind_flags,
+ };
+ D3D(ID3D11Device_CreateTexture2D(p->dev, &desc2d, pdata, &tex_p->tex2d));
+ tex_p->res = (ID3D11Resource *)tex_p->tex2d;
+
+ // Create a staging texture with CPU access for pl_tex_download()
+ if (params->host_readable) {
+ desc2d.BindFlags = 0;
+ desc2d.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc2d.Usage = D3D11_USAGE_STAGING;
+
+ D3D(ID3D11Device_CreateTexture2D(p->dev, &desc2d, NULL,
+ &tex_p->staging2d));
+ tex_p->staging = (ID3D11Resource *) tex_p->staging2d;
+ }
+ break;
+ case 3:;
+ D3D11_TEXTURE3D_DESC desc3d = {
+ .Width = params->w,
+ .Height = params->h,
+ .Depth = params->d,
+ .MipLevels = 1,
+ .Format = dxfmt,
+ .Usage = usage,
+ .BindFlags = bind_flags,
+ };
+ D3D(ID3D11Device_CreateTexture3D(p->dev, &desc3d, pdata, &tex_p->tex3d));
+ tex_p->res = (ID3D11Resource *)tex_p->tex3d;
+
+ // Create a staging texture with CPU access for pl_tex_download()
+ if (params->host_readable) {
+ desc3d.BindFlags = 0;
+ desc3d.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc3d.Usage = D3D11_USAGE_STAGING;
+
+ D3D(ID3D11Device_CreateTexture3D(p->dev, &desc3d, NULL,
+ &tex_p->staging3d));
+ tex_p->staging = (ID3D11Resource *) tex_p->staging3d;
+ }
+ break;
+ default:
+ pl_unreachable();
+ }
+
+ tex_p->array_slice = -1;
+
+ if (!tex_init(gpu, tex))
+ goto error;
+
+ if (params->initial_data && params->format->emulated) {
+ struct pl_tex_transfer_params ul_params = {
+ .tex = tex,
+ .ptr = (void *) params->initial_data,
+ .rc = { 0, 0, 0, params->w, params->h, params->d },
+ };
+
+ // Since we re-use GPU helpers which require writable images, just fake it
+ bool writable = tex->params.host_writable;
+ tex->params.host_writable = true;
+ if (!pl_tex_upload(gpu, &ul_params))
+ goto error;
+ tex->params.host_writable = writable;
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After texture create");
+
+ return tex;
+
+error:
+ pl_d3d11_tex_destroy(gpu, tex);
+ return NULL;
+}
+
+pl_tex pl_d3d11_wrap(pl_gpu gpu, const struct pl_d3d11_wrap_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+
+ struct pl_tex_t *tex = pl_zalloc_obj(NULL, tex, struct pl_tex_d3d11);
+ tex->sampler_type = PL_SAMPLER_NORMAL;
+
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ DXGI_FORMAT fmt = DXGI_FORMAT_UNKNOWN;
+ D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
+ D3D11_BIND_FLAG bind_flags = 0;
+ UINT mip_levels = 1;
+ UINT array_size = 1;
+ UINT sample_count = 1;
+
+ D3D11_RESOURCE_DIMENSION type;
+ ID3D11Resource_GetType(params->tex, &type);
+
+ switch (type) {
+ case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
+ D3D(ID3D11Resource_QueryInterface(params->tex, &IID_ID3D11Texture1D,
+ (void **) &tex_p->tex1d));
+ tex_p->res = (ID3D11Resource *) tex_p->tex1d;
+
+ D3D11_TEXTURE1D_DESC desc1d;
+ ID3D11Texture1D_GetDesc(tex_p->tex1d, &desc1d);
+
+ tex->params.w = desc1d.Width;
+ mip_levels = desc1d.MipLevels;
+ array_size = desc1d.ArraySize;
+ fmt = desc1d.Format;
+ usage = desc1d.Usage;
+ bind_flags = desc1d.BindFlags;
+ break;
+
+ case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
+ D3D(ID3D11Resource_QueryInterface(params->tex, &IID_ID3D11Texture2D,
+ (void **) &tex_p->tex2d));
+ tex_p->res = (ID3D11Resource *) tex_p->tex2d;
+
+ D3D11_TEXTURE2D_DESC desc2d;
+ ID3D11Texture2D_GetDesc(tex_p->tex2d, &desc2d);
+
+ tex->params.w = desc2d.Width;
+ tex->params.h = desc2d.Height;
+ mip_levels = desc2d.MipLevels;
+ array_size = desc2d.ArraySize;
+ fmt = desc2d.Format;
+ sample_count = desc2d.SampleDesc.Count;
+ usage = desc2d.Usage;
+ bind_flags = desc2d.BindFlags;
+
+ // Allow the format and size of 2D textures to be overridden to support
+ // shader views of video resources
+ if (params->fmt) {
+ fmt = params->fmt;
+ tex->params.w = params->w;
+ tex->params.h = params->h;
+ }
+
+ break;
+
+ case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
+ D3D(ID3D11Resource_QueryInterface(params->tex, &IID_ID3D11Texture3D,
+ (void **) &tex_p->tex3d));
+ tex_p->res = (ID3D11Resource *) tex_p->tex3d;
+
+ D3D11_TEXTURE3D_DESC desc3d;
+ ID3D11Texture3D_GetDesc(tex_p->tex3d, &desc3d);
+
+ tex->params.w = desc3d.Width;
+ tex->params.h = desc3d.Height;
+ tex->params.d = desc3d.Depth;
+ mip_levels = desc3d.MipLevels;
+ fmt = desc3d.Format;
+ usage = desc3d.Usage;
+ bind_flags = desc3d.BindFlags;
+ break;
+
+ case D3D11_RESOURCE_DIMENSION_UNKNOWN:
+ case D3D11_RESOURCE_DIMENSION_BUFFER:
+ PL_ERR(gpu, "Resource is not suitable to wrap");
+ goto error;
+ }
+
+ if (mip_levels != 1) {
+ PL_ERR(gpu, "Mipmapped textures not supported for wrapping");
+ goto error;
+ }
+ if (sample_count != 1) {
+ PL_ERR(gpu, "Multisampled textures not supported for wrapping");
+ goto error;
+ }
+ if (usage != D3D11_USAGE_DEFAULT) {
+ PL_ERR(gpu, "Resource is not D3D11_USAGE_DEFAULT");
+ goto error;
+ }
+
+ if (array_size > 1) {
+ if (params->array_slice < 0 || params->array_slice >= array_size) {
+ PL_ERR(gpu, "array_slice out of range");
+ goto error;
+ }
+ tex_p->array_slice = params->array_slice;
+ } else {
+ tex_p->array_slice = -1;
+ }
+
+ if (bind_flags & D3D11_BIND_SHADER_RESOURCE) {
+ tex->params.sampleable = true;
+
+ // Blit emulation uses a render pass on <FL11_0
+ if (p->fl < D3D_FEATURE_LEVEL_11_0)
+ tex->params.blit_src = true;
+ }
+ if (bind_flags & D3D11_BIND_RENDER_TARGET) {
+ tex->params.renderable = true;
+
+ // Blit emulation uses a render pass on <FL11_0
+ if (p->fl < D3D_FEATURE_LEVEL_11_0)
+ tex->params.blit_dst = true;
+ }
+ static const D3D11_BIND_FLAG storable_flags =
+ D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
+ if ((bind_flags & storable_flags) == storable_flags) {
+ tex->params.storable = true;
+
+ // Blit emulation uses image storage on >=FL11_0. A feature level check
+ // isn't required because <FL11_0 doesn't have storable images.
+ tex->params.blit_src = tex->params.blit_dst = true;
+ }
+
+ for (int i = 0; i < gpu->num_formats; i++) {
+ DXGI_FORMAT target_fmt = fmt_to_dxgi(gpu->formats[i]);
+ if (fmt == target_fmt) {
+ tex->params.format = gpu->formats[i];
+ break;
+ }
+ }
+ if (!tex->params.format) {
+ PL_ERR(gpu, "Could not find a suitable pl_fmt for wrapped resource");
+ goto error;
+ }
+
+ if (!tex_init(gpu, tex))
+ goto error;
+
+ pl_d3d11_flush_message_queue(ctx, "After texture wrap");
+
+ return tex;
+
+error:
+ pl_d3d11_tex_destroy(gpu, tex);
+ return NULL;
+}
+
+void pl_d3d11_tex_invalidate(pl_gpu gpu, pl_tex tex)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ // Resource discarding requires D3D11.1
+ if (!p->imm1)
+ return;
+
+ // Prefer discarding a view to discarding the whole resource. The reason
+ // for this is that a pl_tex can refer to a single member of a texture
+ // array. Discarding the SRV, RTV or UAV should only discard that member.
+ if (tex_p->rtv) {
+ ID3D11DeviceContext1_DiscardView(p->imm1, (ID3D11View *) tex_p->rtv);
+ } else if (tex_p->uav) {
+ ID3D11DeviceContext1_DiscardView(p->imm1, (ID3D11View *) tex_p->uav);
+ } else if (tex_p->srv) {
+ ID3D11DeviceContext1_DiscardView(p->imm1, (ID3D11View *) tex_p->srv);
+ } else if (tex_p->array_slice < 0) {
+ // If there are no views, only discard if the ID3D11Resource is not a
+ // texture array
+ ID3D11DeviceContext1_DiscardResource(p->imm1, tex_p->res);
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After texture invalidate");
+}
+
+void pl_d3d11_tex_clear_ex(pl_gpu gpu, pl_tex tex,
+ const union pl_clear_color color)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+
+ if (tex->params.format->type == PL_FMT_UINT) {
+ if (tex_p->uav) {
+ ID3D11DeviceContext_ClearUnorderedAccessViewUint(p->imm, tex_p->uav,
+ color.u);
+ } else {
+ float c[4] = { color.u[0], color.u[1], color.u[2], color.u[3] };
+ ID3D11DeviceContext_ClearRenderTargetView(p->imm, tex_p->rtv, c);
+ }
+
+ } else if (tex->params.format->type == PL_FMT_SINT) {
+ if (tex_p->uav) {
+ ID3D11DeviceContext_ClearUnorderedAccessViewUint(p->imm, tex_p->uav,
+ (const uint32_t *)color.i);
+ } else {
+ float c[4] = { color.i[0], color.i[1], color.i[2], color.i[3] };
+ ID3D11DeviceContext_ClearRenderTargetView(p->imm, tex_p->rtv, c);
+ }
+
+ } else if (tex_p->rtv) {
+ ID3D11DeviceContext_ClearRenderTargetView(p->imm, tex_p->rtv, color.f);
+ } else {
+ ID3D11DeviceContext_ClearUnorderedAccessViewFloat(p->imm, tex_p->uav, color.f);
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After texture clear");
+}
+
+#define pl_rect3d_to_box(rc) \
+ ((D3D11_BOX) { \
+ .left = rc.x0, .top = rc.y0, .front = rc.z0, \
+ .right = rc.x1, .bottom = rc.y1, .back = rc.z1, \
+ })
+
+void pl_d3d11_tex_blit(pl_gpu gpu, const struct pl_tex_blit_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ struct pl_tex_d3d11 *src_p = PL_PRIV(params->src);
+ DXGI_FORMAT src_fmt = fmt_to_dxgi(params->src->params.format);
+ struct pl_tex_d3d11 *dst_p = PL_PRIV(params->dst);
+ DXGI_FORMAT dst_fmt = fmt_to_dxgi(params->dst->params.format);
+
+ // If the blit operation doesn't require flipping, scaling or format
+ // conversion, we can use CopySubresourceRegion
+ pl_rect3d src_rc = params->src_rc, dst_rc = params->dst_rc;
+ if (pl_rect3d_eq(src_rc, dst_rc) && src_fmt == dst_fmt) {
+ pl_rect3d rc = params->src_rc;
+ pl_rect3d_normalize(&rc);
+
+ ID3D11DeviceContext_CopySubresourceRegion(p->imm, dst_p->res,
+ tex_subresource(params->dst), rc.x0, rc.y0, rc.z0, src_p->res,
+ tex_subresource(params->src), &pl_rect3d_to_box(rc));
+ } else if (p->fl >= D3D_FEATURE_LEVEL_11_0) {
+ if (!pl_tex_blit_compute(gpu, params))
+ PL_ERR(gpu, "Failed compute shader fallback blit");
+ } else {
+ pl_tex_blit_raster(gpu, params);
+ }
+
+ pl_d3d11_flush_message_queue(ctx, "After texture blit");
+}
+
+bool pl_d3d11_tex_upload(pl_gpu gpu, const struct pl_tex_transfer_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ pl_tex tex = params->tex;
+ pl_fmt fmt = tex->params.format;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+ struct pl_tex_transfer_params *slices = NULL;
+ bool ret = false;
+
+ pl_d3d11_timer_start(gpu, params->timer);
+
+ if (fmt->emulated) {
+
+ int num_slices = pl_tex_transfer_slices(gpu, tex_p->texel_fmt, params, &slices);
+ for (int i = 0; i < num_slices; i++) {
+ // Copy the source data buffer into an intermediate buffer
+ pl_buf tbuf = pl_buf_create(gpu, pl_buf_params(
+ .memory_type = PL_BUF_MEM_DEVICE,
+ .format = tex_p->texel_fmt,
+ .size = pl_tex_transfer_size(&slices[i]),
+ .initial_data = slices[i].ptr,
+ .storable = true,
+ ));
+
+ if (!tbuf) {
+ PL_ERR(gpu, "Failed creating buffer for tex upload fallback!");
+ goto error;
+ }
+
+ slices[i].ptr = NULL;
+ slices[i].buf = tbuf;
+ slices[i].buf_offset = 0;
+ bool ok = pl_tex_upload_texel(gpu, &slices[i]);
+ pl_buf_destroy(gpu, &tbuf);
+ if (!ok)
+ goto error;
+ }
+
+ } else {
+
+ ID3D11DeviceContext_UpdateSubresource(p->imm, tex_p->res,
+ tex_subresource(tex), &pl_rect3d_to_box(params->rc), params->ptr,
+ params->row_pitch, params->depth_pitch);
+
+ }
+
+ ret = true;
+
+error:
+ pl_d3d11_timer_end(gpu, params->timer);
+ pl_d3d11_flush_message_queue(ctx, "After texture upload");
+
+ pl_free(slices);
+ return ret;
+}
+
+bool pl_d3d11_tex_download(pl_gpu gpu, const struct pl_tex_transfer_params *params)
+{
+ struct pl_gpu_d3d11 *p = PL_PRIV(gpu);
+ struct d3d11_ctx *ctx = p->ctx;
+ const struct pl_tex_t *tex = params->tex;
+ pl_fmt fmt = tex->params.format;
+ struct pl_tex_d3d11 *tex_p = PL_PRIV(tex);
+ struct pl_tex_transfer_params *slices = NULL;
+ bool ret = false;
+
+ if (!tex_p->staging)
+ return false;
+
+ pl_d3d11_timer_start(gpu, params->timer);
+
+ if (fmt->emulated) {
+
+ pl_buf tbuf = NULL;
+ int num_slices = pl_tex_transfer_slices(gpu, tex_p->texel_fmt, params, &slices);
+ for (int i = 0; i < num_slices; i++) {
+ const size_t slice_size = pl_tex_transfer_size(&slices[i]);
+ bool ok = pl_buf_recreate(gpu, &tbuf, pl_buf_params(
+ .storable = true,
+ .size = slice_size,
+ .memory_type = PL_BUF_MEM_DEVICE,
+ .format = tex_p->texel_fmt,
+ .host_readable = true,
+ ));
+
+ if (!ok) {
+ PL_ERR(gpu, "Failed creating buffer for tex download fallback!");
+ goto error;
+ }
+
+ void *ptr = slices[i].ptr;
+ slices[i].ptr = NULL;
+ slices[i].buf = tbuf;
+ slices[i].buf_offset = 0;
+
+ // Download into an intermediate buffer first
+ ok = pl_tex_download_texel(gpu, &slices[i]);
+ ok = ok && pl_buf_read(gpu, tbuf, 0, ptr, slice_size);
+ if (!ok) {
+ pl_buf_destroy(gpu, &tbuf);
+ goto error;
+ }
+ }
+ pl_buf_destroy(gpu, &tbuf);
+
+ } else {
+
+ ID3D11DeviceContext_CopySubresourceRegion(p->imm,
+ (ID3D11Resource *) tex_p->staging, 0, params->rc.x0, params->rc.y0,
+ params->rc.z0, tex_p->res, tex_subresource(tex),
+ &pl_rect3d_to_box(params->rc));
+
+ D3D11_MAPPED_SUBRESOURCE lock;
+ D3D(ID3D11DeviceContext_Map(p->imm, (ID3D11Resource *) tex_p->staging, 0,
+ D3D11_MAP_READ, 0, &lock));
+
+ char *cdst = params->ptr;
+ char *csrc = lock.pData;
+ size_t line_size = pl_rect_w(params->rc) * tex->params.format->texel_size;
+ for (int z = 0; z < pl_rect_d(params->rc); z++) {
+ for (int y = 0; y < pl_rect_h(params->rc); y++) {
+ memcpy(cdst + z * params->depth_pitch + y * params->row_pitch,
+ csrc + (params->rc.z0 + z) * lock.DepthPitch +
+ (params->rc.y0 + y) * lock.RowPitch + params->rc.x0,
+ line_size);
+ }
+ }
+
+ ID3D11DeviceContext_Unmap(p->imm, (ID3D11Resource*)tex_p->staging, 0);
+ }
+
+ ret = true;
+
+error:
+ pl_d3d11_timer_end(gpu, params->timer);
+ pl_d3d11_flush_message_queue(ctx, "After texture download");
+
+ pl_free(slices);
+ return ret;
+}