nir/drawpixels: handle load_color0, load_input, load_interpolated_input
[mesa.git] / src / compiler / glsl / gl_nir_linker.c
index 547549bc4e072ed2553eef139a920c68b0734baf..8b5ff28a3996a1dcfe8b1cc011700daeb2a92358 100644 (file)
  * Also note that this is tailored for ARB_gl_spirv needs and particularities
  */
 
+static bool
+add_interface_variables(const struct gl_context *cts,
+                        struct gl_shader_program *prog,
+                        struct set *resource_set,
+                        unsigned stage, GLenum programInterface)
+{
+   const struct exec_list *var_list = NULL;
+
+   struct gl_linked_shader *sh = prog->_LinkedShaders[stage];
+   if (!sh)
+      return true;
+
+   nir_shader *nir = sh->Program->nir;
+   assert(nir);
+
+   switch (programInterface) {
+   case GL_PROGRAM_INPUT:
+      var_list = &nir->inputs;
+      break;
+   case GL_PROGRAM_OUTPUT:
+      var_list = &nir->outputs;
+      break;
+   default:
+      assert("!Should not get here");
+      break;
+   }
+
+   nir_foreach_variable(var, var_list) {
+      if (var->data.how_declared == nir_var_hidden)
+         continue;
+
+      int loc_bias = 0;
+      switch(var->data.mode) {
+      case nir_var_system_value:
+      case nir_var_shader_in:
+         if (programInterface != GL_PROGRAM_INPUT)
+            continue;
+         loc_bias = (stage == MESA_SHADER_VERTEX) ? VERT_ATTRIB_GENERIC0
+                                                  : VARYING_SLOT_VAR0;
+         break;
+      case nir_var_shader_out:
+         if (programInterface != GL_PROGRAM_OUTPUT)
+            continue;
+         loc_bias = (stage == MESA_SHADER_FRAGMENT) ? FRAG_RESULT_DATA0
+                                                    : VARYING_SLOT_VAR0;
+         break;
+      default:
+         continue;
+      }
+
+      if (var->data.patch)
+         loc_bias = VARYING_SLOT_PATCH0;
+
+      struct gl_shader_variable *sh_var =
+         rzalloc(prog, struct gl_shader_variable);
+
+      /* In the ARB_gl_spirv spec, names are considered optional debug info, so
+       * the linker needs to work without them. Returning them is optional.
+       * For simplicity, we ignore names.
+       */
+      sh_var->name = NULL;
+      sh_var->type = var->type;
+      sh_var->location = var->data.location - loc_bias;
+      sh_var->index = var->data.index;
+
+      if (!link_util_add_program_resource(prog, resource_set,
+                                          programInterface,
+                                          sh_var, 1 << stage)) {
+         return false;
+      }
+   }
+
+   return true;
+}
+
+/* TODO: as we keep adding features, this method is becoming more and more
+ * similar to its GLSL counterpart at linker.cpp. Eventually it would be good
+ * to check if they could be refactored, and reduce code duplication somehow
+ */
 void
 nir_build_program_resource_list(struct gl_context *ctx,
                                 struct gl_shader_program *prog)
@@ -44,15 +123,73 @@ nir_build_program_resource_list(struct gl_context *ctx,
       prog->data->NumProgramResourceList = 0;
    }
 
-   struct set *resource_set = _mesa_set_create(NULL,
-                                               _mesa_hash_pointer,
-                                               _mesa_key_pointer_equal);
+   int input_stage = MESA_SHADER_STAGES, output_stage = 0;
+
+   /* Determine first input and final output stage. These are used to
+    * detect which variables should be enumerated in the resource list
+    * for GL_PROGRAM_INPUT and GL_PROGRAM_OUTPUT.
+    */
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      if (!prog->_LinkedShaders[i])
+         continue;
+      if (input_stage == MESA_SHADER_STAGES)
+         input_stage = i;
+      output_stage = i;
+   }
+
+   /* Empty shader, no resources. */
+   if (input_stage == MESA_SHADER_STAGES && output_stage == 0)
+      return;
+
+   struct set *resource_set = _mesa_pointer_set_create(NULL);
+
+   /* Add inputs and outputs to the resource list. */
+   if (!add_interface_variables(ctx, prog, resource_set, input_stage,
+                                GL_PROGRAM_INPUT)) {
+      return;
+   }
+
+   if (!add_interface_variables(ctx, prog, resource_set, output_stage,
+                                GL_PROGRAM_OUTPUT)) {
+      return;
+   }
+
+   /* Add transform feedback varyings and buffers. */
+   if (prog->last_vert_prog) {
+      struct gl_transform_feedback_info *linked_xfb =
+         prog->last_vert_prog->sh.LinkedTransformFeedback;
+
+      /* Add varyings. */
+      if (linked_xfb->NumVarying > 0) {
+         for (int i = 0; i < linked_xfb->NumVarying; i++) {
+            if (!link_util_add_program_resource(prog, resource_set,
+                                                GL_TRANSFORM_FEEDBACK_VARYING,
+                                                &linked_xfb->Varyings[i], 0))
+            return;
+         }
+      }
+
+      /* Add buffers. */
+      for (unsigned i = 0; i < ctx->Const.MaxTransformFeedbackBuffers; i++) {
+         if ((linked_xfb->ActiveBuffers >> i) & 1) {
+            linked_xfb->Buffers[i].Binding = i;
+            if (!link_util_add_program_resource(prog, resource_set,
+                                                GL_TRANSFORM_FEEDBACK_BUFFER,
+                                                &linked_xfb->Buffers[i], 0))
+            return;
+         }
+      }
+   }
 
    /* Add uniforms
     *
     * Here, it is expected that nir_link_uniforms() has already been
     * called, so that UniformStorage table is already available.
     */
