9

Vulkan で S3TC (BC/DXT) 圧縮を使用して圧縮画像を読み込もうとしてきましたが、これまでのところうまくいきませんでした。

圧縮された画像に関する Vulkan 仕様の内容は次のとおりです。

https://www.khronos.org/registry/dataformat/specs/1.1/dataformat.1.1.html#S3TC :

S3TC 圧縮画像フォーマットを使用して格納された圧縮テクスチャ画像は、4×4 テクセル ブロックのコレクションとして表されます。各ブロックには、64 ビットまたは 128 ビットのテクセル データが含まれます。画像は、各 4×4 ブロックが 1 つのピクセルとして扱われる通常の 2D ラスター画像としてエンコードされます。

https://www.khronos.org/registry/vulkan/specs/1.0/xhtml/vkspec.html#resources-images :

線形タイリングで作成された画像の場合、rowPitch、arrayPitch、および depthPitch は、線形メモリ内のサブリソースのレイアウトを記述します。非圧縮形式の場合、rowPitch は、隣接する行で同じ x 座標を持つテクセル間のバイト数です (y 座標は 1 異なります)。arrayPitch は、イメージの隣接する配列レイヤーで同じ x 座標と y 座標を持つテクセル間のバイト数です (配列レイヤーの値は 1 異なります)。depthPitch は、3D イメージの隣接するスライスで同じ x 座標と y 座標を持つテクセル間のバイト数です (z 座標は 1 異なります)。サブリソース内のテクセルの開始バイトには、アドレス指定式として表現された次のアドレスがあります。

// (x,y,z,layer) はテクセル座標です

address(x,y,z,layer) = 層配列ピッチ + z深さピッチ + y行ピッチ + x texelSize + オフセット

圧縮形式の場合、rowPitch は隣接する行の圧縮ブロック間のバイト数です。arrayPitch は、隣接する配列層のブロック間のバイト数です。depthPitch は、3D イメージの隣接するスライスのブロック間のバイト数です。

// (x,y,z,layer) はブロック座標です

address(x,y,z,layer) = レイヤーarrayPitch + z depthPitch + y rowPitch + x blockSize + オフセット;

arrayPitch は、配列として作成されていない画像では未定義です。depthPitch は 3D 画像に対してのみ定義されます。

カラー形式の場合、VkImageSubresource のアスペクトマスク メンバーは VK_IMAGE_ASPECT_COLOR_BIT である必要があります。深度/ステンシル形式の場合、アスペクトは VK_IMAGE_ASPECT_DEPTH_BIT または VK_IMAGE_ASPECT_STENCIL_BIT のいずれかでなければなりません。深度とステンシルのアスペクトを別々に格納する実装では、これらのサブリソース レイアウトのそれぞれをクエリすると、そのアスペクトに使用されるメモリの領域を表す異なるオフセットとサイズが返されます。深度とステンシルのアスペクトをインターリーブして格納する実装では、同じオフセットとサイズが返され、インターリーブされたメモリ割り当てを表します。

私の画像は通常の 2D 画像 (0 レイヤー、1 ミップマップ) であるため、arrayPitchまたはdepthPitchはありません。S3TC 圧縮はハードウェアで直接サポートされているため、最初に解凍せずに画像データを使用できるはずです。OpenGL では、これはglCompressedTexImage2Dを使用して実行できます。これは過去に私にとってはうまくいきました。

OpenGL では、画像フォーマットとして GL_COMPRESSED_RGBA_S3TC_DXT1_EXT を使用しました。Vulkan では、同等の VK_FORMAT_BC1_RGBA_UNORM_BLOCK を使用しています。画像データをマッピングするための私のコードは次のとおりです。

auto dds = load_dds("img.dds");
auto *srcData = static_cast<uint8_t*>(dds.data());
auto *destData = static_cast<uint8_t*>(vkImageMapPtr); // Pointer to mapped memory of VkImage
destData += layout.offset(); // layout = VkImageLayout of the image
assert((w %4) == 0);
assert((h %4) == 0);
assert(blockSize == 8); // S3TC BC1
auto wBlocks = w /4;
auto hBlocks = h /4;
for(auto y=decltype(hBlocks){0};y<hBlocks;++y)
{
    auto *rowDest = destData +y *layout.rowPitch(); // rowPitch is 0
    auto *rowSrc = srcData +y *(wBlocks *blockSize);
    for(auto x=decltype(wBlocks){0};x<wBlocks;++x)
    {
        auto *pxDest = rowDest +x *blockSize;
        auto *pxSrc = rowSrc +x *blockSize; // 4x4 image block
        memcpy(pxDest,pxSrc,blockSize); // 64Bit per block
    }
}

