anv: Add better push constant support
authorJason Ekstrand <jason.ekstrand@intel.com>
Fri, 30 Oct 2015 05:24:54 +0000 (22:24 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Fri, 30 Oct 2015 05:26:36 +0000 (22:26 -0700)
What we had before was kind of a hack where we made certain untrue
assumptions about the incoming data.  This new support, while it still
doesn't support indirects properly (that will come), at least pulls the
offsets and strides from SPIR-V like it's supposed to.

src/vulkan/Makefile.am
src/vulkan/anv_nir.h
src/vulkan/anv_nir_lower_push_constants.c [new file with mode: 0644]
src/vulkan/anv_pipeline.c

index 475fa4f2ad2723ea7638ada0429877ae126db38f..0d6c9df6b67de3c949192adf8400637a71182695 100644 (file)
@@ -69,6 +69,7 @@ VULKAN_SOURCES =                                        \
        anv_meta.c                                      \
        anv_nir_apply_dynamic_offsets.c                 \
        anv_nir_apply_pipeline_layout.c                 \
+       anv_nir_lower_push_constants.c                  \
        anv_pass.c                                      \
        anv_pipeline.c                                  \
        anv_private.h                                   \
index af95e3a8849434a94e536157b51adafbea268b71..b164ae581e1b358f3b31669cb8a5f85c31d08e9f 100644 (file)
@@ -37,6 +37,8 @@ anv_vk_shader_stage_for_mesa_stage(gl_shader_stage stage)
    return (VkShaderStage)(int)stage;
 }
 
+void anv_nir_lower_push_constants(nir_shader *shader, bool is_scalar);
+
 void anv_nir_apply_dynamic_offsets(struct anv_pipeline *pipeline,
                                    nir_shader *shader,
                                    struct brw_stage_prog_data *prog_data);
diff --git a/src/vulkan/anv_nir_lower_push_constants.c b/src/vulkan/anv_nir_lower_push_constants.c
new file mode 100644 (file)
index 0000000..af48470
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "anv_nir.h"
+
+struct lower_push_constants_state {
+   nir_shader *shader;
+   bool is_scalar;
+};
+
+static bool
+lower_push_constants_block(nir_block *block, void *void_state)
+{
+   struct lower_push_constants_state *state = void_state;
+
+   nir_foreach_instr(block, instr) {
+      if (instr->type != nir_instr_type_intrinsic)
+         continue;
+
+      nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
+
+      /* TODO: Handle indirect push constants */
+      if (intrin->intrinsic != nir_intrinsic_load_push_constant)
+         continue;
+
+      assert(intrin->const_index[0] % 4 == 0);
+      unsigned dword_offset = intrin->const_index[0] / 4;
+
+      /* We just turn them into uniform loads with the appropreate offset */
+      intrin->intrinsic = nir_intrinsic_load_uniform;
+      intrin->const_index[0] = 0;
+      if (state->is_scalar) {
+         intrin->const_index[1] = dword_offset;
+      } else {
+         unsigned shift = dword_offset % 4;
+         /* Can't cross the vec4 boundary */
+         assert(shift + intrin->num_components <= 4);
+
+         /* vec4 shifts are in units of vec4's */
+         intrin->const_index[1] = dword_offset / 4;
+
+         if (shift) {
+            /* If there's a non-zero shift then we need to load a whole vec4
+             * and use a move to swizzle it into place.
+             */
+            assert(intrin->dest.is_ssa);
+            nir_alu_instr *mov = nir_alu_instr_create(state->shader,
+                                                      nir_op_imov);
+            mov->src[0].src = nir_src_for_ssa(&intrin->dest.ssa);
+            for (unsigned i = 0; i < intrin->num_components; i++)
+               mov->src[0].swizzle[i] = i + shift;
+            mov->dest.write_mask = (1 << intrin->num_components) - 1;
+            nir_ssa_dest_init(&intrin->instr, &intrin->dest,
+                              intrin->num_components, NULL);
+
+            nir_ssa_def_rewrite_uses(&intrin->dest.ssa,
+                                     nir_src_for_ssa(&mov->dest.dest.ssa));
+            nir_instr_insert_after(&intrin->instr, &mov->instr);
+
+            /* Stomp the number of components to 4 */
+            intrin->num_components = 4;
+         }
+      }
+   }
+
+   return true;
+}
+
+void
+anv_nir_lower_push_constants(nir_shader *shader, bool is_scalar)
+{
+   struct lower_push_constants_state state = {
+      .shader = shader,
+      .is_scalar = is_scalar,
+   };
+
+   nir_foreach_overload(shader, overload) {
+      if (overload->impl)
+         nir_foreach_block(overload->impl, lower_push_constants_block, &state);
+   }
+
+   assert(shader->num_uniforms % 4 == 0);
+   if (is_scalar)
+      shader->num_uniforms /= 4;
+   else
+      shader->num_uniforms = DIV_ROUND_UP(shader->num_uniforms, 16);
+}
index 59f304f55df7830a585053d0ba3185c34b9f454c..9fb5ddba20ba7d3dd3fde04d4ece23b279b676a0 100644 (file)
@@ -357,22 +357,12 @@ anv_pipeline_compile(struct anv_pipeline *pipeline,
    if (nir == NULL)
       return NULL;
 
-   bool have_push_constants = false;
-   nir_foreach_variable(var, &nir->uniforms) {
-      const struct glsl_type *type = var->type;
-      if (glsl_type_is_array(type))
-         type = glsl_get_array_element(type);
-
-      if (!glsl_type_is_sampler(type)) {
-         have_push_constants = true;
-         break;
-      }
-   }
+   anv_nir_lower_push_constants(nir, is_scalar_shader_stage(compiler, stage));
 
    /* Figure out the number of parameters */
    prog_data->nr_params = 0;
 
-   if (have_push_constants) {
+   if (nir->num_uniforms > 0) {
       /* If the shader uses any push constants at all, we'll just give
        * them the maximum possible number
        */
@@ -394,7 +384,7 @@ anv_pipeline_compile(struct anv_pipeline *pipeline,
        * params array, it doesn't really matter what we put here.
        */
       struct anv_push_constants *null_data = NULL;
-      if (have_push_constants) {
+      if (nir->num_uniforms > 0) {
          /* Fill out the push constants section of the param array */
          for (unsigned i = 0; i < MAX_PUSH_CONSTANTS_SIZE / sizeof(float); i++)
             prog_data->param[i] = (const gl_constant_value *)