+   int top_level_array_base_offset = -1;
+   int top_level_array_size_in_bytes = -1;
+   int second_element_offset = -1;
+   int block_index = -1;
    for (unsigned i = 0; i < prog->data->NumUniformStorage; i++) {
       struct gl_uniform_storage *uniform = &prog->data->UniformStorage[i];
 
@@ -60,12 +197,75 @@ nir_build_program_resource_list(struct gl_context *ctx,
       if (uniform->hidden)
          continue;
 
-      if (!link_util_add_program_resource(prog, resource_set, GL_UNIFORM, uniform,
+      if (!link_util_should_add_buffer_variable(prog, uniform,
+                                                top_level_array_base_offset,
+                                                top_level_array_size_in_bytes,
+                                                second_element_offset, block_index))
+         continue;
+
+
+      if (prog->data->UniformStorage[i].offset >= second_element_offset) {
+         top_level_array_base_offset =
+            prog->data->UniformStorage[i].offset;
+
+         top_level_array_size_in_bytes =
+            prog->data->UniformStorage[i].top_level_array_size *
+            prog->data->UniformStorage[i].top_level_array_stride;
+
+         /* Set or reset the second element offset. For non arrays this
+          * will be set to -1.
+          */
+         second_element_offset = top_level_array_size_in_bytes ?
+            top_level_array_base_offset +
+            prog->data->UniformStorage[i].top_level_array_stride : -1;
+      }
+      block_index = uniform->block_index;
+
+
+      GLenum interface = uniform->is_shader_storage ? GL_BUFFER_VARIABLE : GL_UNIFORM;
+      if (!link_util_add_program_resource(prog, resource_set, interface, uniform,
                                           uniform->active_shader_mask)) {
          return;
       }
    }
 
 
+   for (unsigned i = 0; i < prog->data->NumUniformBlocks; i++) {
+      if (!link_util_add_program_resource(prog, resource_set, GL_UNIFORM_BLOCK,
+                                          &prog->data->UniformBlocks[i],
+                                          prog->data->UniformBlocks[i].stageref))
+         return;
+   }
+
+   for (unsigned i = 0; i < prog->data->NumShaderStorageBlocks; i++) {
+      if (!link_util_add_program_resource(prog, resource_set, GL_SHADER_STORAGE_BLOCK,
+                                          &prog->data->ShaderStorageBlocks[i],
+                                          prog->data->ShaderStorageBlocks[i].stageref))
+         return;
+   }
+
+   /* Add atomic counter buffers. */
+   for (unsigned i = 0; i < prog->data->NumAtomicBuffers; i++) {
+      if (!link_util_add_program_resource(prog, resource_set, GL_ATOMIC_COUNTER_BUFFER,
+                                          &prog->data->AtomicBuffers[i], 0))
+         return;
+   }
+
    _mesa_set_destroy(resource_set, NULL);
 }
+
+bool
+gl_nir_link(struct gl_context *ctx, struct gl_shader_program *prog,
+            const struct gl_nir_linker_options *options)
+{
+   if (!gl_nir_link_uniform_blocks(ctx, prog))
+      return false;
+
+   if (!gl_nir_link_uniforms(ctx, prog, options->fill_parameters))
+      return false;
+
+   gl_nir_link_assign_atomic_counter_resources(ctx, prog);
+   gl_nir_link_assign_xfb_resources(ctx, prog);
+
+   return true;
+}