Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Is there some way to bind uniform with dynamic size to shader?

  1. #1
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    14

    Question Is there some way to bind uniform with dynamic size to shader?

    I have shader with constant size of uniform array looks like next:
    Code :
    #version 430  // maximum version that can be supported by my ATI card with open source mesa driver.
     
    const int MAX_POINT_LIGHTS = 5;
    ...
     
    uniform PointLight pointLights[MAX_POINT_LIGHTS];
     
    struct Attenuation {
      float constant;
      float linear;
      float exponent;
      float distance;
    };
     
     
    struct PointLight {
      vec3 colour;
      vec3 position;
      float intensity;
      Attenuation att;
    };
     
    ...


    May I use in some way dynamic size of uniform array with my own structs?
    I mean:
    1 - On the CPU side I calculate "point light" number as three items.
    2 - I bind them those three lights to the shader.
    3 - On the shader side I have array with 3 items with known size.

    That is so cool if it possible!

    Tank you for answer!
    Last edited by nimelord; 12-05-2017 at 12:53 PM.

  2. #2
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    2,523
    The size of a uniform array must be known when the shader is linked. You can declare the array with the maximum required size then use a uniform int variable to indicate the number of valid elements in the array.

    An array which is the last element of a shader storage block can have a dynamic size, the size being determined by the size of the buffer associated with the block.

  3. #3
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    14
    Quote Originally Posted by GClements View Post
    An array which is the last element of a shader storage block can have a dynamic size, the size being determined by the size of the buffer associated with the block.
    Do you mean this one? https://www.khronos.org/opengl/wiki/..._Buffer_Object

    If yes, how can I use my "struct PointLight" in the such way?
    I have found only using of primitives in examples, I mean int, float, e.t.c.

  4. #4
    Member Regular Contributor
    Join Date
    May 2016
    Posts
    445
    Code glsl:
    layout (std430, binding = 1) buffer POINTLIGHTBLOCK {
        PointLight mypointlights[]; /* unsized array */
    };

    Code cpp:
    /* create your pointlight buffer "mypointlightbuffer" here */
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, mypointlightbuffer);

    and read about the memory layout (std430 in case of shader storage blocks)
    https://www.khronos.org/opengl/wiki/...#Memory_layout

  5. #5
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    14
    I did it using "Shader Storage Buffer".
    It basically works except one thing that confused me.

    In shader i have structure with substructure: Attenuation is part of PointLight. (see code with them in the topic body)

    When I fill buffer in correct order: colour, position, intensity, att.constant, att.linear, att.exponent, att.distance, I get mess with data on the shader side.
    But if I write Attenuation data before intensity, then all is ok.

    Could you explain such behavior?

  6. #6
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    14
    I caught the problem!
    This was a packing of data in the buffer.

    I use two vec3 objects, one float, and substructure with four floats in the order.
    I read somewhere that for using vec3 it must be filled as vec4: vec3 + one empty float.
    So, I did it in such way.
    In fact I was wrong: after last vec3 and before other type empty float is redundant. (I think so. Anyway it works.)
    See comment in the code sample where was is redundant empty float:

    Thus I should use vec4 instead vec3.


    My shader:
    Code :
    #version 430
    ...
     
    struct Attenuation {
      float constant;
      float linear;
      float exponent;
      float distance;
    };
     
     
    struct PointLight {
      vec3 colour;
      vec3 position;
      float intensity;
      Attenuation att;
    };
     
    ...
     
    layout (shared, binding = 3) buffer NewPointLight {
      PointLight pls[];
    } newPointLights;
    ...


    And binding:
    Code :
     
        private int pointLights;
        ...
     
     
        pointLights = glGenBuffers();
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, pointLights);
        ByteBuffer pointLightsData = BufferUtils.createByteBuffer(52);  // in bytes
     
        for (PointLight pointLight : scene.getSceneLight().getPointLightList()) {
     
          pointLightsData.putFloat(pointLight.getColor().x);
          pointLightsData.putFloat(pointLight.getColor().y);
          pointLightsData.putFloat(pointLight.getColor().z);
          pointLightsData.putFloat(0);
     
          pointLightsData.putFloat(pointLight.getPosition().x);
          pointLightsData.putFloat(pointLight.getPosition().y);
          pointLightsData.putFloat(pointLight.getPosition().z);
          pointLightsData.putFloat(0); // this one is dedundant!
     
          pointLightsData.putFloat(pointLight.getIntensity());
     
          pointLightsData.putFloat(pointLight.getAttenuation().getConstant());
          pointLightsData.putFloat(pointLight.getAttenuation().getLinear());
          pointLightsData.putFloat(pointLight.getAttenuation().getExponent());
          pointLightsData.putFloat(pointLight.getAttenuation().getDistance());
        }
        pointLightsData.flip();
     
        glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, pointLights);
        glBufferData(GL_SHADER_STORAGE_BUFFER, pointLightsData, GL_STATIC_DRAW);
        glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);


    I have one more question: I must build and fill buffer per each rendered frame, or there is more correct way to use it?

    Thanks for answer.
    Last edited by nimelord; 12-07-2017 at 01:08 PM.

  7. #7
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    2,523
    Quote Originally Posted by nimelord View Post
    This was a packing of data in the buffer.

    I use two vec3 objects, one float, and substructure with four floats in the order.
    I read somewhere that for using vec3 it must be filled as vec4: vec3 + one empty float.
    So, I did it in such way.
    In fact I was wrong: after last vec3 and before other type empty float is redundant. (I think so. Anyway it works.)
    See comment in the code sample where was is redundant empty float:
    A vec3 will be aligned to a 4N boundary (where N is the size of a float). So there's a single float of padding between colour and position to ensure that position has the correct alignment. The intensity field doesn't require aligning, nor does the Attenuation structure (as it's comprised entirely of floats).

    More generally: vec3 and vec4 are aligned to 4N, vec2 to 2N, and a structure to the largest alignment of any of its members. The std140 layout additionally requires array elements and structures to be aligned to at least 4N.

    Quote Originally Posted by nimelord View Post
    I have one more question: I must build and fill buffer per each rendered frame, or there is more correct way to use it?
    If the data is constant, you'd create and fill the buffer once, at start-up. If you need to change the data, you can either overwrite the portions which change or replace the entire buffer. Note that overwriting a portion of a buffer which is being used by pending commands will stall until those commands have completed.

  8. #8
    Junior Member Newbie
    Join Date
    Nov 2017
    Posts
    14
    Quote Originally Posted by GClements View Post
    A vec3 will be aligned to a 4N boundary (where N is the size of a float). So there's a single float of padding between colour and position to ensure that position has the correct alignment. The intensity field doesn't require aligning, nor does the Attenuation structure (as it's comprised entirely of floats).

    More generally: vec3 and vec4 are aligned to 4N, vec2 to 2N, and a structure to the largest alignment of any of its members. The std140 layout additionally requires array elements and structures to be aligned to at least 4N.

    hm.....

    Why it don't need empty cell between position and intensity, if last vec3 (position) needs to be aligned to a 4N too, as you wrote?
    If I write empty one after position it is not work.


    In my case I keep lights on the buffer. These lights can be moved on the scene, so I need to change content in the buffer between renrering of new frame of scene.
    I think I should bind already builded buffer, and just rewrite it, am I right?

  9. #9
    Senior Member OpenGL Guru
    Join Date
    Jun 2013
    Posts
    2,523
    Quote Originally Posted by nimelord View Post
    Why it don't need empty cell between position and intensity, if last vec3 (position) needs to be aligned to a 4N too, as you wrote?
    position needs to start at a multiple of 4N from the beginning of the block, which is why there's padding before it. intensity doesn't have any alignment requirement, so there's no padding inserted between position and intensity.

    Quote Originally Posted by nimelord View Post
    In my case I keep lights on the buffer. These lights can be moved on the scene, so I need to change content in the buffer between renrering of new frame of scene.
    I think I should bind already builded buffer, and just rewrite it, am I right?
    I'd suggest replacing the entire buffer contents, with glBufferData() or glMapBufferRange(GL_MAP_INVALIDATE_BUFFER_BIT).

  10. #10
    Senior Member OpenGL Guru Dark Photon's Avatar
    Join Date
    Oct 2004
    Location
    Druidia
    Posts
    4,171
    Quote Originally Posted by GClements View Post
    positionI'd suggest replacing the entire buffer contents, with glBufferData() or glMapBufferRange(GL_MAP_INVALIDATE_BUFFER_BIT).
    I second that recommendation. I've fixed quite a few frame stalls where partial updates (via synchronized Map or Sub) triggering implicit synchronization inside the GL driver was the cause.

    Also, if you sometimes test on NVidia drivers, glEnable( GL_DEBUG_OUTPUT ) and plug in a glDebugMessageCallback() (part of OpenGL 4.3; see KHR_debug for details), and the NVidia driver will tell you when your buffer update method is causing synchronization (potential slowdowns) inside the driver. Not sure if anyone else's GL driver does this though. As easy as it is to try, it's worth checking.
    Last edited by Dark Photon; 12-11-2017 at 06:25 AM.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •