zink: cache those pipelines
[mesa.git] / src / gallium / drivers / zink / zink_program.c
1 /*
2 * Copyright 2018 Collabora Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "zink_program.h"
25
26 #include "zink_compiler.h"
27 #include "zink_context.h"
28 #include "zink_screen.h"
29
30 #include "util/hash_table.h"
31 #include "util/u_debug.h"
32 #include "util/u_memory.h"
33
34 static VkDescriptorSetLayout
35 create_desc_set_layout(VkDevice dev,
36 struct zink_shader *stages[PIPE_SHADER_TYPES - 1])
37 {
38 VkDescriptorSetLayoutBinding bindings[PIPE_SHADER_TYPES * PIPE_MAX_CONSTANT_BUFFERS];
39 int num_bindings = 0;
40
41 for (int i = 0; i < PIPE_SHADER_TYPES - 1; i++) {
42 struct zink_shader *shader = stages[i];
43 if (!shader)
44 continue;
45
46 VkShaderStageFlagBits stage_flags = zink_shader_stage(i);
47 for (int j = 0; j < shader->num_bindings; j++) {
48 assert(num_bindings < ARRAY_SIZE(bindings));
49 bindings[num_bindings].binding = shader->bindings[j].binding;
50 bindings[num_bindings].descriptorType = shader->bindings[j].type;
51 bindings[num_bindings].descriptorCount = 1;
52 bindings[num_bindings].stageFlags = stage_flags;
53 bindings[num_bindings].pImmutableSamplers = NULL;
54 ++num_bindings;
55 }
56 }
57
58 VkDescriptorSetLayoutCreateInfo dcslci = {};
59 dcslci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
60 dcslci.pNext = NULL;
61 dcslci.flags = 0;
62 dcslci.bindingCount = num_bindings;
63 dcslci.pBindings = bindings;
64
65 VkDescriptorSetLayout dsl;
66 if (vkCreateDescriptorSetLayout(dev, &dcslci, 0, &dsl) != VK_SUCCESS) {
67 debug_printf("vkCreateDescriptorSetLayout failed\n");
68 return VK_NULL_HANDLE;
69 }
70
71 return dsl;
72 }
73
74 static VkPipelineLayout
75 create_pipeline_layout(VkDevice dev, VkDescriptorSetLayout dsl)
76 {
77 assert(dsl != VK_NULL_HANDLE);
78
79 VkPipelineLayoutCreateInfo plci = {};
80 plci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
81
82 plci.pSetLayouts = &dsl;
83 plci.setLayoutCount = 1;
84
85 VkPipelineLayout layout;
86 if (vkCreatePipelineLayout(dev, &plci, NULL, &layout) != VK_SUCCESS) {
87 debug_printf("vkCreatePipelineLayout failed!\n");
88 return VK_NULL_HANDLE;
89 }
90
91 return layout;
92 }
93
94 static uint32_t
95 hash_gfx_pipeline_state(const void *key)
96 {
97 return _mesa_hash_data(key, sizeof(struct zink_gfx_pipeline_state));
98 }
99
100 static bool
101 equals_gfx_pipeline_state(const void *a, const void *b)
102 {
103 return memcmp(a, b, sizeof(struct zink_gfx_pipeline_state)) == 0;
104 }
105
106 struct zink_gfx_program *
107 zink_create_gfx_program(VkDevice dev,
108 struct zink_shader *stages[PIPE_SHADER_TYPES - 1])
109 {
110 struct zink_gfx_program *prog = CALLOC_STRUCT(zink_gfx_program);
111 if (!prog)
112 goto fail;
113
114 prog->pipelines = _mesa_hash_table_create(NULL, hash_gfx_pipeline_state,
115 equals_gfx_pipeline_state);
116 if (!prog->pipelines)
117 goto fail;
118
119 for (int i = 0; i < PIPE_SHADER_TYPES - 1; ++i)
120 prog->stages[i] = stages[i];
121
122 prog->dsl = create_desc_set_layout(dev, stages);
123 if (!prog->dsl)
124 goto fail;
125
126 prog->layout = create_pipeline_layout(dev, prog->dsl);
127 if (!prog->layout)
128 goto fail;
129
130 return prog;
131
132 fail:
133 if (prog)
134 zink_destroy_gfx_program(dev, prog);
135 return NULL;
136 }
137
138 void
139 zink_destroy_gfx_program(VkDevice dev, struct zink_gfx_program *prog)
140 {
141 if (prog->layout)
142 vkDestroyPipelineLayout(dev, prog->layout, NULL);
143
144 if (prog->dsl)
145 vkDestroyDescriptorSetLayout(dev, prog->dsl, NULL);
146
147 FREE(prog);
148 }
149
150 struct pipeline_cache_entry {
151 struct zink_gfx_pipeline_state state;
152 VkPipeline pipeline;
153 };
154
155 VkPipeline
156 zink_get_gfx_pipeline(VkDevice dev, struct zink_gfx_program *prog,
157 struct zink_gfx_pipeline_state *state)
158 {
159 /* TODO: use pre-hashed versions to save some time (can re-hash only when
160 state changes) */
161 struct hash_entry *entry = _mesa_hash_table_search(prog->pipelines, state);
162 if (!entry) {
163 VkPipeline pipeline = zink_create_gfx_pipeline(dev, prog, state);
164 if (pipeline == VK_NULL_HANDLE)
165 return VK_NULL_HANDLE;
166
167 struct pipeline_cache_entry *pc_entry = CALLOC_STRUCT(pipeline_cache_entry);
168 if (!pc_entry)
169 return NULL;
170
171 memcpy(&pc_entry->state, state, sizeof(*state));
172 pc_entry->pipeline = pipeline;
173
174 entry = _mesa_hash_table_insert(prog->pipelines, &pc_entry->state, pc_entry);
175 assert(entry);
176 }
177
178 return ((struct pipeline_cache_entry *)(entry->data))->pipeline;
179 }