Enabled sampler arrays in shader 41/291641/1
authorDavid Steele <david.steele@samsung.com>
Wed, 19 Apr 2023 16:22:49 +0000 (17:22 +0100)
committerDavid Steele <david.steele@samsung.com>
Wed, 19 Apr 2023 16:22:49 +0000 (17:22 +0100)
Previously, texture units were only bound to the first
element of any sampler array.

Modified the shader reflection parser to find sampler arrays
of the format
  uniform sampler2D mySamplers[16]

and store the number of elements in the sampler code.

Modified the binding code to ensure that textures are bound
to each element of a sampler array.

Made several assumptions in this code:
A) that GL returns sequential locations for each uniform array element
(It doesn't guarantee a strict sequence, but in practice, this is the
case).

B) that the dali-core implementation binds texture units in the order 0..N-1
with a binding index of 0..N-1. (A safe assumption, given we know how
dali-core works!)

Note, we don't really use locations for non-sampler uniform array elements,
either, we just offset from the first location.

Change-Id: I68c61f3e22303271d5d916c3588f4a3bd5898757
Signed-off-by: David Steele <david.steele@samsung.com>
dali/internal/graphics/gles-impl/gles-context.cpp
dali/internal/graphics/gles-impl/gles-graphics-reflection.cpp

index 5e83601..8a3602a 100644 (file)
@@ -309,6 +309,12 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
   // Map binding# to sampler location
   const auto& reflection = !newProgram ? currentProgram->GetReflection() : newProgram->GetReflection();
   const auto& samplers   = reflection.GetSamplers();
+
+  uint32_t currentSampler = 0;
+  uint32_t currentElement = 0;
+
+  // @warning Assume that binding.binding is strictly linear in the same order as mCurrentTextureBindings
+  // elements. This avoids having to sort the bindings.
   for(const auto& binding : mImpl->mCurrentTextureBindings)
   {
     auto texture = const_cast<GLES::Texture*>(static_cast<const GLES::Texture*>(binding.texture));
@@ -316,24 +322,28 @@ void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::
     // Texture may not have been initialized yet...(tbm_surface timing issue?)
     if(!texture->GetGLTexture())
     {
-      // Attempt to reinitialize
-      // @todo need to put this somewhere else where it isn't const.
-      // Maybe post it back on end of initialize queue if initialization fails?
       texture->InitializeResource();
     }
 
     // Warning, this may cause glWaitSync to occur on the GPU.
     dependencyChecker.CheckNeedsSync(this, texture);
-
     texture->Bind(binding);
-
-    texture->Prepare(); // @todo also non-const.
-
-    if(binding.binding < samplers.size()) // binding maps to texture unit. (texture bindings should also be in binding order)
+    texture->Prepare();
+
+    // @warning Assume that location of array elements is sequential.
+    // @warning GL does not guarantee this, but in practice, it is.
+    gl.Uniform1i(samplers[currentSampler].location + currentElement,
+                 samplers[currentSampler].offset + currentElement);
+    ++currentElement;
+    if(currentElement >= samplers[currentSampler].elementCount)
+    {
+      ++currentSampler;
+      currentElement = 0;
+    }
+    if(currentSampler >= samplers.size())
     {
-      // Offset is set to the lexical offset within the frag shader, map it to the texture unit
-      // @todo Explicitly set the texture unit through the graphics interface
-      gl.Uniform1i(samplers[binding.binding].location, samplers[binding.binding].offset);
+      // Don't bind more textures than there are active samplers.
+      break;
     }
   }
 
index ee7299a..04247c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -60,7 +60,8 @@ bool operator==(const StringSize& lhs, const char* rhs)
   return strncmp(lhs.mString, rhs, lhs.mLength) == 0;
 }
 
-const char* const    DELIMITERS = " \t\n";
+const char* const    DELIMITERS           = " \t\n";
+const char* const    DELIMITERS_INC_INDEX = " \t\n[]";
 constexpr StringSize UNIFORM{"uniform"};
 constexpr StringSize SAMPLER_PREFIX{"sampler"};
 constexpr StringSize SAMPLER_TYPES[]   = {"2D", "Cube", "ExternalOES"};
