crossxtex/DirectXTex/DirectXTexD3D11.cpp

970 lines
31 KiB
C++
Raw Normal View History

2016-08-22 18:26:36 +00:00
//-------------------------------------------------------------------------------------
// DirectXTexD3D11.cpp
//
// DirectX Texture Library - Direct3D 11 helpers
//
// Copyright (c) Microsoft Corporation. All rights reserved.
2018-02-24 06:24:46 +00:00
// Licensed under the MIT License.
2016-08-22 18:26:36 +00:00
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//-------------------------------------------------------------------------------------
#include "DirectXTexP.h"
2016-08-22 18:26:36 +00:00
2020-08-12 21:35:39 +00:00
#ifdef _GAMING_XBOX
#error This module is not supported for GDK
#elif !defined(_XBOX_ONE) || !defined(_TITLE)
2016-08-22 18:26:36 +00:00
#include <d3d10.h>
#endif
#ifndef IID_GRAPHICS_PPV_ARGS
#define IID_GRAPHICS_PPV_ARGS(x) IID_PPV_ARGS(x)
#endif
2016-09-09 02:09:46 +00:00
using namespace DirectX;
2016-08-22 18:26:36 +00:00
using Microsoft::WRL::ComPtr;
2017-07-12 07:56:51 +00:00
static_assert(static_cast<int>(TEX_DIMENSION_TEXTURE1D) == static_cast<int>(D3D11_RESOURCE_DIMENSION_TEXTURE1D), "header enum mismatch");
static_assert(static_cast<int>(TEX_DIMENSION_TEXTURE2D) == static_cast<int>(D3D11_RESOURCE_DIMENSION_TEXTURE2D), "header enum mismatch");
static_assert(static_cast<int>(TEX_DIMENSION_TEXTURE3D) == static_cast<int>(D3D11_RESOURCE_DIMENSION_TEXTURE3D), "header enum mismatch");
2017-01-07 01:16:43 +00:00
2016-09-09 02:09:46 +00:00
namespace
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
HRESULT Capture(
_In_ ID3D11DeviceContext* pContext,
_In_ ID3D11Resource* pSource,
const TexMetadata& metadata,
2019-12-13 08:01:17 +00:00
const ScratchImage& result) noexcept
2016-09-09 02:09:46 +00:00
{
if (!pContext || !pSource || !result.GetPixels())
return E_POINTER;
2016-08-22 18:26:36 +00:00
#if defined(_XBOX_ONE) && defined(_TITLE)
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11Device> d3dDevice;
pContext->GetDevice(d3dDevice.GetAddressOf());
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (d3dDevice->GetCreationFlags() & D3D11_CREATE_DEVICE_IMMEDIATE_CONTEXT_FAST_SEMANTICS)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11DeviceX> d3dDeviceX;
HRESULT hr = d3dDevice.As(&d3dDeviceX);
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11DeviceContextX> d3dContextX;
hr = pContext->QueryInterface(IID_GRAPHICS_PPV_ARGS(d3dContextX.GetAddressOf()));
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
UINT64 copyFence = d3dContextX->InsertFence(0);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
while (d3dDeviceX->IsFencePending(copyFence))
{
SwitchToThread();
}
}
#endif
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (metadata.IsVolumemap())
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
//--- Volume texture ----------------------------------------------------------
assert(metadata.arraySize == 1);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
size_t height = metadata.height;
size_t depth = metadata.depth;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t level = 0; level < metadata.mipLevels; ++level)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
UINT dindex = D3D11CalcSubresource(static_cast<UINT>(level), 0, static_cast<UINT>(metadata.mipLevels));
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
D3D11_MAPPED_SUBRESOURCE mapped;
HRESULT hr = pContext->Map(pSource, dindex, D3D11_MAP_READ, 0, &mapped);
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto pslice = static_cast<const uint8_t*>(mapped.pData);
2016-09-09 02:09:46 +00:00
if (!pslice)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
pContext->Unmap(pSource, dindex);
return E_POINTER;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
size_t lines = ComputeScanlines(metadata.format, height);
if (!lines)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
pContext->Unmap(pSource, dindex);
return E_UNEXPECTED;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
for (size_t slice = 0; slice < depth; ++slice)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
const Image* img = result.GetImage(level, 0, slice);
if (!img)
{
pContext->Unmap(pSource, dindex);
return E_FAIL;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (!img->pixels)
{
pContext->Unmap(pSource, dindex);
return E_POINTER;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const uint8_t* sptr = pslice;
uint8_t* dptr = img->pixels;
for (size_t h = 0; h < lines; ++h)
{
size_t msize = std::min<size_t>(img->rowPitch, mapped.RowPitch);
memcpy_s(dptr, img->rowPitch, sptr, msize);
sptr += mapped.RowPitch;
dptr += img->rowPitch;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
pslice += mapped.DepthPitch;
}
pContext->Unmap(pSource, dindex);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (height > 1)
height >>= 1;
if (depth > 1)
depth >>= 1;
}
}
else
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
//--- 1D or 2D texture --------------------------------------------------------
assert(metadata.depth == 1);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t item = 0; item < metadata.arraySize; ++item)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t height = metadata.height;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t level = 0; level < metadata.mipLevels; ++level)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
UINT dindex = D3D11CalcSubresource(static_cast<UINT>(level), static_cast<UINT>(item), static_cast<UINT>(metadata.mipLevels));
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
D3D11_MAPPED_SUBRESOURCE mapped;
HRESULT hr = pContext->Map(pSource, dindex, D3D11_MAP_READ, 0, &mapped);
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const Image* img = result.GetImage(level, item, 0);
if (!img)
{
pContext->Unmap(pSource, dindex);
return E_FAIL;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
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;
}
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto sptr = static_cast<const uint8_t*>(mapped.pData);
2016-09-09 02:09:46 +00:00
uint8_t* dptr = img->pixels;
for (size_t h = 0; h < lines; ++h)
{
size_t msize = std::min<size_t>(img->rowPitch, mapped.RowPitch);
memcpy_s(dptr, img->rowPitch, sptr, msize);
sptr += mapped.RowPitch;
dptr += img->rowPitch;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
pContext->Unmap(pSource, dindex);
if (height > 1)
height >>= 1;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
return S_OK;
}
2016-08-22 18:26:36 +00:00
}
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Determine if given texture metadata is supported on the given device
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
bool DirectX::IsSupportedTexture(
ID3D11Device* pDevice,
2019-12-12 03:52:48 +00:00
const TexMetadata& metadata) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!pDevice)
2016-08-22 18:26:36 +00:00
return false;
D3D_FEATURE_LEVEL fl = pDevice->GetFeatureLevel();
// Validate format
DXGI_FORMAT fmt = metadata.format;
2016-09-09 02:09:46 +00:00
if (!IsValid(fmt))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
switch (fmt)
2016-08-22 18:26:36 +00:00
{
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:
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_10_0)
2016-08-22 18:26:36 +00:00
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:
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_11_0)
2016-08-22 18:26:36 +00:00
return false;
break;
2017-07-12 07:56:51 +00:00
default:
break;
2016-08-22 18:26:36 +00:00
}
// Validate miplevel count
2016-09-09 02:09:46 +00:00
if (metadata.mipLevels > D3D11_REQ_MIP_LEVELS)
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
2016-08-22 18:26:36 +00:00
// 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;
2016-09-09 02:09:46 +00:00
HRESULT hr = pDevice->CheckFormatSupport(fmt, &formatSupport);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
{
formatSupport = 0;
}
if (metadata.mipLevels > 1 && !(formatSupport & D3D11_FORMAT_SUPPORT_MIP))
{
return false;
}
2016-09-09 02:09:46 +00:00
switch (metadata.dimension)
2016-08-22 18:26:36 +00:00
{
case TEX_DIMENSION_TEXTURE1D:
2016-09-09 02:09:46 +00:00
if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE1D))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D11_REQ_TEXTURE1D_U_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_11_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D10_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D10_REQ_TEXTURE1D_U_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_10_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((arraySize > 1) || (iWidth > D3D_FL9_3_REQ_TEXTURE1D_U_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((fl < D3D_FEATURE_LEVEL_9_3) && (iWidth > D3D_FL9_1_REQ_TEXTURE1D_U_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
}
}
break;
case TEX_DIMENSION_TEXTURE2D:
2016-09-09 02:09:46 +00:00
if (metadata.IsCubemap())
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D11_REQ_TEXTURECUBE_DIMENSION)
|| (iHeight > D3D11_REQ_TEXTURECUBE_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_11_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D10_REQ_TEXTURECUBE_DIMENSION)
|| (iHeight > D3D10_REQ_TEXTURECUBE_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((fl < D3D_FEATURE_LEVEL_10_1) && (arraySize != 6))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_10_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((iWidth > D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION)
|| (iHeight > D3D_FL9_3_REQ_TEXTURECUBE_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((fl < D3D_FEATURE_LEVEL_9_3)
&& ((iWidth > D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION)
|| (iHeight > D3D_FL9_1_REQ_TEXTURECUBE_DIMENSION)))
2016-08-22 18:26:36 +00:00
return false;
}
}
}
else // Not a cube map
{
2016-09-09 02:09:46 +00:00
if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION)
|| (iHeight > D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_11_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((arraySize > D3D10_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION)
|| (iWidth > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION)
|| (iHeight > D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_10_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ((arraySize > 1)
|| (iWidth > D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION)
|| (iHeight > D3D_FL9_3_REQ_TEXTURE2D_U_OR_V_DIMENSION))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
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)))
2016-08-22 18:26:36 +00:00
return false;
}
}
}
break;
case TEX_DIMENSION_TEXTURE3D:
2016-09-09 02:09:46 +00:00
if (!(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURE3D))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
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))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_11_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
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))
2016-08-22 18:26:36 +00:00
return false;
2016-09-09 02:09:46 +00:00
if (fl < D3D_FEATURE_LEVEL_10_0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
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))
2016-08-22 18:26:36 +00:00
return false;
}
}
break;
default:
// Not a supported dimension
return false;
}
return true;
}
//-------------------------------------------------------------------------------------
// Create a texture resource
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::CreateTexture(
ID3D11Device* pDevice,
const Image* srcImages,
size_t nimages,
const TexMetadata& metadata,
2019-12-12 03:52:48 +00:00
ID3D11Resource** ppResource) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return CreateTextureEx(
pDevice, srcImages, nimages, metadata,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
ppResource);
2016-08-22 18:26:36 +00:00
}
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
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,
2019-12-12 03:52:48 +00:00
ID3D11Resource** ppResource) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!pDevice || !srcImages || !nimages || !ppResource)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
*ppResource = nullptr;
2016-09-09 02:09:46 +00:00
if (!metadata.mipLevels || !metadata.arraySize)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
2016-09-09 02:09:46 +00:00
if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX)
2017-01-07 01:16:43 +00:00
|| (metadata.mipLevels > UINT16_MAX) || (metadata.arraySize > UINT16_MAX))
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
2016-09-09 02:09:46 +00:00
std::unique_ptr<D3D11_SUBRESOURCE_DATA[]> initData(new (std::nothrow) D3D11_SUBRESOURCE_DATA[metadata.mipLevels * metadata.arraySize]);
if (!initData)
2016-08-22 18:26:36 +00:00
return E_OUTOFMEMORY;
// Fill out subresource array
2016-09-09 02:09:46 +00:00
if (metadata.IsVolumemap())
2016-08-22 18:26:36 +00:00
{
//--- Volume case -------------------------------------------------------------
2016-09-09 02:09:46 +00:00
if (!metadata.depth)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
2017-01-07 01:16:43 +00:00
if (metadata.depth > UINT16_MAX)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
2016-09-09 02:09:46 +00:00
if (metadata.arraySize > 1)
2016-08-22 18:26:36 +00:00
// Direct3D 11 doesn't support arrays of 3D textures
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
2016-08-22 18:26:36 +00:00
size_t depth = metadata.depth;
size_t idx = 0;
2016-09-09 02:09:46 +00:00
for (size_t level = 0; level < metadata.mipLevels; ++level)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t index = metadata.ComputeIndex(level, 0, 0);
if (index >= nimages)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
const Image& img = srcImages[index];
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (img.format != metadata.format)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (!img.pixels)
2016-08-22 18:26:36 +00:00
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;
2016-09-09 02:09:46 +00:00
for (size_t slice = 1; slice < depth; ++slice)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t tindex = metadata.ComputeIndex(level, 0, slice);
if (tindex >= nimages)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
const Image& timg = srcImages[tindex];
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (!timg.pixels)
2016-08-22 18:26:36 +00:00
return E_POINTER;
2016-09-09 02:09:46 +00:00
if (timg.pixels != pSlice
|| timg.format != metadata.format
|| timg.rowPitch != img.rowPitch
|| timg.slicePitch != img.slicePitch)
2016-08-22 18:26:36 +00:00
return E_FAIL;
pSlice = timg.pixels + img.slicePitch;
}
2016-09-09 02:09:46 +00:00
assert(idx < (metadata.mipLevels * metadata.arraySize));
2016-08-22 18:26:36 +00:00
initData[idx].pSysMem = img.pixels;
2016-09-09 02:09:46 +00:00
initData[idx].SysMemPitch = static_cast<DWORD>(img.rowPitch);
initData[idx].SysMemSlicePitch = static_cast<DWORD>(img.slicePitch);
2016-08-22 18:26:36 +00:00
++idx;
2016-09-09 02:09:46 +00:00
if (depth > 1)
2016-08-22 18:26:36 +00:00
depth >>= 1;
}
}
else
{
//--- 1D or 2D texture case ---------------------------------------------------
size_t idx = 0;
2016-09-09 02:09:46 +00:00
for (size_t item = 0; item < metadata.arraySize; ++item)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for (size_t level = 0; level < metadata.mipLevels; ++level)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t index = metadata.ComputeIndex(level, item, 0);
if (index >= nimages)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
const Image& img = srcImages[index];
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (img.format != metadata.format)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (!img.pixels)
2016-08-22 18:26:36 +00:00
return E_POINTER;
2016-09-09 02:09:46 +00:00
assert(idx < (metadata.mipLevels * metadata.arraySize));
2016-08-22 18:26:36 +00:00
initData[idx].pSysMem = img.pixels;
2016-09-09 02:09:46 +00:00
initData[idx].SysMemPitch = static_cast<DWORD>(img.rowPitch);
initData[idx].SysMemSlicePitch = static_cast<DWORD>(img.slicePitch);
2016-08-22 18:26:36 +00:00
++idx;
}
}
}
// Create texture using static initialization data
2018-03-29 19:00:16 +00:00
HRESULT hr = E_UNEXPECTED;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
DXGI_FORMAT tformat = (forceSRGB) ? MakeSRGB(metadata.format) : metadata.format;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
switch (metadata.dimension)
2016-08-22 18:26:36 +00:00
{
case TEX_DIMENSION_TEXTURE1D:
2016-09-09 02:09:46 +00:00
{
2017-01-07 01:16:43 +00:00
D3D11_TEXTURE1D_DESC desc = {};
2016-09-09 02:09:46 +00:00
desc.Width = static_cast<UINT>(metadata.width);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.ArraySize = static_cast<UINT>(metadata.arraySize);
desc.Format = tformat;
desc.Usage = usage;
desc.BindFlags = bindFlags;
desc.CPUAccessFlags = cpuAccessFlags;
desc.MiscFlags = miscFlags & ~static_cast<uint32_t>(D3D11_RESOURCE_MISC_TEXTURECUBE);
2016-09-09 02:09:46 +00:00
hr = pDevice->CreateTexture1D(&desc, initData.get(), reinterpret_cast<ID3D11Texture1D**>(ppResource));
}
break;
2016-08-22 18:26:36 +00:00
case TEX_DIMENSION_TEXTURE2D:
2016-09-09 02:09:46 +00:00
{
2017-01-07 01:16:43 +00:00
D3D11_TEXTURE2D_DESC desc = {};
2016-09-09 02:09:46 +00:00
desc.Width = static_cast<UINT>(metadata.width);
desc.Height = static_cast<UINT>(metadata.height);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.ArraySize = static_cast<UINT>(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 & ~static_cast<uint32_t>(D3D11_RESOURCE_MISC_TEXTURECUBE);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
hr = pDevice->CreateTexture2D(&desc, initData.get(), reinterpret_cast<ID3D11Texture2D**>(ppResource));
}
break;
2016-08-22 18:26:36 +00:00
case TEX_DIMENSION_TEXTURE3D:
2016-09-09 02:09:46 +00:00
{
2017-01-07 01:16:43 +00:00
D3D11_TEXTURE3D_DESC desc = {};
2016-09-09 02:09:46 +00:00
desc.Width = static_cast<UINT>(metadata.width);
desc.Height = static_cast<UINT>(metadata.height);
desc.Depth = static_cast<UINT>(metadata.depth);
desc.MipLevels = static_cast<UINT>(metadata.mipLevels);
desc.Format = tformat;
desc.Usage = usage;
desc.BindFlags = bindFlags;
desc.CPUAccessFlags = cpuAccessFlags;
desc.MiscFlags = miscFlags & ~static_cast<uint32_t>(D3D11_RESOURCE_MISC_TEXTURECUBE);
2016-09-09 02:09:46 +00:00
hr = pDevice->CreateTexture3D(&desc, initData.get(), reinterpret_cast<ID3D11Texture3D**>(ppResource));
}
break;
2016-08-22 18:26:36 +00:00
}
return hr;
}
//-------------------------------------------------------------------------------------
// Create a shader resource view and associated texture
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::CreateShaderResourceView(
ID3D11Device* pDevice,
const Image* srcImages,
size_t nimages,
const TexMetadata& metadata,
2019-12-12 03:52:48 +00:00
ID3D11ShaderResourceView** ppSRV) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return CreateShaderResourceViewEx(
pDevice, srcImages, nimages, metadata,
D3D11_USAGE_DEFAULT, D3D11_BIND_SHADER_RESOURCE, 0, 0, false,
ppSRV);
2016-08-22 18:26:36 +00:00
}
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
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,
2019-12-12 03:52:48 +00:00
ID3D11ShaderResourceView** ppSRV) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!ppSRV)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
*ppSRV = nullptr;
if (!(bindFlags & D3D11_BIND_SHADER_RESOURCE))
return E_INVALIDARG;
2016-08-22 18:26:36 +00:00
ComPtr<ID3D11Resource> resource;
2016-09-09 02:09:46 +00:00
HRESULT hr = CreateTextureEx(pDevice, srcImages, nimages, metadata,
usage, bindFlags, cpuAccessFlags, miscFlags, forceSRGB,
resource.GetAddressOf());
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
2016-09-09 02:09:46 +00:00
assert(resource);
2016-08-22 18:26:36 +00:00
D3D11_SHADER_RESOURCE_VIEW_DESC SRVDesc = {};
2016-09-09 02:09:46 +00:00
if (forceSRGB)
SRVDesc.Format = MakeSRGB(metadata.format);
2016-08-22 18:26:36 +00:00
else
SRVDesc.Format = metadata.format;
2016-09-09 02:09:46 +00:00
switch (metadata.dimension)
2016-08-22 18:26:36 +00:00
{
case TEX_DIMENSION_TEXTURE1D:
2016-09-09 02:09:46 +00:00
if (metadata.arraySize > 1)
2016-08-22 18:26:36 +00:00
{
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1DARRAY;
2016-09-09 02:09:46 +00:00
SRVDesc.Texture1DArray.MipLevels = static_cast<UINT>(metadata.mipLevels);
SRVDesc.Texture1DArray.ArraySize = static_cast<UINT>(metadata.arraySize);
2016-08-22 18:26:36 +00:00
}
else
{
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE1D;
2016-09-09 02:09:46 +00:00
SRVDesc.Texture1D.MipLevels = static_cast<UINT>(metadata.mipLevels);
2016-08-22 18:26:36 +00:00
}
break;
case TEX_DIMENSION_TEXTURE2D:
2016-09-09 02:09:46 +00:00
if (metadata.IsCubemap())
2016-08-22 18:26:36 +00:00
{
if (metadata.arraySize > 6)
{
2016-09-09 02:09:46 +00:00
assert((metadata.arraySize % 6) == 0);
2016-08-22 18:26:36 +00:00
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBEARRAY;
2016-09-09 02:09:46 +00:00
SRVDesc.TextureCubeArray.MipLevels = static_cast<UINT>(metadata.mipLevels);
SRVDesc.TextureCubeArray.NumCubes = static_cast<UINT>(metadata.arraySize / 6);
2016-08-22 18:26:36 +00:00
}
else
{
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURECUBE;
2016-09-09 02:09:46 +00:00
SRVDesc.TextureCube.MipLevels = static_cast<UINT>(metadata.mipLevels);
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
else if (metadata.arraySize > 1)
2016-08-22 18:26:36 +00:00
{
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2DARRAY;
2016-09-09 02:09:46 +00:00
SRVDesc.Texture2DArray.MipLevels = static_cast<UINT>(metadata.mipLevels);
SRVDesc.Texture2DArray.ArraySize = static_cast<UINT>(metadata.arraySize);
2016-08-22 18:26:36 +00:00
}
else
{
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D;
2016-09-09 02:09:46 +00:00
SRVDesc.Texture2D.MipLevels = static_cast<UINT>(metadata.mipLevels);
2016-08-22 18:26:36 +00:00
}
break;
case TEX_DIMENSION_TEXTURE3D:
2016-09-09 02:09:46 +00:00
assert(metadata.arraySize == 1);
2016-08-22 18:26:36 +00:00
SRVDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE3D;
2016-09-09 02:09:46 +00:00
SRVDesc.Texture3D.MipLevels = static_cast<UINT>(metadata.mipLevels);
2016-08-22 18:26:36 +00:00
break;
default:
return E_FAIL;
}
2016-09-09 02:09:46 +00:00
hr = pDevice->CreateShaderResourceView(resource.Get(), &SRVDesc, ppSRV);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
2016-09-09 02:09:46 +00:00
assert(*ppSRV);
2016-08-22 18:26:36 +00:00
return S_OK;
}
//-------------------------------------------------------------------------------------
2017-01-07 01:16:43 +00:00
// Save a texture resource
2016-08-22 18:26:36 +00:00
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::CaptureTexture(
ID3D11Device* pDevice,
ID3D11DeviceContext* pContext,
ID3D11Resource* pSource,
2019-12-13 08:01:17 +00:00
ScratchImage& result) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!pDevice || !pContext || !pSource)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
D3D11_RESOURCE_DIMENSION resType = D3D11_RESOURCE_DIMENSION_UNKNOWN;
2016-09-09 02:09:46 +00:00
pSource->GetType(&resType);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
HRESULT hr = E_UNEXPECTED;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
switch (resType)
2016-08-22 18:26:36 +00:00
{
case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
2016-09-09 02:09:46 +00:00
{
ComPtr<ID3D11Texture1D> pTexture;
hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf()));
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert(pTexture);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
D3D11_TEXTURE1D_DESC desc;
pTexture->GetDesc(&desc);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11Texture1D> pStaging;
2016-09-14 22:37:44 +00:00
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;
2018-06-13 01:07:34 +00:00
hr = pDevice->CreateTexture1D(&desc, nullptr, pStaging.GetAddressOf());
2016-09-14 22:37:44 +00:00
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-14 22:37:44 +00:00
assert(pStaging);
2016-08-22 18:26:36 +00:00
2016-09-14 22:37:44 +00:00
pContext->CopyResource(pStaging.Get(), pSource);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
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;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
hr = result.Initialize(mdata);
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
hr = Capture(pContext, pStaging.Get(), mdata, result);
}
break;
2016-08-22 18:26:36 +00:00
case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
2016-09-09 02:09:46 +00:00
{
ComPtr<ID3D11Texture2D> pTexture;
hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf()));
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert(pTexture);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
D3D11_TEXTURE2D_DESC desc;
pTexture->GetDesc(&desc);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11Texture2D> pStaging;
if (desc.SampleDesc.Count > 1)
{
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
ComPtr<ID3D11Texture2D> pTemp;
2018-06-13 01:07:34 +00:00
hr = pDevice->CreateTexture2D(&desc, nullptr, pTemp.GetAddressOf());
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert(pTemp);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
DXGI_FORMAT fmt = desc.Format;
if (IsTypeless(fmt))
{
// Assume a UNORM if it exists otherwise use FLOAT
fmt = MakeTypelessUNORM(fmt);
fmt = MakeTypelessFLOAT(fmt);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
UINT support = 0;
hr = pDevice->CheckFormatSupport(fmt, &support);
if (FAILED(hr))
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (!(support & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE))
{
hr = E_FAIL;
break;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (UINT item = 0; item < desc.ArraySize; ++item)
{
for (UINT level = 0; level < desc.MipLevels; ++level)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
UINT index = D3D11CalcSubresource(level, item, desc.MipLevels);
pContext->ResolveSubresource(pTemp.Get(), index, pSource, index, fmt);
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
desc.BindFlags = 0;
desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
2016-08-22 18:26:36 +00:00
2018-06-13 01:07:34 +00:00
hr = pDevice->CreateTexture2D(&desc, nullptr, pStaging.GetAddressOf());
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
break;
2016-09-09 02:09:46 +00:00
assert(pStaging);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
pContext->CopyResource(pStaging.Get(), pTemp.Get());
}
2016-09-14 22:37:44 +00:00
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;
}
2016-09-09 02:09:46 +00:00
else
2016-08-22 18:26:36 +00:00
{
desc.BindFlags = 0;
2016-09-09 02:09:46 +00:00
desc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE;
2016-08-22 18:26:36 +00:00
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.Usage = D3D11_USAGE_STAGING;
2018-06-13 01:07:34 +00:00
hr = pDevice->CreateTexture2D(&desc, nullptr, &pStaging);
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
break;
2016-09-09 02:09:46 +00:00
assert(pStaging);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
pContext->CopyResource(pStaging.Get(), pSource);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
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 : 0u;
2016-09-09 02:09:46 +00:00
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;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
{
ComPtr<ID3D11Texture3D> pTexture;
hr = pSource->QueryInterface(IID_GRAPHICS_PPV_ARGS(pTexture.GetAddressOf()));
if (FAILED(hr))
break;
assert(pTexture);
D3D11_TEXTURE3D_DESC desc;
pTexture->GetDesc(&desc);
ComPtr<ID3D11Texture3D> pStaging;
2016-09-14 22:37:44 +00:00
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;
2018-06-13 01:07:34 +00:00
hr = pDevice->CreateTexture3D(&desc, nullptr, pStaging.GetAddressOf());
2016-09-14 22:37:44 +00:00
if (FAILED(hr))
break;
2016-09-09 02:09:46 +00:00
2016-09-14 22:37:44 +00:00
assert(pStaging);
2016-09-09 02:09:46 +00:00
2016-09-14 22:37:44 +00:00
pContext->CopyResource(pStaging.Get(), pSource);
}
2016-09-09 02:09:46 +00:00
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;
2016-08-22 18:26:36 +00:00
default:
hr = E_FAIL;
break;
}
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
{
result.Release();
return hr;
}
return S_OK;
}