2015-10-04 3 views
5

Meine Frage kam beim Experimentieren mit einer Reihe von verschiedenen Techniken, von denen ich keine Erfahrung habe. Leider weiß ich nicht einmal, ob ich einen albernen logischen Fehler mache, ob ich die Kiste falsch verwende, ob ich in GLSL verpfuscht, etc. Trotzdem gelang es mir, ein neues Rust-Projekt zu starten scratchen, arbeite an einem minimalen Beispiel, das mein Problem zeigt, und das Problem reproduziert auf meinem Computer mindestens.Übergeben eines beliebig großen Objekts an einen Fragment-Shader mit einem UniformBuffer in Glium

Das minimale Beispiel ist jedoch schwer zu erklären, also mache ich zuerst ein noch minimaleres Beispiel, das tut, was ich will, wenn auch durch hackende Bits und auf 128 Elemente beschränkt (vier mal 32 Bit) , in einem GLSLuvec4). Daraus ist der Schritt zu der Version, in der mein Problem auftaucht, ziemlich einfach.

eine funktionierende Version, mit einfachen und uniform Bitverschiebung

Das Programm erzeugt ein einzelnes Rechteck auf dem Bildschirm, mit Textur horizontal von 0.0 zu 128.0 Koordinaten. Das Programm enthält einen Vertex-Shader für das Rechteck und einen Fragment-Shader, der die Texturkoordinaten verwendet, um vertikale Streifen auf das Rechteck zu zeichnen: Wenn die Texturkoordinate (geklammert auf uint) ungerade ist, zeichnet sie eine Farbe, wenn die Texturkoordinate ist sogar, es zieht eine andere Farbe.

// GLIUM, the crate I'll use to do "everything OpenGL" 
#[macro_use] 
extern crate glium; 

// A simple struct to hold the vertices with their texture-coordinates. 
// Nothing deviating much from the tutorials/crate-documentation. 
#[derive(Copy, Clone)] 
struct Vertex { 
    position: [f32; 2], 
    tex_coords: [f32; 2], 
} 

implement_vertex!(Vertex, position, tex_coords); 


// The vertex shader's source. Does nothing special, except passing the 
// texture coordinates along to the fragment shader. 
const VERTEX_SHADER_SOURCE: &'static str = r#" 
    #version 140 

    in vec2 position; 
    in vec2 tex_coords; 
    out vec2 preserved_tex_coords; 

    void main() { 
     preserved_tex_coords = tex_coords; 
     gl_Position = vec4(position, 0.0, 1.0); 
    } 
"#; 

// The fragment shader. uses the texture coordinates to figure out which color to draw. 
const FRAGMENT_SHADER_SOURCE: &'static str = r#" 
    #version 140 

    in vec2 preserved_tex_coords; 
    // FIXME: Hard-coded max number of elements. Replace by uniform buffer object 
    uniform uvec4 uniform_data; 
    out vec4 color; 

    void main() { 
     uint tex_x = uint(preserved_tex_coords.x); 
     uint offset_in_vec = tex_x/32u; 
     uint uint_to_sample_from = uniform_data[offset_in_vec]; 
     bool the_bit = bool((uint_to_sample_from >> tex_x) & 1u); 
     color = vec4(the_bit ? 1.0 : 0.5, 0.0, 0.0, 1.0); 
    } 
"#; 

// Logic deciding whether a certain index corresponds with a 'set' bit on an 'unset' one. 
// In this case, for the alternating stripes, a trivial odd/even test. 
fn bit_should_be_set_at(idx: usize) -> bool { 
    idx % 2 == 0 
} 

