diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index 77d8c71..7c621fa 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -664,12 +664,26 @@ namespace DirectX constexpr float TEX_THRESHOLD_DEFAULT = 0.5f; // Default value for alpha threshold used when converting to 1-bit alpha + struct ConvertOptions + { + TEX_FILTER_FLAGS filter; + float threshold; + }; + HRESULT __cdecl Convert( _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold, _Out_ ScratchImage& image) noexcept; HRESULT __cdecl Convert( _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DXGI_FORMAT format, _In_ TEX_FILTER_FLAGS filter, _In_ float threshold, _Out_ ScratchImage& result) noexcept; + + HRESULT __cdecl ConvertEx( + _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const ConvertOptions& options, + _Out_ ScratchImage& image, _In_opt_ std::function statusCallBack = nullptr); + HRESULT __cdecl ConvertEx( + _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _In_ const ConvertOptions& options, _Out_ ScratchImage& result, + _In_opt_ std::function statusCallBack = nullptr); // Convert the image to a new format HRESULT __cdecl ConvertToSinglePlane(_In_ const Image& srcImage, _Out_ ScratchImage& image) noexcept; @@ -756,6 +770,16 @@ namespace DirectX // Compress is free to use multithreading to improve performance (by default it does not use multithreading) }; + constexpr float TEX_ALPHA_WEIGHT_DEFAULT = 1.0f; + // Default value for alpha weight used for GPU BC7 compression + + struct CompressOptions + { + TEX_COMPRESS_FLAGS flags; + float threshold; + float alphaWeight; + }; + HRESULT __cdecl Compress( _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold, _Out_ ScratchImage& cImage) noexcept; @@ -764,6 +788,14 @@ namespace DirectX _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float threshold, _Out_ ScratchImage& cImages) noexcept; // Note that threshold is only used by BC1. TEX_THRESHOLD_DEFAULT is a typical value to use + HRESULT __cdecl CompressEx( + _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const CompressOptions& options, + _Out_ ScratchImage& cImage, _In_opt_ std::function statusCallBack = nullptr); + HRESULT __cdecl CompressEx( + _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _In_ const CompressOptions& options, _Out_ ScratchImage& cImages, + _In_opt_ std::function statusCallBack = nullptr); + #if defined(__d3d11_h__) || defined(__d3d11_x_h__) HRESULT __cdecl Compress( _In_ ID3D11Device* pDevice, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, @@ -772,6 +804,14 @@ namespace DirectX _In_ ID3D11Device* pDevice, _In_ const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _In_ DXGI_FORMAT format, _In_ TEX_COMPRESS_FLAGS compress, _In_ float alphaWeight, _Out_ ScratchImage& cImages) noexcept; // DirectCompute-based compression (alphaWeight is only used by BC7. 1.0 is the typical value to use) + + HRESULT __cdecl CompressEx( + _In_ ID3D11Device* pDevice, _In_ const Image& srcImage, _In_ DXGI_FORMAT format, _In_ const CompressOptions& options, + _Out_ ScratchImage& image, _In_opt_ std::function statusCallBack = nullptr); + HRESULT __cdecl CompressEx( + _In_ ID3D11Device* pDevice, _In_ const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, + _In_ DXGI_FORMAT format, _In_ const CompressOptions& options, _Out_ ScratchImage& cImages, + _In_opt_ std::function statusCallBack = nullptr); #endif HRESULT __cdecl Decompress(_In_ const Image& cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage& image) noexcept; diff --git a/DirectXTex/DirectXTexCompress.cpp b/DirectXTex/DirectXTexCompress.cpp index 99385a9..e2c17c5 100644 --- a/DirectXTex/DirectXTexCompress.cpp +++ b/DirectXTex/DirectXTexCompress.cpp @@ -74,7 +74,8 @@ namespace const Image& result, uint32_t bcflags, TEX_FILTER_FLAGS srgb, - float threshold) noexcept + float threshold, + const std::function& statusCallback) noexcept { if (!image.pixels || !result.pixels) return E_POINTER; @@ -111,6 +112,14 @@ namespace const size_t rowPitch = image.rowPitch; for (size_t h = 0; h < image.height; h += 4) { + if (statusCallback) + { + if (!statusCallback(h, image.height)) + { + return E_ABORT; + } + } + const uint8_t *sptr = pSrc; uint8_t* dptr = pDest; const size_t ph = std::min(4, image.height - h); @@ -203,7 +212,8 @@ namespace const Image& result, uint32_t bcflags, TEX_FILTER_FLAGS srgb, - float threshold) noexcept + float threshold, + const std::function& statusCallback) noexcept { if (!image.pixels || !result.pixels) return E_POINTER; @@ -239,9 +249,22 @@ namespace bool fail = false; - #pragma omp parallel for + size_t progress = 0; + bool abort = false; + + const size_t progressTotal = std::max(1, (image.height + 3) / 4); + +#pragma omp parallel for shared(progress) for (int nb = 0; nb < static_cast(nBlocks); ++nb) { +#pragma omp flush (abort) + if (abort) + { + // Short circuit the loop body if an abort is requested. + // OpenMP 2.0 does not support cancellation of a 'parallel for' loop. + continue; + } + const int nbWidth = std::max(1, int((image.width + 3) / 4)); int y = nb / nbWidth; @@ -323,9 +346,29 @@ namespace pfEncode(pDest, temp, bcflags); else D3DXEncodeBC1(pDest, temp, threshold, bcflags); + + // Report progress when a new row is reached. + if (x == 0 && statusCallback) + { +#pragma omp atomic + progress += 4; + + if (!statusCallback(progress, progressTotal)) + { + abort = true; +#pragma omp flush (abort) + } + } } - return (fail) ? E_FAIL : S_OK; + if (abort) + { + return E_ABORT; + } + else + { + return (fail) ? E_FAIL : S_OK; + } } #endif // _OPENMP @@ -592,6 +635,38 @@ HRESULT DirectX::Compress( TEX_COMPRESS_FLAGS compress, float threshold, ScratchImage& image) noexcept +{ + CompressOptions options = {}; + options.flags = compress; + options.threshold = threshold; + + return CompressEx(srcImage, format, options, image, nullptr); +} + +_Use_decl_annotations_ +HRESULT DirectX::Compress( + const Image* srcImages, + size_t nimages, + const TexMetadata& metadata, + DXGI_FORMAT format, + TEX_COMPRESS_FLAGS compress, + float threshold, + ScratchImage& cImages) noexcept +{ + CompressOptions options = {}; + options.flags = compress; + options.threshold = threshold; + + return CompressEx(srcImages, nimages, metadata, format, options, cImages, nullptr); +} + +_Use_decl_annotations_ +HRESULT DirectX::CompressEx( + const Image& srcImage, + DXGI_FORMAT format, + const CompressOptions& options, + ScratchImage& image, + std::function statusCallback) { if (IsCompressed(srcImage.format) || !IsCompressed(format)) return E_INVALIDARG; @@ -612,35 +687,56 @@ HRESULT DirectX::Compress( return E_POINTER; } + if (statusCallback) + { + if (!statusCallback(0, img->height)) + { + image.Release(); + return E_ABORT; + } + } + // Compress single image - if (compress & TEX_COMPRESS_PARALLEL) + if (options.flags & TEX_COMPRESS_PARALLEL) { #ifndef _OPENMP - return E_NOTIMPL; + hr = E_NOTIMPL; #else - hr = CompressBC_Parallel(srcImage, *img, GetBCFlags(compress), GetSRGBFlags(compress), threshold); + hr = CompressBC_Parallel(srcImage, *img, GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, statusCallback); #endif // _OPENMP } else { - hr = CompressBC(srcImage, *img, GetBCFlags(compress), GetSRGBFlags(compress), threshold); + hr = CompressBC(srcImage, *img, GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, statusCallback); } if (FAILED(hr)) + { image.Release(); + return hr; + } - return hr; + if (statusCallback) + { + if (!statusCallback(img->height, img->height)) + { + image.Release(); + return E_ABORT; + } + } + + return S_OK; } _Use_decl_annotations_ -HRESULT DirectX::Compress( +HRESULT DirectX::CompressEx( const Image* srcImages, size_t nimages, const TexMetadata& metadata, DXGI_FORMAT format, - TEX_COMPRESS_FLAGS compress, - float threshold, - ScratchImage& cImages) noexcept + const CompressOptions& options, + ScratchImage& cImages, + std::function statusCallback) { if (!srcImages || !nimages) return E_INVALIDARG; @@ -654,6 +750,19 @@ HRESULT DirectX::Compress( cImages.Release(); + if (statusCallback + && nimages == 1 + && !metadata.IsVolumemap() + && metadata.mipLevels == 1 + && metadata.arraySize == 1) + { + // If progress reporting is requested when compressing a single 1D or 2D image, call + // the CompressEx overload that takes a single image. + // This provides a better user experience as progress will be reported as the image + // is being processed, instead of after processing has been completed. + return CompressEx(srcImages[0], format, options, cImages, statusCallback); + } + TexMetadata mdata2 = metadata; mdata2.format = format; HRESULT hr = cImages.Initialize(mdata2); @@ -673,6 +782,15 @@ HRESULT DirectX::Compress( return E_POINTER; } + if (statusCallback) + { + if (!statusCallback(0, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } + for (size_t index = 0; index < nimages; ++index) { assert(dest[index].format == format); @@ -685,33 +803,44 @@ HRESULT DirectX::Compress( return E_FAIL; } - if ((compress & TEX_COMPRESS_PARALLEL)) + if (options.flags & TEX_COMPRESS_PARALLEL) { #ifndef _OPENMP - return E_NOTIMPL; + hr = E_NOTIMPL; #else - if (compress & TEX_COMPRESS_PARALLEL) - { - hr = CompressBC_Parallel(src, dest[index], GetBCFlags(compress), GetSRGBFlags(compress), threshold); - if (FAILED(hr)) - { - cImages.Release(); - return hr; - } - } + hr = CompressBC_Parallel(src, dest[index], GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, nullptr); #endif // _OPENMP } else { - hr = CompressBC(src, dest[index], GetBCFlags(compress), GetSRGBFlags(compress), threshold); - if (FAILED(hr)) + hr = CompressBC(src, dest[index], GetBCFlags(options.flags), GetSRGBFlags(options.flags), options.threshold, nullptr); + } + + if (FAILED(hr)) + { + cImages.Release(); + return hr; + } + + if (statusCallback) + { + if (!statusCallback(index, nimages)) { cImages.Release(); - return hr; + return E_ABORT; } } } + if (statusCallback) + { + if (!statusCallback(nimages, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } + return S_OK; } diff --git a/DirectXTex/DirectXTexCompressGPU.cpp b/DirectXTex/DirectXTexCompressGPU.cpp index 019aeda..6919556 100644 --- a/DirectXTex/DirectXTexCompressGPU.cpp +++ b/DirectXTex/DirectXTexCompressGPU.cpp @@ -219,6 +219,40 @@ HRESULT DirectX::Compress( TEX_COMPRESS_FLAGS compress, float alphaWeight, ScratchImage& image) noexcept +{ + CompressOptions options = {}; + options.flags = compress; + options.alphaWeight = alphaWeight; + + return CompressEx(pDevice, srcImage, format, options, image, nullptr); +} + +_Use_decl_annotations_ +HRESULT DirectX::Compress( + ID3D11Device* pDevice, + const Image* srcImages, + size_t nimages, + const TexMetadata& metadata, + DXGI_FORMAT format, + TEX_COMPRESS_FLAGS compress, + float alphaWeight, + ScratchImage& cImages) noexcept +{ + CompressOptions options = {}; + options.flags = compress; + options.alphaWeight = alphaWeight; + + return CompressEx(pDevice, srcImages, nimages, metadata, format, options, cImages); +} + +_Use_decl_annotations_ +HRESULT DirectX::CompressEx( + ID3D11Device* pDevice, + const Image& srcImage, + DXGI_FORMAT format, + const CompressOptions& options, + ScratchImage& image, + std::function statusCallback) { if (!pDevice || IsCompressed(srcImage.format) || !IsCompressed(format)) return E_INVALIDARG; @@ -236,7 +270,7 @@ HRESULT DirectX::Compress( if (FAILED(hr)) return hr; - hr = gpubc->Prepare(srcImage.width, srcImage.height, compress, format, alphaWeight); + hr = gpubc->Prepare(srcImage.width, srcImage.height, options.flags, format, options.alphaWeight); if (FAILED(hr)) return hr; @@ -252,23 +286,45 @@ HRESULT DirectX::Compress( return E_POINTER; } - hr = GPUCompress(gpubc.get(), srcImage, *img, compress); - if (FAILED(hr)) - image.Release(); + if (statusCallback) + { + if (!statusCallback(0, 100)) + { + image.Release(); + return E_ABORT; + } + } - return hr; + hr = GPUCompress(gpubc.get(), srcImage, *img, options.flags); + + if (FAILED(hr)) + { + image.Release(); + return hr; + } + + if (statusCallback) + { + if (!statusCallback(100, 100)) + { + image.Release(); + return E_ABORT; + } + } + + return S_OK; } _Use_decl_annotations_ -HRESULT DirectX::Compress( +HRESULT DirectX::CompressEx( ID3D11Device* pDevice, const Image* srcImages, size_t nimages, const TexMetadata& metadata, DXGI_FORMAT format, - TEX_COMPRESS_FLAGS compress, - float alphaWeight, - ScratchImage& cImages) noexcept + const CompressOptions& options, + ScratchImage& cImages, + std::function statusCallback) { if (!pDevice || !srcImages || !nimages) return E_INVALIDARG; @@ -311,6 +367,15 @@ HRESULT DirectX::Compress( return E_POINTER; } + if (statusCallback) + { + if (!statusCallback(0, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } + // Process images (ordered by size) switch (metadata.dimension) { @@ -319,10 +384,11 @@ HRESULT DirectX::Compress( { size_t w = metadata.width; size_t h = metadata.height; + size_t progress = 0; for (size_t level = 0; level < metadata.mipLevels; ++level) { - hr = gpubc->Prepare(w, h, compress, format, alphaWeight); + hr = gpubc->Prepare(w, h, options.flags, format, options.alphaWeight); if (FAILED(hr)) { cImages.Release(); @@ -348,12 +414,21 @@ HRESULT DirectX::Compress( return E_FAIL; } - hr = GPUCompress(gpubc.get(), src, dest[index], compress); + hr = GPUCompress(gpubc.get(), src, dest[index], options.flags); if (FAILED(hr)) { cImages.Release(); return hr; } + + if (statusCallback) + { + if (!statusCallback(progress++, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } } if (h > 1) @@ -370,10 +445,11 @@ HRESULT DirectX::Compress( size_t w = metadata.width; size_t h = metadata.height; size_t d = metadata.depth; + size_t progress = 0; for (size_t level = 0; level < metadata.mipLevels; ++level) { - hr = gpubc->Prepare(w, h, compress, format, alphaWeight); + hr = gpubc->Prepare(w, h, options.flags, format, options.alphaWeight); if (FAILED(hr)) { cImages.Release(); @@ -399,12 +475,21 @@ HRESULT DirectX::Compress( return E_FAIL; } - hr = GPUCompress(gpubc.get(), src, dest[index], compress); + hr = GPUCompress(gpubc.get(), src, dest[index], options.flags); if (FAILED(hr)) { cImages.Release(); return hr; } + + if (statusCallback) + { + if (!statusCallback(progress++, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } } if (h > 1) @@ -423,5 +508,14 @@ HRESULT DirectX::Compress( return HRESULT_E_NOT_SUPPORTED; } + if (statusCallback) + { + if (!statusCallback(nimages, nimages)) + { + cImages.Release(); + return E_ABORT; + } + } + return S_OK; } diff --git a/DirectXTex/DirectXTexConvert.cpp b/DirectXTex/DirectXTexConvert.cpp index c534a4c..30f24b7 100644 --- a/DirectXTex/DirectXTexConvert.cpp +++ b/DirectXTex/DirectXTexConvert.cpp @@ -4798,7 +4798,8 @@ namespace _In_ TEX_FILTER_FLAGS filter, _In_ const Image& destImage, _In_ float threshold, - size_t z) noexcept + size_t z, + const std::function& statusCallback) noexcept { assert(srcImage.width == destImage.width); assert(srcImage.height == destImage.height); @@ -4822,6 +4823,14 @@ namespace for (size_t h = 0; h < srcImage.height; ++h) { + if (statusCallback) + { + if (!statusCallback(h, srcImage.height)) + { + return E_ABORT; + } + } + if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) return E_FAIL; @@ -4845,6 +4854,14 @@ namespace // Ordered dithering for (size_t h = 0; h < srcImage.height; ++h) { + if (statusCallback) + { + if (!statusCallback(h, srcImage.height)) + { + return E_ABORT; + } + } + if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) return E_FAIL; @@ -4862,6 +4879,14 @@ namespace // No dithering for (size_t h = 0; h < srcImage.height; ++h) { + if (statusCallback) + { + if (!statusCallback(h, srcImage.height)) + { + return E_ABORT; + } + } + if (!LoadScanline(scanline.get(), width, pSrc, srcImage.rowPitch, srcImage.format)) return E_FAIL; @@ -5061,6 +5086,21 @@ HRESULT DirectX::Convert( TEX_FILTER_FLAGS filter, float threshold, ScratchImage& image) noexcept +{ + ConvertOptions options = {}; + options.filter = filter; + options.threshold = threshold; + + return ConvertEx(srcImage, format, options, image, nullptr); +} + +_Use_decl_annotations_ +HRESULT DirectX::ConvertEx( + const Image& srcImage, + DXGI_FORMAT format, + const ConvertOptions& options, + ScratchImage& image, + std::function statusCallback) { if ((srcImage.format == format) || !IsValid(format)) return E_INVALIDARG; @@ -5088,14 +5128,23 @@ HRESULT DirectX::Convert( return E_POINTER; } - WICPixelFormatGUID pfGUID, targetGUID; - if (UseWICConversion(filter, srcImage.format, format, pfGUID, targetGUID)) + if (statusCallback) { - hr = ConvertUsingWIC(srcImage, pfGUID, targetGUID, filter, threshold, *rimage); + if (!statusCallback(0, rimage->height)) + { + image.Release(); + return E_ABORT; + } + } + + WICPixelFormatGUID pfGUID, targetGUID; + if (UseWICConversion(options.filter, srcImage.format, format, pfGUID, targetGUID)) + { + hr = ConvertUsingWIC(srcImage, pfGUID, targetGUID, options.filter, options.threshold, *rimage); } else { - hr = ConvertCustom(srcImage, filter, *rimage, threshold, 0); + hr = ConvertCustom(srcImage, options.filter, *rimage, options.threshold, 0, statusCallback); } if (FAILED(hr)) @@ -5104,6 +5153,15 @@ HRESULT DirectX::Convert( return hr; } + if (statusCallback) + { + if (!statusCallback(rimage->height, rimage->height)) + { + image.Release(); + return E_ABORT; + } + } + return S_OK; } @@ -5120,6 +5178,23 @@ HRESULT DirectX::Convert( TEX_FILTER_FLAGS filter, float threshold, ScratchImage& result) noexcept +{ + ConvertOptions options = {}; + options.filter = filter; + options.threshold = threshold; + + return ConvertEx(srcImages, nimages, metadata, format, options, result, nullptr); +} + +_Use_decl_annotations_ +HRESULT DirectX::ConvertEx( + const Image* srcImages, + size_t nimages, + const TexMetadata& metadata, + DXGI_FORMAT format, + const ConvertOptions& options, + ScratchImage& result, + std::function statusCallback) { if (!srcImages || !nimages || (metadata.format == format) || !IsValid(format)) return E_INVALIDARG; @@ -5133,6 +5208,19 @@ HRESULT DirectX::Convert( if ((metadata.width > UINT32_MAX) || (metadata.height > UINT32_MAX)) return E_INVALIDARG; + if (statusCallback + && nimages == 1 + && !metadata.IsVolumemap() + && metadata.mipLevels == 1 + && metadata.arraySize == 1) + { + // If progress reporting is requested when converting a single 1D or 2D image, call + // the ConvertEx overload that takes a single image. + // This provides a better user experience as progress will be reported as the image + // is being processed, instead of after processing has been completed. + return ConvertEx(srcImages[0], format, options, result, statusCallback); + } + TexMetadata mdata2 = metadata; mdata2.format = format; HRESULT hr = result.Initialize(mdata2); @@ -5152,8 +5240,17 @@ HRESULT DirectX::Convert( return E_POINTER; } + if (statusCallback) + { + if (!statusCallback(0, nimages)) + { + result.Release(); + return E_ABORT; + } + } + WICPixelFormatGUID pfGUID, targetGUID; - const bool usewic = !metadata.IsPMAlpha() && UseWICConversion(filter, metadata.format, format, pfGUID, targetGUID); + const bool usewic = !metadata.IsPMAlpha() && UseWICConversion(options.filter, metadata.format, format, pfGUID, targetGUID); switch (metadata.dimension) { @@ -5185,11 +5282,11 @@ HRESULT DirectX::Convert( if (usewic) { - hr = ConvertUsingWIC(src, pfGUID, targetGUID, filter, threshold, dst); + hr = ConvertUsingWIC(src, pfGUID, targetGUID, options.filter, options.threshold, dst); } else { - hr = ConvertCustom(src, filter, dst, threshold, 0); + hr = ConvertCustom(src, options.filter, dst, options.threshold, 0, nullptr); } if (FAILED(hr)) @@ -5197,6 +5294,15 @@ HRESULT DirectX::Convert( result.Release(); return hr; } + + if (statusCallback) + { + if (!statusCallback(index, nimages)) + { + result.Release(); + return E_ABORT; + } + } } break; @@ -5238,11 +5344,11 @@ HRESULT DirectX::Convert( if (usewic) { - hr = ConvertUsingWIC(src, pfGUID, targetGUID, filter, threshold, dst); + hr = ConvertUsingWIC(src, pfGUID, targetGUID, options.filter, options.threshold, dst); } else { - hr = ConvertCustom(src, filter, dst, threshold, slice); + hr = ConvertCustom(src, options.filter, dst, options.threshold, slice, nullptr); } if (FAILED(hr)) @@ -5250,6 +5356,15 @@ HRESULT DirectX::Convert( result.Release(); return hr; } + + if (statusCallback) + { + if (!statusCallback(index, nimages)) + { + result.Release(); + return E_ABORT; + } + } } if (d > 1) @@ -5263,6 +5378,15 @@ HRESULT DirectX::Convert( return E_FAIL; } + if (statusCallback) + { + if (!statusCallback(nimages, nimages)) + { + result.Release(); + return E_ABORT; + } + } + return S_OK; }