crossxtex/DirectXTex/DirectXTexCompressGPU.cpp

403 lines
12 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------------------
// DirectXTexCompressGPU.cpp
//
// DirectX Texture Library - DirectCompute-based texture compression
//
// 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"
#include "bcdirectcompute.h"
namespace DirectX
{
inline static DWORD _GetSRGBFlags( _In_ DWORD compress )
{
static_assert( TEX_COMPRESS_SRGB_IN == TEX_FILTER_SRGB_IN, "TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB*" );
static_assert( TEX_COMPRESS_SRGB_OUT == TEX_FILTER_SRGB_OUT, "TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB*" );
static_assert( TEX_COMPRESS_SRGB == TEX_FILTER_SRGB, "TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB*" );
return ( compress & TEX_COMPRESS_SRGB );
}
//-------------------------------------------------------------------------------------
// Converts to R8G8B8A8_UNORM or R8G8B8A8_UNORM_SRGB doing any conversion logic needed
//-------------------------------------------------------------------------------------
static HRESULT _ConvertToRGBA32( _In_ const Image& srcImage, _In_ ScratchImage& image, bool srgb, _In_ DWORD filter )
{
if ( !srcImage.pixels )
return E_POINTER;
DXGI_FORMAT format = srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;
HRESULT hr = image.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 );
if ( FAILED(hr) )
return hr;
const Image *img = image.GetImage( 0, 0, 0 );
if ( !img )
{
image.Release();
return E_POINTER;
}
uint8_t* pDest = img->pixels;
if ( !pDest )
{
image.Release();
return E_POINTER;
}
ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( ( sizeof(XMVECTOR) * srcImage.width ), 16 ) ) );
if ( !scanline )
{
image.Release();
return E_OUTOFMEMORY;
}
const uint8_t *pSrc = srcImage.pixels;
for( size_t h = 0; h < srcImage.height; ++h )
{
if ( !_LoadScanline( scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) )
{
image.Release();
return E_FAIL;
}
_ConvertScanline( scanline.get(), srcImage.width, format, srcImage.format, filter );
if ( !_StoreScanline( pDest, img->rowPitch, format, scanline.get(), srcImage.width ) )
{
image.Release();
return E_FAIL;
}
pSrc += srcImage.rowPitch;
pDest += img->rowPitch;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// Converts to DXGI_FORMAT_R32G32B32A32_FLOAT doing any conversion logic needed
//-------------------------------------------------------------------------------------
static HRESULT _ConvertToRGBAF32( const Image& srcImage, ScratchImage& image, _In_ DWORD filter )
{
if ( !srcImage.pixels )
return E_POINTER;
HRESULT hr = image.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 );
if ( FAILED(hr) )
return hr;
const Image *img = image.GetImage( 0, 0, 0 );
if ( !img )
{
image.Release();
return E_POINTER;
}
uint8_t* pDest = img->pixels;
if ( !pDest )
{
image.Release();
return E_POINTER;
}
const uint8_t *pSrc = srcImage.pixels;
for( size_t h = 0; h < srcImage.height; ++h )
{
if ( !_LoadScanline( reinterpret_cast<XMVECTOR*>(pDest), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) )
{
image.Release();
return E_FAIL;
}
_ConvertScanline( reinterpret_cast<XMVECTOR*>(pDest), srcImage.width, DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.format, filter );
pSrc += srcImage.rowPitch;
pDest += img->rowPitch;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// Compress using GPU, converting to the proper input format for the shader if needed
//-------------------------------------------------------------------------------------
inline static HRESULT _GPUCompress( _In_ GPUCompressBC* gpubc, _In_ const Image& srcImage, _In_ const Image& destImage, _In_ DWORD compress )
{
if ( !gpubc )
return E_POINTER;
assert( srcImage.pixels && destImage.pixels );
DXGI_FORMAT format = gpubc->GetSourceFormat();
if ( srcImage.format == format )
{
// Input is already in our required source format
return gpubc->Compress( srcImage, destImage );
}
else
{
// Convert format and then use as the source image
ScratchImage image;
HRESULT hr;
DWORD srgb = _GetSRGBFlags( compress );
switch( format )
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
hr = _ConvertToRGBA32( srcImage, image, false, srgb );
break;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
hr = _ConvertToRGBA32( srcImage, image, true, srgb );
break;
case DXGI_FORMAT_R32G32B32A32_FLOAT:
hr = _ConvertToRGBAF32( srcImage, image, srgb );
break;
default:
hr = E_UNEXPECTED;
break;
}
if ( FAILED(hr) )
return hr;
const Image *img = image.GetImage( 0, 0, 0 );
if ( !img )
return E_POINTER;
return gpubc->Compress( *img, destImage );
}
}
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Compression
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT Compress( ID3D11Device* pDevice, const Image& srcImage, DXGI_FORMAT format, DWORD compress, float alphaWeight, ScratchImage& image )
{
if ( !pDevice || IsCompressed(srcImage.format) || !IsCompressed(format) )
return E_INVALIDARG;
if ( IsTypeless(format)
|| IsTypeless(srcImage.format) || IsPlanar(srcImage.format) || IsPalettized(srcImage.format) )
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
// Setup GPU compressor
std::unique_ptr<GPUCompressBC> gpubc( new (std::nothrow) GPUCompressBC );
if ( !gpubc )
return E_OUTOFMEMORY;
HRESULT hr = gpubc->Initialize( pDevice );
if ( FAILED(hr) )
return hr;
hr = gpubc->Prepare( srcImage.width, srcImage.height, format, alphaWeight, !(compress & TEX_COMPRESS_BC7_USE_3SUBSETS) );
if ( FAILED(hr) )
return hr;
// Create workspace for result
hr = image.Initialize2D( format, srcImage.width, srcImage.height, 1, 1 );
if ( FAILED(hr) )
return hr;
const Image *img = image.GetImage( 0, 0, 0 );
if ( !img )
{
image.Release();
return E_POINTER;
}
hr = _GPUCompress( gpubc.get(), srcImage, *img, compress );
if ( FAILED(hr) )
image.Release();
return hr;
}
_Use_decl_annotations_
HRESULT Compress( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata,
DXGI_FORMAT format, DWORD compress, float alphaWeight, ScratchImage& cImages )
{
if ( !pDevice || !srcImages || !nimages )
return E_INVALIDARG;
if ( IsCompressed(metadata.format) || !IsCompressed(format) )
return E_INVALIDARG;
if ( IsTypeless(format)
|| IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format) )
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
cImages.Release();
// Setup GPU compressor
std::unique_ptr<GPUCompressBC> gpubc( new (std::nothrow) GPUCompressBC );
if ( !gpubc )
return E_OUTOFMEMORY;
HRESULT hr = gpubc->Initialize( pDevice );
if ( FAILED(hr) )
return hr;
// Create workspace for result
TexMetadata mdata2 = metadata;
mdata2.format = format;
hr = cImages.Initialize( mdata2 );
if ( FAILED(hr) )
return hr;
if ( nimages != cImages.GetImageCount() )
{
cImages.Release();
return E_FAIL;
}
const Image* dest = cImages.GetImages();
if ( !dest )
{
cImages.Release();
return E_POINTER;
}
// Process images (ordered by size)
switch( metadata.dimension )
{
case TEX_DIMENSION_TEXTURE1D:
case TEX_DIMENSION_TEXTURE2D:
{
size_t w = metadata.width;
size_t h = metadata.height;
for( size_t level=0; level < metadata.mipLevels; ++level )
{
hr = gpubc->Prepare( w, h, format, alphaWeight, !(compress & TEX_COMPRESS_BC7_USE_3SUBSETS) );
if ( FAILED(hr) )
{
cImages.Release();
return hr;
}
for( size_t item = 0; item < metadata.arraySize; ++item )
{
size_t index = metadata.ComputeIndex( level, item, 0 );
if ( index >= nimages )
{
cImages.Release();
return E_FAIL;
}
assert( dest[ index ].format == format );
const Image& src = srcImages[ index ];
if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
{
cImages.Release();
return E_FAIL;
}
hr = _GPUCompress( gpubc.get(), src, dest[ index ], compress );
if ( FAILED(hr) )
{
cImages.Release();
return hr;
}
}
if ( h > 1 )
h >>= 1;
if ( w > 1 )
w >>= 1;
}
}
break;
case TEX_DIMENSION_TEXTURE3D:
{
size_t w = metadata.width;
size_t h = metadata.height;
size_t d = metadata.depth;
for( size_t level=0; level < metadata.mipLevels; ++level )
{
hr = gpubc->Prepare( w, h, format, alphaWeight, !(compress & TEX_COMPRESS_BC7_USE_3SUBSETS) );
if ( FAILED(hr) )
{
cImages.Release();
return hr;
}
for( size_t slice=0; slice < d; ++slice )
{
size_t index = metadata.ComputeIndex( level, 0, slice );
if ( index >= nimages )
{
cImages.Release();
return E_FAIL;
}
assert( dest[ index ].format == format );
const Image& src = srcImages[ index ];
if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
{
cImages.Release();
return E_FAIL;
}
hr = _GPUCompress( gpubc.get(), src, dest[ index ], compress );
if ( FAILED(hr) )
{
cImages.Release();
return hr;
}
}
if ( h > 1 )
h >>= 1;
if ( w > 1 )
w >>= 1;
if ( d > 1 )
d >>= 1;
}
}
break;
default:
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
}
return S_OK;
}
}; // namespace