fn main() { 
    use glium::DisplayBuild; 
    let display = glium::glutin::WindowBuilder::new().build_glium().unwrap(); 

    // Sets up the vertices for a rectangle from -0.9 till 0.9 in both dimensions. 
    // Texture coordinates go from 0.0 till 128.0 horizontally, and from 0.0 till 
    // 1.0 vertically. 
    let vertices_buffer = glium::VertexBuffer::new(
     &display, 
     &vec![Vertex { position: [ 0.9, -0.9], tex_coords: [ 0.0, 0.0] }, 
       Vertex { position: [ 0.9, 0.9], tex_coords: [ 0.0, 1.0] }, 
       Vertex { position: [-0.9, -0.9], tex_coords: [128.0, 0.0] }, 
       Vertex { position: [-0.9, 0.9], tex_coords: [128.0, 1.0] }]).unwrap(); 
    // The rectangle will be drawn as a simple triangle strip using the vertices above. 
    let indices_buffer = glium::IndexBuffer::new(&display, 
               glium::index::PrimitiveType::TriangleStrip, 
               &vec![0u8, 1u8, 2u8, 3u8]).unwrap(); 
    // Compiling the shaders defined statically above. 
    let shader_program = glium::Program::from_source(&display, 
                VERTEX_SHADER_SOURCE, 
                FRAGMENT_SHADER_SOURCE, 
                None).unwrap(); 

    // Some hackyy bit-shifting to get the 128 alternating bits set up, in four u32's, 
    // which glium manages to send across as an uvec4. 
    let mut uniform_data = [0u32; 4]; 
    for idx in 0..128 { 
     let single_u32 = &mut uniform_data[idx/32]; 
     *single_u32 = *single_u32 >> 1; 
     if bit_should_be_set_at(idx) { 
      *single_u32 = *single_u32 | (1 << 31); 
     } 
    } 

    // Trivial main loop repeatedly clearing, drawing rectangle, listening for close event. 
    loop { 
     use glium::Surface; 
     let mut frame = display.draw(); 
     frame.clear_color(0.0, 0.0, 0.0, 1.0); 
     frame.draw(&vertices_buffer, &indices_buffer, &shader_program, 
        &uniform! { uniform_data: uniform_data }, 
        &Default::default()).unwrap(); 
     frame.finish().unwrap(); 

     for e in display.poll_events() { if let glium::glutin::Event::Closed = e { return; } } 
    } 
} 

Aber das ist nicht gut genug ...

