403 lines
12 KiB
C++
403 lines
12 KiB
C++
//-------------------------------------------------------------------------------------
|
|
// 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
|