Testing Uniform Read/Write Capabilities

You need the following Simdify® modules to complete this exercise: Simdify® Free Edition

In this exercise you'll learn how to check if your GPU correctly sets uniform values (also referred to as shader constants). Driver bugs are infrequent and these tests let you know if there are problems you should be aware of. It's also worth mentioning that Scenomics uses these tests to verify our OpenGL implementation. We've found plenty of our own bugs this way.

Start The Layout application

  1. Start the Layout app. (Start » Programs » Scenomics » Layout) or (Windows® key and then type 'Layout' to find the app icon.)

    The application displays a splash screen and then the application desktop appears.

    This is a picture of the desktop.

Run Uniform Write/Read Test Command

  1. Examine the main menu and select File » Run Tests.

    The application displays a list of tests that you can run.

  2. Select Test Uniform Write from the listed options and click OK or hit ENTER when you are finished.

    This is going to determine that your GPU is capable of writing uniform values correctly. Verification is performed by writing the uniform values and then assigning them to variables of the same type declared in shader storage buffer objects. We'll explore the test code later.

    Note that some GPUs do not support large shaders. For that reason some of the tests are disabled and will display error messages below. Don't worry about this. The tests can be enabled by editing the GLSL code.

    //////////////////////////////////////////////////
    // Starting Uniform Tests
    //////////////////////////////////////////////////
    
    Found test GLSL: D:\Release6\Content\Tests\GPU\Uniform Write\test_uniform_write.glsl
    Setting up program to run compute shader using GLSL #version 430.
    Successfully compiled shader program.
    Successfully set shader program active on rendering device.
    Validated binding index: 'test_buffer' buffer index is: 0
    Validated buffer size: 'test_buffer' buffer size is 3600 bytes.
    Successfully created buffer 'test_buffer' on device.
    
    //////////////////////////////////////////////////
    // Uniform Read/Write Test For: uniform_test_bool
    //////////////////////////////////////////////////
    
    Setting the following uniform constant:
    
    a_oInfo.Name............................uniform_test_bool
    a_oInfo.TypeCode........................GL_BOOL_ARB
    a_oInfo.TypeName........................bool
    a_oInfo.Index...........................0
    a_oInfo.BufferIndex.....................-1
    a_oInfo.BufferBinding...................-1
    a_oInfo.BufferType......................3
    a_oInfo.Location........................0
    a_oInfo.Offset..........................-1
    a_oInfo.Size............................1
    a_oInfo.Type............................0x8b56
    a_oInfo.TypeSize........................4
    a_oInfo.ArrayStride.....................-1
    a_oInfo.MatrixStride....................-1
    a_oInfo.Rows............................0
    a_oInfo.Cols............................0
    a_oInfo.RowMajor........................0
    a_oInfo.TopLevelArraySize...............0
    a_oInfo.TopLevelArrayStride.............0
    a_oInfo.IsMemberOfTopLevelArray.........0
    a_oInfo.IsMemberOfTopLevelUnsizedArray..0
    a_oInfo.GetTotalSize....................4
    
    a_bWrite is: true
    a_bRead is:  true
    
    Result of memcmp(): 0 [ 0 = passed, any non-zero = fail ]
    
    Uniform constant was assigned to this shader buffer variable in the GLSL code:
    
    a_oInfo.Name............................test_bool
    a_oInfo.TypeCode........................GL_BOOL_ARB
    a_oInfo.TypeName........................bool
    a_oInfo.Index...........................0
    a_oInfo.BufferIndex.....................0
    a_oInfo.BufferBinding...................-1
    a_oInfo.BufferType......................4
    a_oInfo.Location........................-1
    a_oInfo.Offset..........................0
    a_oInfo.Size............................1
    a_oInfo.Type............................0x8b56
    a_oInfo.TypeSize........................4
    a_oInfo.ArrayStride.....................0
    a_oInfo.MatrixStride....................0
    a_oInfo.Rows............................0
    a_oInfo.Cols............................0
    a_oInfo.RowMajor........................0
    a_oInfo.TopLevelArraySize...............1
    a_oInfo.TopLevelArrayStride.............0
    a_oInfo.IsMemberOfTopLevelArray.........0
    a_oInfo.IsMemberOfTopLevelUnsizedArray..0
    a_oInfo.GetTotalSize....................4
    
    ...
    
    

    The test results are pretty simple. There is a heading that indicates the variable being tested, followed by a table of information about the shader constant. This is program reflection information acquired from OpenGL and it can be very useful for diagnosing problems.

    Next we have the write and read data, which clearly shows either success of failure, depending on the result of the test. This makes it much easier to determine the nature of any potential problems. The results of the memcmp function are displayed after the visual presentation of the test data.

    Finally, in this test suite we are assigning the values of the uniforms we set to shader buffer variables so that we can read them back and perform comparisons. The last section contains program reflection information about the shader buffer variable used for the test. This is also useful for diagnosing tricky problems and bugs.

