//------------------------------------------------------------------------------------- // DirectXTexD3D11.cpp // // DirectX Texture Library - Direct3D 11 helpers // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // http://go.microsoft.com/fwlink/?LinkId=248926 //------------------------------------------------------------------------------------- #include "directxtexp.h" #if !defined(_XBOX_ONE) || !defined(_TITLE) #include #endif #ifndef IID_GRAPHICS_PPV_ARGS #define IID_GRAPHICS_PPV_ARGS(x) IID_PPV_ARGS(x) #endif using namespace DirectX; using Microsoft::WRL::ComPtr; static_assert(TEX_DIMENSION_TEXTURE1D == D3D11_RESOURCE_DIMENSION_TEXTURE1D, "header enum mismatch"); static_assert(TEX_DIMENSION_TEXTURE2D == D3D11_RESOURCE_DIMENSION_TEXTURE2D, "header enum mismatch"); static_assert(TEX_DIMENSION_TEXTURE3D == D3D11_RESOURCE_DIMENSION_TEXTURE3D, "header enum mismatch"); namespace { HRESULT Capture( _In_ ID3D11DeviceContext* pContext, _In_ ID3D11Resource* pSource, const TexMetadata& metadata, const ScratchImage& result) { if (!pContext || !pSource || !result.GetPixels()) return E_POINTER; #if defined(_XBOX_ONE) && defined(_TITLE) ComPtr d3dDevice; pContext->GetDevice(d3dDevice.GetAddressOf()); if (d3dDevice->GetCreationFlags() & D3D11_CREATE_DEVICE_IMMEDIATE_CONTEXT_FAST_SEMANTICS) { ComPtr d3dDeviceX; HRESULT hr = d3dDevice.As(&d3dDeviceX); if (FAILED(hr)) return hr; ComPtr d3dContextX; hr = pContext->QueryInterface(IID_GRAPHICS_PPV_ARGS(d3dContextX.GetAddressOf())); if (FAILED(hr)) return hr; UINT64 copyFence = d3dContextX->InsertFence(0); while (d3dDeviceX->IsFencePending(copyFence)) { SwitchToThread(); } } #endif if (metadata.IsVolumemap()) { //--- Volume texture ---------------------------------------------------------- assert(metadata.arraySize == 1); size_t height = metadata.height; size_t depth = metadata.depth; for (size_t level = 0; level < metadata.mipLevels; ++level) { UINT dindex = D3D11CalcSubresource(static_cast(level), 0, static_cast(metadata.mipLevels)); D3D11_MAPPED_SUBRESOURCE mapped; HRESULT hr = pContext->Map(pSource, dindex, D3D11_MAP_READ, 0, &mapped); if (FAILED(hr)) return hr; auto pslice = reinterpret_cast(mapped.pData); if (!pslice) { pContext->Unmap(pSource, dindex); return E_POINTER; } size_t lines = ComputeScanlines(metadata.format, height); if (!lines) { pContext->Unmap(pSource, dindex); return E_UNEXPECTED; } for (size_t slice = 0; slice < depth; ++slice) { const Image* img = result.GetImage(level, 0, slice); if (!img) { pContext->Unmap(pSource, dindex); return E_FAIL; } if (!img->pixels) { pContext->Unmap(pSource, dindex); return E_POINTER; } const uint8_t* sptr = pslice; uint8_t* dptr = img->pixels; for (size_t h = 0; h < lines; ++h) { size_t msize = std::min(img->rowPitch, mapped.RowPitch); memcpy_s(dptr, img->rowPitch, sptr, msize); sptr += mapped.RowPitch; dptr += img->rowPitch; } pslice += mapped.DepthPitch; } pContext->Unmap(pSource, dindex); if (height > 1) height >>= 1; if (depth > 1) depth >>= 1; } } else { //--- 1D or 2D texture -------------------------------------------------------- assert(metadata.depth == 1); for (size_t item = 0; item < metadata.arraySize; ++item) { size_t height = metadata.height; for (size_t level = 0; level < metadata.mipLevels; ++level) { UINT dindex = D3D11CalcSubresource(static_cast(level), static_cast(item), static_cast(metadata.mipLevels)); D3D11_MAPPED_SUBRESOURCE mapped; HRESULT hr = pContext->Map(pSource, dindex, D3D11_MAP_READ, 0, &mapped); if (FAILED(hr)) return hr; const Image* img = result.GetImage(level, item, 0); if (!img) { pContext->Unmap(pSource, dindex); return E_FAIL; } if (!img->pixels) { pContext->Unmap(pSource, dindex); return E_POINTER; } size_t lines = ComputeScanlines(metadata.format, height); if (!lines) { pContext->Unmap(pSource, dindex); return E_UNEXPECTED; } auto sptr = reinterpret_cast(mapped.pData); uint8_t* dptr = img->pixels; for (size_t h = 0; h < lines; ++h) { size_t msize = std::min(img->rowPitch, mapped.RowPitch); memcpy_s(dptr, img->rowPitch, sptr, msize); sptr += mapped.RowPitch; dptr += img->rowPitch; } pContext->Unmap(pSource, dindex); if (height > 1) height >>= 1; } } } return S_OK; } } //===================================================================================== // Entry-points //===================================================================================== //------------------------------------------------------------------------------------- // Determine if given texture metadata is supported on the given device //------------------------------------------------------------------------------------- _Use_decl_annotations_ bool DirectX::IsSupportedTexture( ID3D11Device* pDevice, const TexMetadata& metadata) { if (!pDevice) return false; D3D_FEATURE_LEVEL fl = pDevice->GetFeatureLevel(); // Validate format DXGI_FORMAT fmt = metadata.format; if (!IsValid(fmt)) return false; switch (fmt) { case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_UNORM: case DXGI_FORMAT_BC4_SNORM: case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_UNORM: case DXGI_FORMAT_BC5_SNORM: if (fl < D3D_FEATURE_LEVEL_10_0) return false; break; case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC6H_SF16: case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: if (fl < D3D_FEATURE_LEVEL_11_0) return false; break; } // Validate miplevel count if (metadata.mipLevels > D3D11_REQ_MIP_LEVELS) return false; // Validate array size, dimension, and width/height size_t arraySize = metadata.arraySize; size_t iWidth = metadata.width; size_t iHeight = metadata.height; size_t iDepth = metadata.depth; // Most cases are known apriori based on feature level, but we use this for robustness to handle the few optional cases UINT formatSupport = 0; HRESULT hr = pDevice->CheckFormatSupport(fmt, &formatSupport); if (FAILED(hr)) { formatSupport = 0; } if (metadata.mipLevels > 1 && !(formatSupport & D3D11_FORMAT_SUPPORT_MIP)) { return false; } switch (metadata.dimension) { case TEX_DIMENSION_TEXTURE1D: if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE1D)) return false; if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D11_REQ_TEXTURE1D_U_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_11_0) { if ((arraySize > D3D10_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D10_REQ_TEXTURE1D_U_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_10_0) { if ((arraySize > 1) || (iWidth > D3D_FL9_3_REQ_TEXTURE1D_U_DIMENSION)) return false; if ((fl < D3D_FEATURE_LEVEL_9_3) && (iWidth > D3D_FL9_1_REQ_TEXTURE1D_U_DIMENSION)) return false; } } break; case TEX_DIMENSION_TEXTURE2D: if (metadata.IsCubemap()) { if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE)) return false; if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D11_REQ_TEXTURECUBE_DIMENSION) || (iHeight > D3D11_REQ_TEXTURECUBE_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_11_0) { if ((arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D10_REQ_TEXTURECUBE_DIMENSION) || (iHeight > D3D10_REQ_TEXTURECUBE_DIMENSION)) return false; if ((fl < D3D_FEATURE_LEVEL_10_1) && (arraySize != 6)) return false; if (fl < D3D_FEATURE_LEVEL_10_0) { if ((iWidth > D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION) || (iHeight > D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION)) return false; if ((fl < D3D_FEATURE_LEVEL_9_3) && ((iWidth > D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION) || (iHeight > D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION))) return false; } } } else // Not a cube map { if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)) return false; if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION) || (iHeight > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_11_0) { if ((arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION) || (iWidth > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION) || (iHeight > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_10_0) { if ((arraySize > 1) || (iWidth > D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION) || (iHeight > D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION)) return false; if ((fl < D3D_FEATURE_LEVEL_9_3) && ((iWidth > D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION) || (iHeight > D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION))) return false; } } } break; case TEX_DIMENSION_TEXTURE3D: if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D)) return false; if ((arraySize > 1) || (iWidth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iHeight > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iDepth > D3D11_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_11_0) { if ((iWidth > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iHeight > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iDepth > D3D10_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) return false; if (fl < D3D_FEATURE_LEVEL_10_0) { if ((iWidth > D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iHeight > D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION) || (iDepth > D3D_FL9_1_REQ_TEXTURE3D_U_V_OR_W_DIMENSION)) return false; } } break; default: // Not a supported dimension return false; } return true; } //------------------------------------------------------------------------------------- // Create a texture resource //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateTexture( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, ID3D11Resource** ppResource) { return CreateTextureEx( pDevice, srcImages, nimages, metadata, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, ppResource); } _Use_decl_annotations_ HRESULT DirectX::CreateTextureEx( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11Resource** ppResource) { if (!pDevice || !srcImages || !nimages || !ppResource) return E_INVALIDARG; *ppResource = nullptr; if (!metadata.mipLevels || !metadata.arraySize) return E_INVALIDARG; if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX) || (metadata.mipLevels > UINT16_MAX) || (metadata.arraySize > UINT16_MAX)) return E_INVALIDARG; std::unique_ptr initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[metadata.mipLevels * metadata.arraySize]); if (!initData) return E_OUTOFMEMORY; // Fill out subresource array if (metadata.IsVolumemap()) { //--- Volume case ------------------------------------------------------------- if (!metadata.depth) return E_INVALIDARG; if (metadata.depth > UINT16_MAX) return E_INVALIDARG; if (metadata.arraySize > 1) // Direct3D 11 doesn't support arrays of 3D textures return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); size_t depth = metadata.depth; size_t idx = 0; for (size_t level = 0; level < metadata.mipLevels; ++level) { size_t index = metadata.ComputeIndex(level, 0, 0); if (index >= nimages) return E_FAIL; const Image& img = srcImages[index]; if (img.format != metadata.format) return E_FAIL; if (!img.pixels) return E_POINTER; // Verify pixels in image 1 .. (depth-1) are exactly image->slicePitch apart // For 3D textures, this relies on all slices of the same miplevel being continous in memory // (this is how ScratchImage lays them out), which is why we just give the 0th slice to Direct3D 11 const uint8_t* pSlice = img.pixels + img.slicePitch; for (size_t slice = 1; slice < depth; ++slice) { size_t tindex = metadata.ComputeIndex(level, 0, slice); if (tindex >= nimages) return E_FAIL; const Image& timg = srcImages[tindex]; if (!timg.pixels) return E_POINTER; if (timg.pixels != pSlice || timg.format != metadata.format || timg.rowPitch != img.rowPitch || timg.slicePitch != img.slicePitch) return E_FAIL; pSlice = timg.pixels + img.slicePitch; } assert(idx < (metadata.mipLevels * metadata.arraySize)); initData[idx].pSysMem = img.pixels; initData[idx].SysMemPitch = static_cast(img.rowPitch); initData[idx].SysMemSlicePitch = static_cast(img.slicePitch); ++idx; if (depth > 1) depth >>= 1; } } else { //--- 1D or 2D texture case --------------------------------------------------- size_t idx = 0; for (size_t item = 0; item < metadata.arraySize; ++item) { for (size_t level = 0; level < metadata.mipLevels; ++level) { size_t index = metadata.ComputeIndex(level, item, 0); if (index >= nimages) return E_FAIL; const Image& img = srcImages[index]; if (img.format != metadata.format) return E_FAIL; if (!img.pixels) return E_POINTER; assert(idx < (metadata.mipLevels * metadata.arraySize)); initData[idx].pSysMem = img.pixels; initData[idx].SysMemPitch = static_cast(img.rowPitch); initData[idx].SysMemSlicePitch = static_cast(img.slicePitch); ++idx; } } } // Create texture using static initialization data HRESULT hr = E_FAIL; DXGI_FORMAT tformat = (forceSRGB) ? MakeSRGB(metadata.format) : metadata.format; switch (metadata.dimension) { case TEX_DIMENSION_TEXTURE1D: { D3D11_TEXTURE1D_DESC desc = {}; desc.Width = static_cast(metadata.width); desc.MipLevels = static_cast(metadata.mipLevels); desc.ArraySize = static_cast(metadata.arraySize); desc.Format = tformat; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; hr = pDevice->CreateTexture1D(&desc, initData.get(), reinterpret_cast(ppResource)); } break; case TEX_DIMENSION_TEXTURE2D: { D3D11_TEXTURE2D_DESC desc = {}; desc.Width = static_cast(metadata.width); desc.Height = static_cast(metadata.height); desc.MipLevels = static_cast(metadata.mipLevels); desc.ArraySize = static_cast(metadata.arraySize); desc.Format = tformat; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; if (metadata.IsCubemap()) desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; else desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; hr = pDevice->CreateTexture2D(&desc, initData.get(), reinterpret_cast(ppResource)); } break; case TEX_DIMENSION_TEXTURE3D: { D3D11_TEXTURE3D_DESC desc = {}; desc.Width = static_cast(metadata.width); desc.Height = static_cast(metadata.height); desc.Depth = static_cast(metadata.depth); desc.MipLevels = static_cast(metadata.mipLevels); desc.Format = tformat; desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; hr = pDevice->CreateTexture3D(&desc, initData.get(), reinterpret_cast(ppResource)); } break; } return hr; } //------------------------------------------------------------------------------------- // Create a shader resource view and associated texture //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CreateShaderResourceView( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, ID3D11ShaderResourceView** ppSRV) { return CreateShaderResourceViewEx( pDevice, srcImages, nimages, metadata, D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false, ppSRV); } _Use_decl_annotations_ HRESULT DirectX::CreateShaderResourceViewEx( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, D3D11_USAGE usage, unsigned int bindFlags, unsigned int cpuAccessFlags, unsigned int miscFlags, bool forceSRGB, ID3D11ShaderResourceView** ppSRV) { if (!ppSRV) return E_INVALIDARG; *ppSRV = nullptr; ComPtr resource; HRESULT hr = CreateTextureEx(pDevice, srcImages, nimages, metadata, usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB, resource.GetAddressOf()); if (FAILED(hr)) return hr; assert(resource); D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {}; if (forceSRGB) SRVDesc.Format = MakeSRGB(metadata.format); else SRVDesc.Format = metadata.format; switch (metadata.dimension) { case TEX_DIMENSION_TEXTURE1D: if (metadata.arraySize > 1) { SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1DARRAY; SRVDesc.Texture1DArray.MipLevels = static_cast(metadata.mipLevels); SRVDesc.Texture1DArray.ArraySize = static_cast(metadata.arraySize); } else { SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1D; SRVDesc.Texture1D.MipLevels = static_cast(metadata.mipLevels); } break; case TEX_DIMENSION_TEXTURE2D: if (metadata.IsCubemap()) { if (metadata.arraySize > 6) { assert((metadata.arraySize % 6) == 0); SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBEARRAY; SRVDesc.TextureCubeArray.MipLevels = static_cast(metadata.mipLevels); SRVDesc.TextureCubeArray.NumCubes = static_cast(metadata.arraySize / 6); } else { SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBE; SRVDesc.TextureCube.MipLevels = static_cast(metadata.mipLevels); } } else if (metadata.arraySize > 1) { SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2DARRAY; SRVDesc.Texture2DArray.MipLevels = static_cast(metadata.mipLevels); SRVDesc.Texture2DArray.ArraySize = static_cast(metadata.arraySize); } else { SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D; SRVDesc.Texture2D.MipLevels = static_cast(metadata.mipLevels); } break; case TEX_DIMENSION_TEXTURE3D: assert(metadata.arraySize == 1); SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE3D; SRVDesc.Texture3D.MipLevels = static_cast(metadata.mipLevels); break; default: return E_FAIL; } hr = pDevice->CreateShaderResourceView(resource.Get(), &SRVDesc, ppSRV); if (FAILED(hr)) return hr; assert(*ppSRV); return S_OK; } //------------------------------------------------------------------------------------- // Save a texture resource //------------------------------------------------------------------------------------- _Use_decl_annotations_ HRESULT DirectX::CaptureTexture( ID3D11Device* pDevice, ID3D11DeviceContext* pContext, ID3D11Resource* pSource, ScratchImage& result) { if (!pDevice || !pContext || !pSource) return E_INVALIDARG; D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN; pSource->GetType(&resType); HRESULT hr; switch (resType) { case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { ComPtr pTexture; hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf())); if (FAILED(hr)) break; assert(pTexture); D3D11_TEXTURE1D_DESC desc; pTexture->GetDesc(&desc); ComPtr pStaging; if ((desc.Usage == D3D11_USAGE_STAGING) && (desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { // Handle case where the source is already a staging texture we can use directly pStaging = pTexture; } else { desc.BindFlags = 0; desc.MiscFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.Usage = D3D11_USAGE_STAGING; hr = pDevice->CreateTexture1D(&desc, 0, pStaging.GetAddressOf()); if (FAILED(hr)) break; assert(pStaging); pContext->CopyResource(pStaging.Get(), pSource); } TexMetadata mdata; mdata.width = desc.Width; mdata.height = mdata.depth = 1; mdata.arraySize = desc.ArraySize; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = 0; mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE1D; hr = result.Initialize(mdata); if (FAILED(hr)) break; hr = Capture(pContext, pStaging.Get(), mdata, result); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { ComPtr pTexture; hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf())); if (FAILED(hr)) break; assert(pTexture); D3D11_TEXTURE2D_DESC desc; pTexture->GetDesc(&desc); ComPtr pStaging; if (desc.SampleDesc.Count > 1) { desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; ComPtr pTemp; hr = pDevice->CreateTexture2D(&desc, 0, pTemp.GetAddressOf()); if (FAILED(hr)) break; assert(pTemp); DXGI_FORMAT fmt = desc.Format; if (IsTypeless(fmt)) { // Assume a UNORM if it exists otherwise use FLOAT fmt = MakeTypelessUNORM(fmt); fmt = MakeTypelessFLOAT(fmt); } UINT support = 0; hr = pDevice->CheckFormatSupport(fmt, &support); if (FAILED(hr)) break; if (!(support & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE)) { hr = E_FAIL; break; } for (UINT item = 0; item < desc.ArraySize; ++item) { for (UINT level = 0; level < desc.MipLevels; ++level) { UINT index = D3D11CalcSubresource(level, item, desc.MipLevels); pContext->ResolveSubresource(pTemp.Get(), index, pSource, index, fmt); } } desc.BindFlags = 0; desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.Usage = D3D11_USAGE_STAGING; hr = pDevice->CreateTexture2D(&desc, 0, pStaging.GetAddressOf()); if (FAILED(hr)) break; assert(pStaging); pContext->CopyResource(pStaging.Get(), pTemp.Get()); } else if ((desc.Usage == D3D11_USAGE_STAGING) && (desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { // Handle case where the source is already a staging texture we can use directly pStaging = pTexture; } else { desc.BindFlags = 0; desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.Usage = D3D11_USAGE_STAGING; hr = pDevice->CreateTexture2D(&desc, 0, &pStaging); if (FAILED(hr)) break; assert(pStaging); pContext->CopyResource(pStaging.Get(), pSource); } TexMetadata mdata; mdata.width = desc.Width; mdata.height = desc.Height; mdata.depth = 1; mdata.arraySize = desc.ArraySize; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = (desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) ? TEX_MISC_TEXTURECUBE : 0; mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE2D; hr = result.Initialize(mdata); if (FAILED(hr)) break; hr = Capture(pContext, pStaging.Get(), mdata, result); } break; case D3D11_RESOURCE_DIMENSION_TEXTURE3D: { ComPtr pTexture; hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf())); if (FAILED(hr)) break; assert(pTexture); D3D11_TEXTURE3D_DESC desc; pTexture->GetDesc(&desc); ComPtr pStaging; if ((desc.Usage == D3D11_USAGE_STAGING) && (desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { // Handle case where the source is already a staging texture we can use directly pStaging = pTexture; } else { desc.BindFlags = 0; desc.MiscFlags = 0; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.Usage = D3D11_USAGE_STAGING; hr = pDevice->CreateTexture3D(&desc, 0, pStaging.GetAddressOf()); if (FAILED(hr)) break; assert(pStaging); pContext->CopyResource(pStaging.Get(), pSource); } TexMetadata mdata; mdata.width = desc.Width; mdata.height = desc.Height; mdata.depth = desc.Depth; mdata.arraySize = 1; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = 0; mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE3D; hr = result.Initialize(mdata); if (FAILED(hr)) break; hr = Capture(pContext, pStaging.Get(), mdata, result); } break; default: hr = E_FAIL; break; } if (FAILED(hr)) { result.Release(); return hr; } return S_OK; }