@@ -148,27 +149,50 @@ void ParseShaderSamplers(std::string shaderSource, std::vector<Dali::Graphics::U
 
     while(uniform)
     {
+      // From "uniform" to ";", not ignoring comments.
       char* outerToken = strtok_r(uniform + UNIFORM.mLength, ";", &uniform);
 
       char* nextPtr = nullptr;
       char* token   = strtok_r(outerToken, DELIMITERS, &nextPtr);
       while(token)
       {
+        // Ignore any token up to "sampler"
         if(SAMPLER_PREFIX == token)
         {
           token += SAMPLER_PREFIX.mLength;
           if(std::find(SAMPLER_TYPES, END_SAMPLER_TYPES, token) != END_SAMPLER_TYPES)
           {
             bool found(false);
-            token = strtok_r(nullptr, DELIMITERS, &nextPtr);
+            // We now are at next token after "samplerxxx" in outerToken token "stream"
+
+            // Does it use array notation?
+            int  arraySize = 0; // 0 = No array
+            auto iter      = std::string(token).find("[", 0);
+            if(iter != std::string::npos)
+            {
+              // Get Array size from source. (Warning, may be higher than GetActiveUniform suggests)
+              iter++;
+              arraySize = int(strtol(token + iter, nullptr, 0));
+            }
+
+            token = strtok_r(nullptr, DELIMITERS_INC_INDEX, &nextPtr); // " ", "\t", "\n", "[", "]"
 
             for(uint32_t i = 0; i < static_cast<uint32_t>(uniformOpaques.size()); ++i)
             {
               if(samplerPositions[i] == -1 &&
                  strncmp(token, uniformOpaques[i].name.c_str(), uniformOpaques[i].name.size()) == 0)
               {
-                samplerPositions[i] = uniformOpaques[i].offset = samplerPosition++;
-                found                                          = true;
+                // We have found a matching name.
+                samplerPositions[i] = uniformOpaques[i].offset = samplerPosition;
+                if(arraySize == 0)
+                {
+                  ++samplerPosition;
+                }
+                else
+                {
+                  samplerPosition += arraySize;
+                }
+                found = true;
                 break;
               }
             }
@@ -289,6 +313,7 @@ void Reflection::BuildUniformReflection()
     GLenum type;
     int    written;
     gl->GetActiveUniform(glProgram, i, maxLen, &written, &elementCount, &type, name);
+
     int location = gl->GetUniformLocation(glProgram, name);
 
     Dali::Graphics::UniformInfo uniformInfo;
@@ -296,16 +321,21 @@ void Reflection::BuildUniformReflection()
     uniformInfo.name = name;
     if(elementCount > 1)
     {
+      // If we have an active uniform that refers to an array, only the first element
+      // is present in this list, and is referenced as "uniform[0]", but the element
+      // count is non-zero to indicate how many uniforms there are in the array.
+
+      // Strip off the array, but store the element count
       auto iter = std::string(uniformInfo.name).find("[", 0);
       if(iter != std::string::npos)
       {
-        uniformInfo.name = std::string(name).substr(0, iter);
+        uniformInfo.name         = std::string(name).substr(0, iter);
+        uniformInfo.elementCount = elementCount;
       }
     }
-
     uniformInfo.uniformClass = IsSampler(type) ? Dali::Graphics::UniformClass::COMBINED_IMAGE_SAMPLER : Dali::Graphics::UniformClass::UNIFORM;
-    uniformInfo.location     = location; //IsSampler(type) ? 0 : location;
-    uniformInfo.binding      = 0;        // IsSampler(type) ? location : 0;
+    uniformInfo.location     = location; // GL doesn't guarantee that consecutive array elements have sequential locations. But, we only store location of first element.
+    uniformInfo.binding      = 0;
     uniformInfo.bufferIndex  = 0;
     uniformInfo.offset       = 0;