2016-03-21 20 views
8

Ho cercato di caricare immagini compresse con compressione S3TC (BC/DXT) in Vulkan, ma finora non ho avuto molta fortuna.Impossibile creare l'immagine da dati di texture compresse (S3TC)

Ecco cosa dice la specifica Vulkan su immagini compresse:

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

immagini texture compressi memorizzati utilizzando i formati di immagine S3TC compressi sono rappresentati come un insieme di blocchi di 4 × 4 Texel, dove ogni blocco contiene 64 o 128 bit di dati texel. L'immagine è codificata come una normale immagine raster 2D in cui ogni blocco 4 × 4 viene trattato come un singolo pixel.

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

Per le immagini create con piastrelle lineare, rowPitch, arrayPitch e depthPitch descrivono il layout del subresource in memoria lineare. Per i formati non compressi, rowPitch è il numero di byte tra i texel con la stessa coordinata x nelle righe adiacenti (le coordinate y differiscono di uno). arrayPitch è il numero di byte tra i texel con la stessa coordinata x e y nei livelli dell'array adiacenti dell'immagine (i valori del livello dell'array differiscono di uno). depthPitch è il numero di byte tra i texel con la stessa coordinata x e y nelle sezioni adiacenti di un'immagine 3D (le coordinate z differiscono di uno). Espressa come formula di indirizzamento, il byte iniziale di un texel nel subresource ha indirizzo:

// (x, y, z, strato) sono in texel coordinate

indirizzo (x, y, z , strato) = strato arrayPitch + z depthPitch + y + x rowPitch texelSize + compensato

Per i formati compressi, il rowPitch è il numero di byte tra blocchi compressi in file adiacenti. arrayPitch è il numero di byte tra i blocchi nei livelli dell'array adiacenti. depthPitch è il numero di byte tra i blocchi nelle sezioni adiacenti di un'immagine 3D.

// (x, y, z, strato) sono in blocco coordinate

indirizzo (x, y, z, strato) = strato arrayPitch + z depthPitch + y + x rowPitch blockSize + offset;

arrayPitch non è definito per le immagini che non sono state create come matrici. depthPitch è definito solo per immagini 3D.

Per i formati colore, il membro aspectMask di VkImageSubresource deve essere VK_IMAGE_ASPECT_COLOR_BIT. Per i formati di profondità/stencil, l'aspetto deve essere VK_IMAGE_ASPECT_DEPTH_BIT o VK_IMAGE_ASPECT_STENCIL_BIT. Nelle implementazioni che memorizzano separatamente gli aspetti di profondità e stencil, l'interrogazione di ciascuno di questi layout di sottobresce restituirà un offset e una dimensione diversi che rappresentano l'area di memoria utilizzata per quell'aspetto. Nelle implementazioni che memorizzano gli aspetti di profondità e stencil intercalati, vengono restituiti lo stesso offset e le dimensioni e rappresentano l'allocazione di memoria interlacciata.

La mia immagine è un'immagine 2D normale (0 strati, 1 mipmap), quindi non c'è arrayPitch o depthPitch. Poiché la compressione S3TC è direttamente supportata dall'hardware, dovrebbe essere possibile utilizzare i dati dell'immagine senza prima decomprimerla. In OpenGL questo può essere fatto usando glCompressedTexImage2D, e questo ha funzionato per me in passato.

In OpenGL ho usato GL_COMPRESSED_RGBA_S3TC_DXT1_EXT come formato immagine, per Vulkan sto usando VK_FORMAT_BC1_RGBA_UNORM_BLOCK, che dovrebbe essere equivalente. Ecco il mio codice per mappare i dati di immagine:

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 
    } 
} 

Ed ecco il codice per l'inizializzazione l'immagine:

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); 

Il codice viene eseguito senza problemi, tuttavia l'immagine risultante non è corretto. Questa è l'immagine di origine:

Black/Pink checkerboard pattern

E questo è il risultato:

Green/Black checkerboard pattern, much smaller than source image

Sono certo che il problema risiede nel primo codice snipped che ho postato, tuttavia, in caso contrario, ho scritto un piccolo adattamento del demo del triangolo dal Vulkan SDK che produce lo stesso risultato. Può essere scaricato here. Il codice sorgente è incluso, tutto ciò che ho modificato dalla demo del triangolo è la funzione "demo_prepare_texture_image" in tri.c (righe da 803 a 903) e i file "dds.cpp" e "dds.h". "dds.cpp" contiene il codice per caricare il dds e mappare la memoria dell'immagine.

Sto usando gli per caricare i dati di dds (che dovrebbe funzionare perfettamente con Vulkan), che è anche incluso nel download sopra. Per costruire il progetto, l'SDK Vulkan include la directory che deve essere aggiunta al progetto "tri" e il percorso del file system deve essere modificato (tri.c, Linea 809).

L'immagine di origine ("x64/Debug/test.dds" nel progetto) utilizza la compressione DXT1. Ho provato anche su hardware diverso, con lo stesso risultato.

Qualsiasi codice di esempio per inizializzare/mappare immagini compresse sarebbe di grande aiuto.

risposta

2

Il tuo problema è in realtà abbastanza semplice: nella funzione demo_prepare_textures, la prima riga, c'è una variabile tex_format, che è impostata su VK_FORMAT_B8G8R8A8_UNORM (che è ciò che è nell'esempio originale). Questo alla fine si abitua a creare VkImageView. Se si modifica questo valore in VK_FORMAT_BC1_RGBA_UNORM_BLOCK, viene visualizzata correttamente la texture sul triangolo.

Come parte - è possibile verificare che la trama sia stata caricata correttamente, con RenderDoc, fornito con l'installazione di Vulkan SDK. Effettuando una cattura, e guardando nella scheda TextureViewer, la scheda Inputs mostra che la tua texture sembra identica a quella sul disco, anche con il formato errato.

fixed image

+0

Se cambio il formato, l'immagine risultante è ancora più incasinato (http: // sciolyte.com/sharex/2016-03-25_15-35-40.gif). RenderDoc si blocca immediatamente dopo aver tentato di eseguire qualsiasi programma attraverso di esso. La demo ha funzionato davvero per te con quel cambiamento? Hai cambiato qualcos'altro? – Silverlan

+0

Se funziona per te, potresti fornire il tuo codice con le modifiche? Non l'ho ancora fatto funzionare dalla mia parte. – Silverlan

+1

Oltre al percorso di caricamento del DDS e alcune impostazioni del progetto per utilizzare il mio SDK Vulkan 1.0.5 installato e la toolchain VS 120, non ho modificato nient'altro dal download tri_dds.zip. Forse un problema di driver? Forse allega il tuo vulkaninfo? – MuertoExcobito

Problemi correlati