イメージを初期化するコードは次のとおりです。

vk::Device device = ...; // Initialization
vk::AllocationCallbacks allocatorCallbacks = ...; // Initialization
[...] // Load the dds data
uint32_t width = dds.width();
uint32_t height = dds.height();
auto format = dds.format(); // = vk::Format::eBc1RgbaUnormBlock;

vk::Extent3D extent(width,height,1);

vk::ImageCreateInfo imageInfo(
    vk::ImageCreateFlagBits(0),
    vk::ImageType::e2D,format,
    extent,1,1,
    vk::SampleCountFlagBits::e1,
    vk::ImageTiling::eLinear,
    vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eColorAttachment,
    vk::SharingMode::eExclusive,
    0,nullptr,
    vk::ImageLayout::eUndefined
);

vk::Image img = nullptr;
device.createImage(&imageInfo,&allocatorCallbacks,&img);

vk::MemoryRequirements memRequirements;
device.getImageMemoryRequirements(img,&memRequirements);
uint32_t typeIndex = 0;
get_memory_type(memRequirements.memoryTypeBits(),vk::MemoryPropertyFlagBits::eHostVisible,typeIndex); // -> typeIndex is set to 1
auto szMem = memRequirements.size();
vk::MemoryAllocateInfo memAlloc(szMem,typeIndex);
vk::DeviceMemory mem;
device.allocateMemory(&memAlloc,&allocatorCallbacks,&mem); // Note: Using the default allocation (nullptr) doesn't change anything
device.bindImageMemory(img,mem,0);

uint32_t mipLevel = 0;
vk::ImageSubresource resource(
    vk::ImageAspectFlagBits::eColor,
    mipLevel,
    0
);
vk::SubresourceLayout layout;
device.getImageSubresourceLayout(img,&resource,&layout);

auto *srcData = device.mapMemory(mem,0,szMem,vk::MemoryMapFlagBits(0));
[...] // Map the dds-data (See code from first post)
device.unmapMemory(mem);

コードは問題なく実行されますが、結果の画像は正しくありません。これはソース画像です:

ブラック/ピンクの市松模様

そして、これは結果です:

緑/黒のチェッカーボード パターン、ソース イメージよりもはるかに小さい

問題は私が投稿した最初のコードにあると確信していますが、そうでない場合に備えて、Vulkan SDK の三角形のデモの小さな適応を作成しました。これにより、同じ結果が得られます。ここからダウンロードできます。ソースコードが含まれています。三角形のデモから変更したのは、tri.c の「demo_prepare_texture_image」関数( 803 行目から 903 行目) と、「dds.cpp」ファイルと「dds.h」ファイルだけです。「dds.cpp」には、dds をロードし、イメージ メモリをマッピングするためのコードが含まれています。

上記のダウンロードにも含まれているdds-data(「Vulkanで完全に動作する」はずです)をロードするためにgliを使用しています。プロジェクトをビルドするには、Vulkan SDK インクルード ディレクトリを「tri」プロジェクトに追加し、dds へのパスを変更する必要があります (tri.c、809 行目)。

ソース イメージ (プロジェクト内の「x64/Debug/test.dds」) は DXT1 圧縮を使用します。異なるハードウェアでもテストしましたが、結果は同じでした。

圧縮された画像を初期化/マッピングするためのサンプルコードも大いに役立ちます。

4

1 に答える 1

3

あなたの問題は実際には非常に単純です-demo_prepare_textures関数の最初の行には、にtex_format設定されている変数がありますVK_FORMAT_B8G8R8A8_UNORM(これは元のサンプルの内容です)。これは最終的に VkImageView の作成に使用されます。これを に変更するだけVK_FORMAT_BC1_RGBA_UNORM_BLOCKで、三角形にテクスチャが正しく表示されます。

余談ですがRenderDoc、Vulkan SDK のインストールに付属する を使用して、テクスチャが正しく読み込まれたことを確認できます。それをキャプチャしてタブを見ると、TextureViewerタブInputsは、フォーマットが正しくなくても、テクスチャがディスク上のものと同じに見えることを示しています。

固定画像

于 2016-03-25T11:10:30.193 に答える