A ordem de alinhamento e declaração dos uniformes é importante?

10

Na seção 6.4 Buffers Constantes do livro Renderização e Computação Prática com Direct3D 11 (páginas 325, 326), é mencionado:

Por padrão, o compilador HLSL tentará alinhar constantes para que não abranjam vários registros float4. [...] A compactação para um buffer constante HLSL também pode ser especificada manualmente através da palavra-chave packoffset.

Suponho que uma regra semelhante se aplique ao equivalente ao OpenGL, Objetos de Buffer Uniformes, pois eles mapeiam para o mesmo recurso de hardware.

Mas e uniformes de baunilha? Quais são as regras que se aplicam ao declarar uniformes?

uniform vec2 xy; // Can we expect the compiler to pack xy
uniform vec2 zw; // into a same four component register?

uniform vec2 rg;
uniform float foo; // Will this prevent from packing rg and ba?
uniform vec2 ba;   // If so, will foo eat up a full four components register?

Se o compilador pode fazer essas otimizações, quão boas elas são? Podemos dizer explicitamente ao compilador para empacotar ou não e quando deveríamos?

Julien Guertault
fonte

Respostas:

4

Eu procurei uma resposta, então baixei o analisador de shader da AMD para ver o conjunto produzido quando compilado para GCN. Na montagem abaixo, os registros vetoriais são v # e os registros escalares são s #.

Parece que os uniformes, mesmo os uniformes de vetor, são passados ​​para o sombreador como escalares separados, portanto, um vec3 usaria 3 registros escalares. O bit que achei confuso foi o v0 a v4, não tenho certeza se v0 é um registro de 4 flutuações completo ou um único flutuador em um registro, com um registro de vetor completo abrangendo v0 a v3. De uma forma ou de outra, não pareceu mudar entre as duas versões, para que eu possa assumir que a ordem de definição não afetou a montagem.

http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/07/AMD_GCN3_Instruction_Set_Architecture.pdf

#version 450

uniform vec2 xy; 
uniform vec2 zw;

out vec4 v;

void main(){ 
    v.xy = xy; 
    v.zw = zw; 
}

shader 
  asic(VI)
  type(VS)

  v_cndmask_b32  v0, s0, v0, vcc               
  v_mov_b32     v0, 0                          
  v_mov_b32     v1, 1.0                        
  exp           pos0, v0, v0, v0, v1 done      
  s_andn2_b32   s0, s5, 0x3fff0000             
  s_mov_b32     s1, s0                         
  s_mov_b32     s2, s6                         
  s_mov_b32     s3, s7                         
  s_mov_b32     s0, s4                         
  s_buffer_load_dwordx2  s[4:5], s[0:3], 0x00  
  s_buffer_load_dwordx2  s[0:1], s[0:3], 0x10  
  s_waitcnt     expcnt(0) & lgkmcnt(0)         
  v_mov_b32     v0, s4                         
  v_mov_b32     v1, s5                         
  v_mov_b32     v2, s0                         
  v_mov_b32     v3, s1                         
  exp           param0, v0, v1, v2, v3         
end

#version 450

uniform vec2 xy;
uniform float z;
uniform vec2 zw;

out vec4 v;

void main(){ 
    v.xy = xy; 
    v.zw = zw;
    v.w += z;
}

shader 
  asic(VI)
  type(VS)

  v_cndmask_b32  v0, s0, v0, vcc              
  v_mov_b32     v0, 0                         
  v_mov_b32     v1, 1.0                       
  s_andn2_b32   s0, s5, 0x3fff0000            
  exp           pos0, v0, v0, v0, v1 done     
  s_mov_b32     s1, s0                        
  s_mov_b32     s2, s6                        
  s_mov_b32     s3, s7                        
  s_mov_b32     s0, s4                        
  s_buffer_load_dword  s4, s[0:3], 0x10       
  s_buffer_load_dwordx2  s[6:7], s[0:3], 0x00 
  s_buffer_load_dwordx2  s[0:1], s[0:3], 0x20 
  s_waitcnt     expcnt(0) & lgkmcnt(0)        
  v_mov_b32     v0, s4                        
  v_add_f32     v0, s1, v0                    
  v_mov_b32     v1, s6                        
  v_mov_b32     v2, s7                        
  v_mov_b32     v3, s0                        
  exp           param0, v1, v2, v3, v0        
end
Joshua Waring
fonte
2
A ordem de definição afetou o layout. A parte relevante aqui são as s_buffer_load_dwordinstruções - elas estão lendo os uniformes de entrada e o último número em hexadecimal é o deslocamento para leitura. Isso mostra que, no primeiro caso, xyestá no deslocamento 0 e zwno deslocamento 16. No segundo caso, você tem xyno deslocamento 0, zno deslocamento 16 e zwno deslocamento 32. Parece que todos os uniformes são individualmente alinhados por 16 bytes e não empacotados juntos ou reordenados.
Nathan Reed