#define QUALITY 2‘li bir shader gömülüdür — sabiti değiştirin
ve SPIR-V’yi yeniden derlersiniz. Bir UBO’dan quality okuyan
shader esnektir ama fragment başına bir load + branch öder.
Specialization sabitleri orta yoldur: değer pipeline
creation zamanında enjekte edilir, sürücünün shader derleyicisi
onu literal sabit olarak görür — ölü dallar katlanır, döngüler
açılır — ama SPIR-V’ye hiç dokunmadınız.
Mekanizma
// GLSL tarafı — varsayılanlı isimli sabit:
layout(constant_id = 0) const int QUALITY = 2;
layout(constant_id = 1) const bool USE_FILTER = true;
void main() {
if (USE_FILTER) { /* … */ } // USE_FILTER=false iken katlanıp gider
for (int i = 0; i < QUALITY; ++i) { /* … */ } // açılır
}// Host tarafı — pipeline create'te varsayılanları ez:
struct { int32_t quality; uint32_t useFilter; } spec{4, 1};
vk::SpecializationMapEntry entries[2];
entries[0].setConstantID(0).setOffset(offsetof(decltype(spec), quality))
.setSize(sizeof(spec.quality));
entries[1].setConstantID(1).setOffset(offsetof(decltype(spec), useFilter))
.setSize(sizeof(spec.useFilter));
vk::SpecializationInfo specInfo{};
specInfo.setMapEntries(entries).setDataSize(sizeof(spec)).setPData(&spec);
vk::PipelineShaderStageCreateInfo stage{};
stage.setPModule(shaderModule).setPName("main").setPSpecializationInfo(&specInfo);
// … pipelineCreateInfo.setStages(stage) …
Sürücünün JIT’i, pipeline creation’da SPIR-V’yi verilen değerler
katlanmış olarak derler — QUALITY=4 ISA’da bir literal olur.
Neden sadece UBO değil
| Yaklaşım | Shader’ın gördüğü | Maliyet | Yeniden derleme? |
|---|---|---|---|
#define | derleme-zamanı literal | bedava | evet — yeni SPIR-V |
| Spec sabiti | pipeline build’de literal | bedava | hayır — sadece yeni VkPipeline |
| UBO / push sabiti | bellekten load | kullanım başına load | hayır |
Kazanç: dal katlama ve döngü açma. USE_FILTER‘ın bir UBO
değeri olduğu if (USE_FILTER)‘lı bir shader her iki tarafın
register tahsisini değerlendirmek zorundadır; spec sabitiyle
ölü dal tamamen kaybolur — daha az register, daha iyi occupancy.
Parladığı yer
- Kalite merdivenleri:
QUALITY=1..4bir örnekleme döngüsünü açar — tek SPIR-V’den düşük/orta/yüksek pipeline’lar. - Özellik anahtarları:
HAS_NORMALMAP,USE_SRGB— tek shader kaynağı, sürücü her varyantı özelleştirir. - Workgroup boyutları:
layout(local_size_x_id = 0)— GLSL’i düzenlemeden aynı compute kernel’ini 64 vs 256 thread için özelleştirin. - Codec/format varyantları: bir YUV→RGB dönüşüm shader’ı 420 vs 422 vs 444 alt örnekleme için özelleşir — stride matematiği immediate’lere katlanır.
p67 bağlantısı
GPU PNG encoder’ı W, H ve bytesPerRow için spec sabitleri
kullanır — aynı shader her image boyutu için sabit-boyutlu bir
filter kernel’ine derlenir. Dispatch sayısı değerlere bağlıdır,
bu yüzden CPU onları geri okur ve dispatch’ten önce ikinci bir
sabit seti iter.
Example: VulkanExamples/p67_pngenc
Pratik kurallar
constant_iddeğerleri aşama-başına isim alanındadır — vertex’teki id 0 ile fragment’teki bağımsızdır.- Bool’lar spec verisinde
uint32_t‘dir —VkBool32genişliği 1 değil. - Spec sabitleri draw başına değil pipeline creation’da
özelleşir — değeri değiştirmek yeni bir
VkPipelineyaratmak demektir (cache’leyen sürücülerde ucuz; pipeline cache yine tekilleştirir). - Varsayılan değerler (GLSL’deki
= 2) uygulama ezmezse uygulanır — “sağlıklı fallback” için kullanışlı. - Tüm optimizasyonlar hayatta kalmaz: bir sürücü nadiren-kullanılan bir spec-const dalını katlamayı atlayabilir — ama literali her zaman alır.