d3d11_texture.cpp (18636B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) 3 4 #include "d3d11_texture.h" 5 #include "d3d11_device.h" 6 #include "d3d_common.h" 7 8 #include "common/assert.h" 9 #include "common/log.h" 10 #include "common/string_util.h" 11 12 #include "fmt/format.h" 13 14 #include <array> 15 16 Log_SetChannel(D3D11Device); 17 18 std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, 19 GPUTexture::Type type, GPUTexture::Format format, 20 const void* data, u32 data_stride) 21 { 22 return D3D11Texture::Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride); 23 } 24 25 bool D3D11Device::SupportsTextureFormat(GPUTexture::Format format) const 26 { 27 const DXGI_FORMAT dfmt = D3DCommon::GetFormatMapping(format).resource_format; 28 if (dfmt == DXGI_FORMAT_UNKNOWN) 29 return false; 30 31 UINT support = 0; 32 const UINT required = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_SAMPLE; 33 return (SUCCEEDED(m_device->CheckFormatSupport(dfmt, &support)) && ((support & required) == required)); 34 } 35 36 D3D11Sampler::D3D11Sampler(ComPtr<ID3D11SamplerState> ss) : m_ss(std::move(ss)) 37 { 38 } 39 40 D3D11Sampler::~D3D11Sampler() = default; 41 42 void D3D11Sampler::SetDebugName(std::string_view name) 43 { 44 SetD3DDebugObjectName(m_ss.Get(), name); 45 } 46 47 std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config& config) 48 { 49 static constexpr std::array<D3D11_TEXTURE_ADDRESS_MODE, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{ 50 D3D11_TEXTURE_ADDRESS_WRAP, // Repeat 51 D3D11_TEXTURE_ADDRESS_CLAMP, // ClampToEdge 52 D3D11_TEXTURE_ADDRESS_BORDER, // ClampToBorder 53 D3D11_TEXTURE_ADDRESS_MIRROR, // MirrorRepeat 54 }}; 55 static constexpr u8 filter_count = static_cast<u8>(GPUSampler::Filter::MaxCount); 56 static constexpr D3D11_FILTER filters[filter_count][filter_count][filter_count] = { 57 { 58 {D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT}, 59 {D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT}, 60 }, 61 { 62 {D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR}, 63 {D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, D3D11_FILTER_MIN_MAG_MIP_LINEAR}, 64 }}; 65 66 D3D11_SAMPLER_DESC desc = {}; 67 desc.AddressU = ta[static_cast<u8>(config.address_u.GetValue())]; 68 desc.AddressV = ta[static_cast<u8>(config.address_v.GetValue())]; 69 desc.AddressW = ta[static_cast<u8>(config.address_w.GetValue())]; 70 std::memcpy(desc.BorderColor, RGBA8ToFloat(config.border_color).data(), sizeof(desc.BorderColor)); 71 desc.MinLOD = static_cast<float>(config.min_lod); 72 desc.MaxLOD = static_cast<float>(config.max_lod); 73 74 if (config.anisotropy > 1) 75 { 76 desc.Filter = D3D11_FILTER_ANISOTROPIC; 77 desc.MaxAnisotropy = config.anisotropy; 78 } 79 else 80 { 81 desc.Filter = filters[static_cast<u8>(config.mip_filter.GetValue())][static_cast<u8>(config.min_filter.GetValue())] 82 [static_cast<u8>(config.mag_filter.GetValue())]; 83 desc.MaxAnisotropy = 1; 84 } 85 86 ComPtr<ID3D11SamplerState> ss; 87 const HRESULT hr = m_device->CreateSamplerState(&desc, ss.GetAddressOf()); 88 if (FAILED(hr)) [[unlikely]] 89 { 90 ERROR_LOG("CreateSamplerState() failed: {:08X}", static_cast<unsigned>(hr)); 91 return {}; 92 } 93 94 return std::unique_ptr<GPUSampler>(new D3D11Sampler(std::move(ss))); 95 } 96 97 D3D11Texture::D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, 98 ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, 99 ComPtr<ID3D11View> rtv_dsv, ComPtr<ID3D11UnorderedAccessView> uav) 100 : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels), 101 static_cast<u8>(samples), type, format), 102 m_texture(std::move(texture)), m_srv(std::move(srv)), m_rtv_dsv(std::move(rtv_dsv)), m_uav(std::move(uav)) 103 { 104 } 105 106 D3D11Texture::~D3D11Texture() 107 { 108 D3D11Device::GetInstance().UnbindTexture(this); 109 } 110 111 D3D11_TEXTURE2D_DESC D3D11Texture::GetDesc() const 112 { 113 D3D11_TEXTURE2D_DESC desc; 114 m_texture->GetDesc(&desc); 115 return desc; 116 } 117 118 void D3D11Texture::CommitClear(ID3D11DeviceContext1* context) 119 { 120 if (m_state == GPUTexture::State::Dirty) 121 return; 122 123 if (IsDepthStencil()) 124 { 125 if (m_state == GPUTexture::State::Invalidated) 126 context->DiscardView(GetD3DDSV()); 127 else 128 context->ClearDepthStencilView(GetD3DDSV(), D3D11_CLEAR_DEPTH, GetClearDepth(), 0); 129 } 130 else if (IsRenderTarget() || IsRWTexture()) 131 { 132 if (m_state == GPUTexture::State::Invalidated) 133 context->DiscardView(GetD3DRTV()); 134 else 135 context->ClearRenderTargetView(GetD3DRTV(), GetUNormClearColor().data()); 136 } 137 138 m_state = GPUTexture::State::Dirty; 139 } 140 141 bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/, 142 u32 level /*= 0*/) 143 { 144 if (m_type == Type::DynamicTexture) 145 { 146 void* map; 147 u32 map_stride; 148 if (!Map(&map, &map_stride, x, y, width, height, layer, level)) 149 return false; 150 151 StringUtil::StrideMemCpy(map, map_stride, data, pitch, GetPixelSize() * width, height); 152 Unmap(); 153 return true; 154 } 155 156 const CD3D11_BOX box(static_cast<LONG>(x), static_cast<LONG>(y), 0, static_cast<LONG>(x + width), 157 static_cast<LONG>(y + height), 1); 158 const u32 srnum = D3D11CalcSubresource(level, layer, m_levels); 159 160 ID3D11DeviceContext1* context = D3D11Device::GetD3DContext(); 161 CommitClear(context); 162 163 GPUDevice::GetStatistics().buffer_streamed += height * pitch; 164 GPUDevice::GetStatistics().num_uploads++; 165 166 context->UpdateSubresource(m_texture.Get(), srnum, &box, data, pitch, 0); 167 m_state = GPUTexture::State::Dirty; 168 return true; 169 } 170 171 bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/, 172 u32 level /*= 0*/) 173 { 174 if (m_type != Type::DynamicTexture || (x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || 175 layer > m_layers || level > m_levels) 176 { 177 return false; 178 } 179 180 const bool discard = (width == m_width && height == m_height); 181 const u32 srnum = D3D11CalcSubresource(level, layer, m_levels); 182 183 ID3D11DeviceContext1* context = D3D11Device::GetD3DContext(); 184 CommitClear(context); 185 186 D3D11_MAPPED_SUBRESOURCE sr; 187 HRESULT hr = context->Map(m_texture.Get(), srnum, discard ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_READ_WRITE, 0, &sr); 188 if (FAILED(hr)) [[unlikely]] 189 { 190 ERROR_LOG("Map pixels texture failed: {:08X}", static_cast<unsigned>(hr)); 191 return false; 192 } 193 194 GPUDevice::GetStatistics().buffer_streamed += height * sr.RowPitch; 195 GPUDevice::GetStatistics().num_uploads++; 196 197 *map = static_cast<u8*>(sr.pData) + (y * sr.RowPitch) + (x * GetPixelSize()); 198 *map_stride = sr.RowPitch; 199 m_mapped_subresource = srnum; 200 m_state = GPUTexture::State::Dirty; 201 return true; 202 } 203 204 void D3D11Texture::Unmap() 205 { 206 D3D11Device::GetD3DContext()->Unmap(m_texture.Get(), m_mapped_subresource); 207 m_mapped_subresource = 0; 208 } 209 210 void D3D11Texture::SetDebugName(std::string_view name) 211 { 212 SetD3DDebugObjectName(m_texture.Get(), name); 213 } 214 215 DXGI_FORMAT D3D11Texture::GetDXGIFormat() const 216 { 217 return D3DCommon::GetFormatMapping(m_format).resource_format; 218 } 219 220 std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, 221 u32 samples, Type type, Format format, 222 const void* initial_data /* = nullptr */, 223 u32 initial_data_stride /* = 0 */) 224 { 225 if (!ValidateConfig(width, height, layers, layers, samples, type, format)) 226 return nullptr; 227 228 u32 bind_flags = 0; 229 D3D11_USAGE usage = D3D11_USAGE_DEFAULT; 230 u32 cpu_access = 0; 231 switch (type) 232 { 233 case Type::RenderTarget: 234 bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; 235 break; 236 case Type::DepthStencil: 237 bind_flags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; 238 break; 239 case Type::Texture: 240 bind_flags = D3D11_BIND_SHADER_RESOURCE; 241 break; 242 case Type::DynamicTexture: 243 bind_flags = D3D11_BIND_SHADER_RESOURCE; 244 usage = D3D11_USAGE_DYNAMIC; 245 cpu_access = D3D11_CPU_ACCESS_WRITE; 246 break; 247 case Type::RWTexture: 248 bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; 249 break; 250 default: 251 break; 252 } 253 254 const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format); 255 256 CD3D11_TEXTURE2D_DESC desc(fm.resource_format, width, height, layers, levels, bind_flags, usage, cpu_access, samples, 257 0, 0); 258 259 D3D11_SUBRESOURCE_DATA srd; 260 srd.pSysMem = initial_data; 261 srd.SysMemPitch = initial_data_stride; 262 srd.SysMemSlicePitch = initial_data_stride * height; 263 264 ComPtr<ID3D11Texture2D> texture; 265 const HRESULT tex_hr = device->CreateTexture2D(&desc, initial_data ? &srd : nullptr, texture.GetAddressOf()); 266 if (FAILED(tex_hr)) 267 { 268 ERROR_LOG("Create texture failed: 0x{:08X} ({}x{} levels:{} samples:{} format:{} bind_flags:{:X} initial_data:{})", 269 static_cast<unsigned>(tex_hr), width, height, levels, samples, static_cast<unsigned>(format), bind_flags, 270 initial_data); 271 return nullptr; 272 } 273 274 if (initial_data) 275 { 276 GPUDevice::GetStatistics().buffer_streamed += height * initial_data_stride; 277 GPUDevice::GetStatistics().num_uploads++; 278 } 279 280 ComPtr<ID3D11ShaderResourceView> srv; 281 if (bind_flags & D3D11_BIND_SHADER_RESOURCE) 282 { 283 const D3D11_SRV_DIMENSION srv_dimension = 284 (desc.SampleDesc.Count > 1) ? 285 D3D11_SRV_DIMENSION_TEXTURE2DMS : 286 (desc.ArraySize > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DARRAY : D3D11_SRV_DIMENSION_TEXTURE2D); 287 const CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(srv_dimension, fm.srv_format, 0, desc.MipLevels, 0, desc.ArraySize); 288 const HRESULT hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf()); 289 if (FAILED(hr)) [[unlikely]] 290 { 291 ERROR_LOG("Create SRV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); 292 return nullptr; 293 } 294 } 295 296 ComPtr<ID3D11View> rtv_dsv; 297 if (bind_flags & D3D11_BIND_RENDER_TARGET) 298 { 299 const D3D11_RTV_DIMENSION rtv_dimension = 300 (desc.SampleDesc.Count > 1) ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D; 301 const CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(rtv_dimension, fm.rtv_format, 0, 0, desc.ArraySize); 302 ComPtr<ID3D11RenderTargetView> rtv; 303 const HRESULT hr = device->CreateRenderTargetView(texture.Get(), &rtv_desc, rtv.GetAddressOf()); 304 if (FAILED(hr)) [[unlikely]] 305 { 306 ERROR_LOG("Create RTV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); 307 return nullptr; 308 } 309 310 rtv_dsv = std::move(rtv); 311 } 312 else if (bind_flags & D3D11_BIND_DEPTH_STENCIL) 313 { 314 const D3D11_DSV_DIMENSION dsv_dimension = 315 (desc.SampleDesc.Count > 1) ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; 316 const CD3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc(dsv_dimension, fm.dsv_format, 0, 0, desc.ArraySize); 317 ComPtr<ID3D11DepthStencilView> dsv; 318 const HRESULT hr = device->CreateDepthStencilView(texture.Get(), &dsv_desc, dsv.GetAddressOf()); 319 if (FAILED(hr)) [[unlikely]] 320 { 321 ERROR_LOG("Create DSV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); 322 return nullptr; 323 } 324 325 rtv_dsv = std::move(dsv); 326 } 327 328 ComPtr<ID3D11UnorderedAccessView> uav; 329 if (bind_flags & D3D11_BIND_UNORDERED_ACCESS) 330 { 331 const D3D11_UAV_DIMENSION uav_dimension = 332 (desc.ArraySize > 1 ? D3D11_UAV_DIMENSION_TEXTURE2DARRAY : D3D11_UAV_DIMENSION_TEXTURE2D); 333 const CD3D11_UNORDERED_ACCESS_VIEW_DESC uav_desc(uav_dimension, fm.srv_format, 0, 0, desc.ArraySize); 334 const HRESULT hr = device->CreateUnorderedAccessView(texture.Get(), &uav_desc, uav.GetAddressOf()); 335 if (FAILED(hr)) [[unlikely]] 336 { 337 ERROR_LOG("Create UAV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); 338 return nullptr; 339 } 340 } 341 342 return std::unique_ptr<D3D11Texture>(new D3D11Texture(width, height, layers, levels, samples, type, format, 343 std::move(texture), std::move(srv), std::move(rtv_dsv), 344 std::move(uav))); 345 } 346 347 D3D11TextureBuffer::D3D11TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements) 348 { 349 } 350 351 D3D11TextureBuffer::~D3D11TextureBuffer() = default; 352 353 bool D3D11TextureBuffer::CreateBuffer() 354 { 355 const u32 size_in_bytes = GetSizeInBytes(); 356 if (!m_buffer.Create(D3D11_BIND_SHADER_RESOURCE, size_in_bytes, size_in_bytes)) 357 return false; 358 359 static constexpr std::array<DXGI_FORMAT, static_cast<u32>(Format::MaxCount)> dxgi_formats = {{ 360 DXGI_FORMAT_R16_UINT, 361 }}; 362 363 CD3D11_SHADER_RESOURCE_VIEW_DESC srv_desc(m_buffer.GetD3DBuffer(), dxgi_formats[static_cast<u32>(m_format)], 0, 364 m_size_in_elements); 365 const HRESULT hr = 366 D3D11Device::GetD3DDevice()->CreateShaderResourceView(m_buffer.GetD3DBuffer(), &srv_desc, m_srv.GetAddressOf()); 367 if (FAILED(hr)) [[unlikely]] 368 { 369 ERROR_LOG("CreateShaderResourceView() failed: {:08X}", static_cast<unsigned>(hr)); 370 return false; 371 } 372 373 return true; 374 } 375 376 void* D3D11TextureBuffer::Map(u32 required_elements) 377 { 378 const u32 esize = GetElementSize(m_format); 379 const auto res = m_buffer.Map(D3D11Device::GetD3DContext(), esize, esize * required_elements); 380 m_current_position = res.index_aligned; 381 return res.pointer; 382 } 383 384 void D3D11TextureBuffer::Unmap(u32 used_elements) 385 { 386 const u32 size = used_elements * GetElementSize(m_format); 387 GPUDevice::GetStatistics().buffer_streamed += size; 388 GPUDevice::GetStatistics().num_uploads++; 389 m_buffer.Unmap(D3D11Device::GetD3DContext(), size); 390 } 391 392 void D3D11TextureBuffer::SetDebugName(std::string_view name) 393 { 394 SetD3DDebugObjectName(m_buffer.GetD3DBuffer(), name); 395 } 396 397 std::unique_ptr<GPUTextureBuffer> D3D11Device::CreateTextureBuffer(GPUTextureBuffer::Format format, 398 u32 size_in_elements) 399 { 400 std::unique_ptr<D3D11TextureBuffer> tb = std::make_unique<D3D11TextureBuffer>(format, size_in_elements); 401 if (!tb->CreateBuffer()) 402 tb.reset(); 403 404 return tb; 405 } 406 407 D3D11DownloadTexture::D3D11DownloadTexture(Microsoft::WRL::ComPtr<ID3D11Texture2D> tex, u32 width, u32 height, 408 GPUTexture::Format format) 409 : GPUDownloadTexture(width, height, format, false), m_texture(std::move(tex)) 410 { 411 } 412 413 D3D11DownloadTexture::~D3D11DownloadTexture() 414 { 415 if (IsMapped()) 416 D3D11DownloadTexture::Unmap(); 417 } 418 419 std::unique_ptr<D3D11DownloadTexture> D3D11DownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format) 420 { 421 D3D11_TEXTURE2D_DESC desc = {}; 422 desc.Width = width; 423 desc.Height = height; 424 desc.Format = D3DCommon::GetFormatMapping(format).srv_format; 425 desc.MipLevels = 1; 426 desc.ArraySize = 1; 427 desc.SampleDesc.Count = 1; 428 desc.SampleDesc.Quality = 0; 429 desc.Usage = D3D11_USAGE_STAGING; 430 desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; 431 432 Microsoft::WRL::ComPtr<ID3D11Texture2D> tex; 433 HRESULT hr = D3D11Device::GetD3DDevice()->CreateTexture2D(&desc, nullptr, tex.GetAddressOf()); 434 if (FAILED(hr)) 435 { 436 ERROR_LOG("CreateTexture2D() failed: {:08X}", hr); 437 return {}; 438 } 439 440 return std::unique_ptr<D3D11DownloadTexture>(new D3D11DownloadTexture(std::move(tex), width, height, format)); 441 } 442 443 void D3D11DownloadTexture::CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, 444 u32 height, u32 src_layer, u32 src_level, bool use_transfer_pitch) 445 { 446 D3D11Texture* src11 = static_cast<D3D11Texture*>(src); 447 448 DebugAssert(src11->GetFormat() == m_format); 449 DebugAssert(src_level < src11->GetLevels()); 450 DebugAssert((src_x + width) <= src11->GetMipWidth(src_level) && (src_y + height) <= src11->GetMipHeight(src_level)); 451 DebugAssert((dst_x + width) <= m_width && (dst_y + height) <= m_height); 452 DebugAssert((dst_x == 0 && dst_y == 0) || !use_transfer_pitch); 453 454 ID3D11DeviceContext1* const ctx = D3D11Device::GetD3DContext(); 455 src11->CommitClear(ctx); 456 457 D3D11Device::GetStatistics().num_downloads++; 458 459 if (IsMapped()) 460 Unmap(); 461 462 // depth textures need to copy the whole thing.. 463 const u32 subresource = D3D11CalcSubresource(src_level, src_layer, src11->GetLevels()); 464 if (GPUTexture::IsDepthFormat(src11->GetFormat())) 465 { 466 ctx->CopySubresourceRegion(m_texture.Get(), 0, 0, 0, 0, src11->GetD3DTexture(), subresource, nullptr); 467 } 468 else 469 { 470 const CD3D11_BOX sbox(src_x, src_y, 0, src_x + width, src_y + height, 1); 471 ctx->CopySubresourceRegion(m_texture.Get(), 0, dst_x, dst_y, 0, src11->GetD3DTexture(), subresource, &sbox); 472 } 473 474 m_needs_flush = true; 475 } 476 477 bool D3D11DownloadTexture::Map(u32 x, u32 y, u32 width, u32 height) 478 { 479 if (IsMapped()) 480 return true; 481 482 D3D11_MAPPED_SUBRESOURCE sr; 483 HRESULT hr = D3D11Device::GetD3DContext()->Map(m_texture.Get(), 0, D3D11_MAP_READ, 0, &sr); 484 if (FAILED(hr)) 485 { 486 ERROR_LOG("Map() failed: {:08X}", hr); 487 return false; 488 } 489 490 m_map_pointer = static_cast<u8*>(sr.pData); 491 m_current_pitch = sr.RowPitch; 492 return true; 493 } 494 495 void D3D11DownloadTexture::Unmap() 496 { 497 if (!IsMapped()) 498 return; 499 500 D3D11Device::GetD3DContext()->Unmap(m_texture.Get(), 0); 501 m_map_pointer = nullptr; 502 } 503 504 void D3D11DownloadTexture::Flush() 505 { 506 if (!m_needs_flush) 507 return; 508 509 if (IsMapped()) 510 Unmap(); 511 512 // Handled when mapped. 513 } 514 515 void D3D11DownloadTexture::SetDebugName(std::string_view name) 516 { 517 if (name.empty()) 518 return; 519 520 SetD3DDebugObjectName(m_texture.Get(), name); 521 } 522 523 std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) 524 { 525 return D3D11DownloadTexture::Create(width, height, format); 526 } 527 528 std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, 529 void* memory, size_t memory_size, 530 u32 memory_stride) 531 { 532 ERROR_LOG("D3D11 cannot import memory for download textures"); 533 return {}; 534 }