Get The Test Code Path

  1. Select File » Get Application Script Path... from the main menu.

    The software displays a dialog that allows you to select a script.

    This is a picture of the desktop.
  2. Type app_service_test_uniform_util.ssl and left click the script when it appears.
  3. Click OK or hit ENTER when you are finished.

    The software copies the full path to the script to the Windows® clipboard.

Examine The Test Code 'Execute' Function

  1. Start a text editor of your choice and select the option to open a file from disk.
  2. Select CTRL + V to paste the fragment shader file path (into the place in the dialog where you specify the file to open) and open the file.
  3. Use the text editor search feature to find a function named Execute. There are a few clearly delineated sections.
    1. Set up a shader program.
    2. Set up a shader buffer for testing.
    3. Set up a memory barrier.
    4. A section for testing a single function at a time.
    5. A section that performs all tests.
    ///////////////////////////////////////////////////////////////////////////////
    // function
    ///////////////////////////////////////////////////////////////////////////////
    
    function bool Execute()
    {
       //////////////////////////////////////////////////
       // Set up the shader program.
       //////////////////////////////////////////////////
    
       auto GpuTestDevice a_oTest;
    
       Render3D a_oAccel = Application.GetAccelerate3D();
       a_oTest.m_pAccel = a_oAccel;
    
       auto Program a_oProgram;
       a_oTest.m_pProgram = a_oProgram;
    
       // Create and bind the uniform buffer.
       a_oTest.m_sGlslTestFile = "test_uniform_write.glsl";
       a_oTest.m_sBufferName = "test_buffer";
    
       a_oTest.m_slInfo.Add( LibAppServiceTest.Bar() );
       a_oTest.m_slInfo.Add( "// Starting Uniform Tests" );
       a_oTest.m_slInfo.Add( LibAppServiceTest.Bar() );
       a_oTest.m_slInfo.AddBlank();
    
       bool a_bInit = LibAppServiceTest.Initialize( a_oTest );
       if( !( a_bInit ) )
       {
          return false;
       }
    
       Console.Clear();
    
       //////////////////////////////////////////////////
       // Create and bind the shader buffer.
       //////////////////////////////////////////////////
    
       auto ShaderBufferNode a_oShaderBuffer;
    
       auto ShaderBufferInfo a_oShaderBufferInfo;
       a_oShaderBufferInfo.m_pAccel = a_oAccel;
       a_oShaderBufferInfo.m_pRenderInfo = a_oTest.m_pRenderInfo;
       a_oShaderBufferInfo.m_pBuffer = a_oShaderBuffer;
       a_oShaderBufferInfo.m_pProgram = a_oProgram;
       a_oShaderBufferInfo.m_sBufferName = "test_buffer";
       a_oShaderBufferInfo.m_eBufferUsage = Enum.RBU_DynamicDraw();
    
       bool a_bCreatedBuffer = LibRender3D.CreateShaderBuffer(
          a_oShaderBufferInfo, a_oTest.m_slInfo );
    
       if( !( a_bCreatedBuffer ) )
       {
          a_oTest.m_slInfo.AddBlank();
          a_oTest.m_slInfo.Add( LibAssert.PrintCallStack() );
          LibAppServiceBuild.Out( a_oTest.m_slInfo );
          return false;
       }
    
       //////////////////////////////////////////////////
       // Set up the memory barrier.
       //////////////////////////////////////////////////
    
       // Since we're writing to a shader buffer, we
       // need to use a memory barrier between
       // accesses to the buffer.
       auto MemoryBarrier m;
       m.GlShaderStorageBarrierBit = true;
    
       //////////////////////////////////////////////////
       // Execute single tests.
       //////////////////////////////////////////////////
    
       if( false )
       {
          TestUniformFloat64Matrix( a_oTest, 2, 2 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 2, 3 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 2, 4 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 3, 2 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 3, 3 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 3, 4 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 4, 2 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 4, 3 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
          TestUniformFloat64Matrix( a_oTest, 4, 4 );
          a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
          LibAppServiceBuild.Out( a_oTest.m_slInfo );
    
          // LEAVE THIS HERE.
          a_oTest.m_pAccel.DeleteProgram( a_oProgram.GetHandle() );
          a_oTest.m_pAccel.DestroyShaderBuffer( a_oShaderBuffer.GetParams() );
          delete a_oTest.m_pRenderInfo;
    
          return false;
       }
    
       //////////////////////////////////////////////////
       // Execute all tests.
       //////////////////////////////////////////////////
    
       TestUniformBool( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolArray( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformBoolVectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformUint32( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint32VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformUint64( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformUint64VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformInt32( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt32VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformInt64( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformInt64VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformFloat32( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 2, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 2, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 2, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 3, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 3, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 3, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 4, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 4, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32Matrix( a_oTest, 4, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 2, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 2, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 2, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 3, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 3, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 3, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 4, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 4, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat32MatrixArray( a_oTest, 4, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       TestUniformFloat64( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Array( a_oTest );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Vector( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Vector( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Vector( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64VectorArray( a_oTest, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64VectorArray( a_oTest, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64VectorArray( a_oTest, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 2, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 2, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 2, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 3, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 3, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 3, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 4, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 4, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64Matrix( a_oTest, 4, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 2, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 2, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 2, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 3, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 3, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 3, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 4, 2 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 4, 3 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
       TestUniformFloat64MatrixArray( a_oTest, 4, 4 );
       a_oTest.m_pAccel.InsertMemoryBarrier( m );
    
       // Do cleanup.
       a_oTest.m_pAccel.DeleteProgram( a_oProgram.GetHandle() );
       a_oTest.m_pAccel.DestroyShaderBuffer( a_oShaderBuffer.GetParams() );
       delete a_oTest.m_pRenderInfo;
    
       //////////////////////////////////////////////////
       // Finis~
       //////////////////////////////////////////////////
    
       LibAppServiceBuild.Out( a_oTest.m_slInfo );
    
       Console.Blank();
       Console.Out( "Executed: " + Script.Runtime.CurrentFunction.Name );
    
       return true;
    }
             

Examine The Test Code 'TestUniformFloat64VectorArray' Function

  1. Use the text editor search feature to find a function named TestUniformFloat64VectorArray. There are a few key sections.

    1. Create the variable name.
    2. Initialize the uniform or return upon failure.
    3. Create the data we want to write and declare storage for the readback.
    4. Set the uniform on the device and execute the shader program.
    5. Get reflection information (constant info) for the shader buffer variable.
    6. Read back the value of the uniform that was written to the shader buffer variable during program execution.
    7. Print data.
    8. Print test results.
    9. Print shader buffer variable reflection information.

    ///////////////////////////////////////////////////////////////////////////////
    // function
    ///////////////////////////////////////////////////////////////////////////////
    
    function void TestUniformFloat64VectorArray( GpuTestDevice p_oTest, int p_nVectorDim )
    {
       //////////////////////////////////////////////////
       // Create variable name.
       //////////////////////////////////////////////////
    
       string a_sVariable = "test_dvec" + p_nVectorDim + "_array[0]";
       string a_sUniformName = "uniform_" + a_sVariable;
    
       //////////////////////////////////////////////////
       // Initialize uniform.
       //////////////////////////////////////////////////
    
       auto ProgramConstantInfo a_oUniformInfo;
    
       bool a_bInit = LibAppServiceTest.InitializeUniform(
          p_oTest.m_pAccel, p_oTest.m_pProgram, a_sUniformName,
          a_oUniformInfo, p_oTest.m_slInfo );
    
       if( !( a_bInit ) )
       {
          return;
       }
    
       //////////////////////////////////////////////////
       // Create the data we want to write and declare storage for the readback
       //////////////////////////////////////////////////
    
       auto Float64VectorArray a_avWrite;
       auto Float64Vector wa = new Float64Vector( 0.1,  1.2,  2.3,  3.4 );
       auto Float64Vector wb = new Float64Vector( 4.4,  5.3,  6.2,  7.1 );
       auto Float64Vector wc = new Float64Vector( 8.1,  9.2,  10.3, 11.4 );
       auto Float64Vector wd = new Float64Vector( 12.4, 13.3, 14.2, 15.1 );
       a_avWrite.Add( wa );
       a_avWrite.Add( wb );
       a_avWrite.Add( wc );
       a_avWrite.Add( wd );
    
       auto Float64VectorArray a_avRead;
       auto Float64Vector ra = new Float64Vector( 0, 0, 0, 0 );
       auto Float64Vector rb = new Float64Vector( 0, 0, 0, 0 );
       auto Float64Vector rc = new Float64Vector( 0, 0, 0, 0 );
       auto Float64Vector rd = new Float64Vector( 0, 0, 0, 0 );
       a_avRead.Add( ra );
       a_avRead.Add( rb );
       a_avRead.Add( rc );
       a_avRead.Add( rd );
    
       auto Float64ArrayView src_view = a_avWrite.GetView();
       auto Float64ArrayView dst_view = a_avRead.GetView();
    
       // For vec2 or vec3, we need to clear some
       // values so that we can be sure our write
       // and read data is minimal and clean.
       for( int v = 0; v < a_avWrite.GetCount(); ++v )
       {
          Float64Vector a_vVector = a_avWrite.Get( v );
          for( int s = p_nVectorDim; s < Enum.GLSL_DataType_Vec4(); ++s )
          {
             a_vVector.SetComponent( s, 0 );
          }
       }
    
       //////////////////////////////////////////////////
       // Set the uniform on the device and execute the shader program.
       //////////////////////////////////////////////////
    
       auto Float64ArrayAlgorithms fa;
       auto Float64VectorArrayAlgorithms va;
    
       p_oTest.m_pAccel.SetProgramConstantFloat64VectorArray(
          p_oTest.m_pProgram.GetHandle(), a_oUniformInfo.Location,
          a_avWrite, p_nVectorDim, a_avWrite.GetCount() );
    
       p_oTest.m_pAccel.ExecuteProgram( true, 128, 128, 1, 32, 32, 1 );
    
       //////////////////////////////////////////////////
       // Get reflection information (constant info) for the shader buffer variable.
       //////////////////////////////////////////////////
    
       auto ProgramConstantInfo a_oBufferInfo;
       p_oTest.m_pAccel.GetProgramConstantInfo(
          p_oTest.m_pProgram.GetHandle(), a_sVariable, a_oBufferInfo );
    
       //////////////////////////////////////////////////
       // Read back the value of the uniform that was written to the shader buffer variable during program execution.
       //////////////////////////////////////////////////
    
       // All vec3 uniforms are aligned to 16-bytes
       // so we have to do the readback as if we
       // are reading back a vec4.
       if( p_nVectorDim == Enum.GLSL_DataType_Vec3() )
       {
          p_nVectorDim = Enum.GLSL_DataType_Vec4();
       }
    
       int a_nSize = SizeLimits.sizeof_float64() *
          p_nVectorDim * a_avWrite.GetCount();
    
       auto Float64Array a_afReadData = new Float64Array(
          p_nVectorDim * a_avWrite.GetCount(), 0 );
       auto Float64ArrayView read_view = a_afReadData.GetView();
    
       auto MemoryPointer a_oDstPtr = a_afReadData.GetPointer();
    
       p_oTest.m_pAccel.ReadShaderSubBuffer(
          a_oBufferInfo.Offset, a_afReadData.SizeInBytes(), a_oDstPtr );
    
       va.PackScalarArrayInVectors(
          read_view.First, read_view.Last, dst_view.First, p_nVectorDim );
    
       //////////////////////////////////////////////////
       // Print data.
       //////////////////////////////////////////////////
    
       p_oTest.m_slInfo.AddBlank();
       p_oTest.m_slInfo.Add( "Write values:" );
       LibFloat64VectorArray.ConvertToString(
          a_avWrite, 0, a_avWrite.GetCount(), p_oTest.m_slInfo );
    
       p_oTest.m_slInfo.AddBlank();
       p_oTest.m_slInfo.Add( "Read values:" );
       LibFloat64VectorArray.ConvertToString(
          a_avRead, 0, a_avRead.GetCount(), p_oTest.m_slInfo );
    
       //////////////////////////////////////////////////
       // Print test results.
       //////////////////////////////////////////////////
    
       LibAppServiceTest.DoMemcmp( p_oTest.m_slInfo,
          fa.Memcmp( src_view.First, dst_view.First, a_nSize ) );
    
       //////////////////////////////////////////////////
       // Print shader buffer variable reflection information.
       //////////////////////////////////////////////////
    
       GetBufferCopyMessage( p_oTest.m_slInfo );
    
       LibProgramConstantInfo.Print( a_oBufferInfo, p_oTest.m_slInfo );
    }