Dieses Programm funktioniert, und zeigt das Rechteck mit Streifen abwechseln, hat aber die klare Begrenzung auf 128 Streifen beschränkt zu sein (oder 64 Streifen, I Die anderen 64 sind "der Hintergrund des Rechtecks". Um beliebig viele Streifen zuzulassen (oder im Allgemeinen beliebig viele Daten an einen Fragment-Shader zu übergeben), kann uniform buffer objects verwendet werden, which glium exposes. Die most relevant example in the glium repo kann leider nicht auf meinem Computer kompiliert werden: die GLSL Version wird nicht unterstützt, das buffer Schlüsselwort ist ein Syntaxfehler in den unterstützten Versionen, Compute Shader im Allgemeinen werden nicht unterstützt (mit glium, auf meinem Rechner), und beide sind kopflos Render Kontexte.

Ein nicht-so-viel-Version arbeiten, mit Puffer uniform

also ohne die Möglichkeit, von diesem Beispiel zu beginnen, hatte ich von Grund auf neu starten Sie die Dokumentation verwenden. Für das obige Beispiel, kam ich mit dem Follow-up:

// Nothing changed here... 
#[macro_use] 
extern crate glium; 

#[derive(Copy, Clone)] 
struct Vertex { 
    position: [f32; 2], 
    tex_coords: [f32; 2], 
} 

implement_vertex!(Vertex, position, tex_coords); 


const VERTEX_SHADER_SOURCE: &'static str = r#" 
    #version 140 

    in vec2 position; 
    in vec2 tex_coords; 
    out vec2 preserved_tex_coords; 

    void main() { 
     preserved_tex_coords = tex_coords; 
     gl_Position = vec4(position, 0.0, 1.0); 
    } 
"#; 
// ... up to here. 

// The updated fragment shader. This one uses an entire uint per stripe, even though only one 
// boolean value is stored in each. 
const FRAGMENT_SHADER_SOURCE: &'static str = r#" 
    #version 140 
    // examples/gpgpu.rs uses 
    //  #version 430 
    //  buffer layout(std140); 
    // but that shader version is not supported by my machine, and the second line is 
    // a syntax error in `#version 140` 

    in vec2 preserved_tex_coords; 

    // Judging from the GLSL standard, this is what I have to write: 
    layout(std140) uniform; 
    uniform uniform_data { 
     // TODO: Still hard-coded max number of elements, but now arbitrary at compile-time. 
     uint values[128]; 
    }; 
    out vec4 color; 

    // This one now becomes much simpler: get the coordinate, clamp to uint, index into 
    // uniform using tex_x, cast to bool, choose color. 
    void main() { 
     uint tex_x = uint(preserved_tex_coords.x); 
     bool the_bit = bool(values[tex_x]); 
     color = vec4(the_bit ? 1.0 : 0.5, 0.0, 0.0, 1.0); 
    } 
"#; 


// Mostly copy-paste from glium documentation: define a Data type, which stores u32s, 
// make it implement the right traits 
struct Data { 
    values: [u32], 
} 

implement_buffer_content!(Data); 
implement_uniform_block!(Data, values); 


// Same as before 
fn bit_should_be_set_at(idx: usize) -> bool { 
    idx % 2 == 0 
} 

// Mostly the same as before 
fn main() { 
    use glium::DisplayBuild; 
    let display = glium::glutin::WindowBuilder::new().build_glium().unwrap(); 

    let vertices_buffer = glium::VertexBuffer::new(
     &display, 
     &vec![Vertex { position: [ 0.9, -0.9], tex_coords: [ 0.0, 0.0] }, 
       Vertex { position: [ 0.9, 0.9], tex_coords: [ 0.0, 1.0] }, 
       Vertex { position: [-0.9, -0.9], tex_coords: [128.0, 0.0] }, 
       Vertex { position: [-0.9, 0.9], tex_coords: [128.0, 1.0] }]).unwrap(); 
    let indices_buffer = glium::IndexBuffer::new(&display, 
               glium::index::PrimitiveType::TriangleStrip, 
               &vec![0u8, 1u8, 2u8, 3u8]).unwrap(); 
    let shader_program = glium::Program::from_source(&display, 
                VERTEX_SHADER_SOURCE, 
                FRAGMENT_SHADER_SOURCE, 
                None).unwrap(); 


    // Making the UniformBuffer, with room for 128 4-byte objects (which u32s are). 
    let mut buffer: glium::uniforms::UniformBuffer<Data> = 
       glium::uniforms::UniformBuffer::empty_unsized(&display, 4 * 128).unwrap(); 
    { 
     // Loop over all elements in the buffer, setting the 'bit' 
     let mut mapping = buffer.map(); 
     for (idx, val) in mapping.values.iter_mut().enumerate() { 
      *val = bit_should_be_set_at(idx) as u32; 
      // This _is_ actually executed 128 times, as expected. 
     } 
    } 

    // Iterating again, reading the buffer, reveals the alternating 'bits' are really 
    // written to the buffer. 

    // This loop is similar to the original one, except that it passes the buffer 
    // instead of a [u32; 4]. 
    loop { 
     use glium::Surface; 
     let mut frame = display.draw(); 
     frame.clear_color(0.0, 0.0, 0.0, 1.0); 
     frame.draw(&vertices_buffer, &indices_buffer, &shader_program, 
        &uniform! { uniform_data: &buffer }, 
        &Default::default()).unwrap(); 
     frame.finish().unwrap(); 

     for e in display.poll_events() { if let glium::glutin::Event::Closed = e { return; } } 
    } 
} 

Ich würde erwarten, dass dies das gleiche gestreifte Rechteck erzeugen (oder einige Fehler zu geben, oder Absturz, wenn etwas, das ich war falsch gemacht haben). Stattdessen zeigt es das Rechteck mit dem am weitesten rechts liegenden Viertel in durchgehendem, leuchtendem Rot (dh "das Bit schien gesetzt zu sein, als der Fragment-Shader es gelesen hat") und die restlichen drei Viertel dunkler rot (dh "das Bit war nicht gesetzt, als das Fragment-Shader lesen Sie es ").

-Update seit original

Posting

ich wirklich bin hier im Dunkel stechend, so zu denken, es könnte ein geringer Fehler bei Speicherordnungs, endianness sein, Puffer Über-/Unterschreitung, usw. Ich hat versucht, verschiedene Möglichkeiten, Füllen "benachbarter" Speicherplätze mit leicht erkennbaren Bitmustern (z. B. ein Bit in jedem dritten Satz, einer in allen vier, zwei Sätze gefolgt von zwei nicht gesetzten usw.). Dies hat die Ausgabe nicht verändert.

Einer der offensichtlichen Möglichkeiten, um Speicher zu bekommen ‚nahe‘ die uint values[128] ist es in die Data Struktur zu setzen, gerade vor dem values (hinter den values ist nicht erlaubt, da Data ‚s values: [u32] dynamische Größe ist). Wie oben erwähnt, ändert dies nicht die Ausgabe. Ein korrekt gefülltes uvec4 in dem Puffer uniform_data und eine main ähnliche Funktion wie des ersten Beispiels zu erstellen, führt jedoch zu dem ursprünglichen Ergebnis. Dies zeigt, dass die glium::uniforms::UniformBuffer<Data>in sefunktioniert arbeiten.

Ich habe daher den Titel aktualisiert, um zu reflektieren, dass das Problem irgendwo anders zu liegen scheint.

Nachdem Eli Antwort

@Eli Friedman Antwort hat mir geholfen, zu einer Lösung Fortschritt, aber ich bin ganz noch nicht da.

Das Zuweisen und Füllen eines viermal so großen Puffers änderte die Ausgabe von einem viertelgefüllten Rechteck zu einem voll gefüllten Rechteck. Ups, das wollte ich nicht. Mein Shader liest nun aus den richtigen Speicherwörtern. All diese Wörter sollten mit dem richtigen Bitmuster gefüllt sein. Dennoch wurde kein Teil des Rechtecks ​​gestreift. Da bit_should_be_set_at jedes andere Bit gesetzt sollte ich die Hypothese entwickelt, dass die folgenden, was los war ist:

Bits: 1010101010101010101010101010101010101 
Seen:^^^^^^^^^^ 
What it looks like: all bits set 

Um diese Hypothese zu testen, änderte ich bit_should_be_set_attrue auf ein Vielfaches von 3 zurückzukehren, 4, 5, 6, 7 und 8. Die Ergebnisse fallen mit meiner Hypothese zusammen:

Bits: 1001001001001001001001001001001001001 
Seen:^^^^^^^^^^ 
What it looks like: first bit set, then repeating two unset, one set. 

Bits: 1000100010001000100010001000100010001 
Seen:^^^^^^^^^^ 
What it looks like: all bits set 

Bits: 1000010000100001000010000100001000010 
Seen:^^^^^^^^^^ 
What it looks like: first bit set, then repeating four unset, one set. 

Bits: 1000001000001000001000001000001000001 
Seen:^^^^^^^^^^ 
What it looks like: first bit set, then repeating two unset, one set. 

Bits: 1000000100000010000001000000100000010 
Seen:^^^^^^^^^^ 
What it looks like: first bit set, then repeating six unset, one set. 

Bits: 1000000010000000100000001000000010000 
Seen:^^^^^^^^^^ 
What it looks like: first bit set, then every other bit set. 

Macht diese Hypothese Sinn? Und egal: Sieht es so aus, als ob das Problem darin besteht, die Daten auf der Seite von Rust zu setzen oder sie auf der GLSL-Seite wieder zu lesen?

Antwort

1

Das Problem, mit dem Sie es zu tun haben, hängt damit zusammen, wie Uniformen zugewiesen werden. uint values[128]; hat nicht das Speicherlayout, von dem Sie denken, dass es das tut; es hat tatsächlich das gleiche Speicherlayout wie uint4 values[128]. Siehe https://www.opengl.org/registry/specs/ARB/uniform_buffer_object.txt Unterabschnitt 2.15.3.1.2.

+0

Danke, das hat mich auf den Weg gebracht, dies zu lösen. Ich habe die Frage aktualisiert, um meine Fortschritte zu reflektieren. – Thierry

Verwandte Themen