panfrost: Prepare shader_meta descriptors at emission time
[mesa.git] / src / gallium / drivers / panfrost / pan_blend_cso.c
1 /*
2 * Copyright (C) 2019 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * 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 NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors (Collabora):
24 * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
25 *
26 */
27
28 #include <stdio.h>
29 #include "util/u_memory.h"
30 #include "pan_blend_shaders.h"
31 #include "pan_blending.h"
32 #include "pan_bo.h"
33 #include "panfrost-quirks.h"
34
35 /* A given Gallium blend state can be encoded to the hardware in numerous,
36 * dramatically divergent ways due to the interactions of blending with
37 * framebuffer formats. Conceptually, there are two modes:
38 *
39 * - Fixed-function blending (for suitable framebuffer formats, suitable blend
40 * state, and suitable blend constant)
41 *
42 * - Blend shaders (for everything else)
43 *
44 * A given Gallium blend configuration will compile to exactly one
45 * fixed-function blend state, if it compiles to any, although the constant
46 * will vary across runs as that is tracked outside of the Gallium CSO.
47 *
48 * However, that same blend configuration will compile to many different blend
49 * shaders, depending on the framebuffer formats active. The rationale is that
50 * blend shaders override not just fixed-function blending but also
51 * fixed-function format conversion. As such, each blend shader must be
52 * hardcoded to a particular framebuffer format to correctly pack/unpack it. As
53 * a concrete example, to the hardware there is no difference (!) between RG16F
54 * and RG16UI -- both are simply 4-byte-per-pixel chunks. Thus both formats
55 * require a blend shader (even with blending is totally disabled!), required
56 * to do conversion as necessary (if necessary).
57 *
58 * All of this state is encapsulated in the panfrost_blend_state struct
59 * (our subclass of pipe_blend_state).
60 */
61
62 /* Given an initialized CSO and a particular framebuffer format, grab a
63 * blend shader, generating and compiling it if it doesn't exist
64 * (lazy-loading in a way). This routine, when the cache hits, should
65 * befast, suitable for calling every draw to avoid wacky dirty
66 * tracking paths. If the cache hits, boom, done. */
67
68 static struct panfrost_blend_shader *
69 panfrost_get_blend_shader(
70 struct panfrost_context *ctx,
71 struct panfrost_blend_state *blend,
72 enum pipe_format fmt,
73 unsigned rt)
74 {
75 /* Prevent NULL collision issues.. */
76 assert(fmt != 0);
77
78 /* Check the cache. Key by the RT and format */
79 struct hash_table_u64 *shaders = blend->rt[rt].shaders;
80 unsigned key = (fmt << 3) | rt;
81
82 struct panfrost_blend_shader *shader =
83 _mesa_hash_table_u64_search(shaders, key);
84
85 if (shader)
86 return shader;
87
88 /* Cache miss. Build one instead, cache it, and go */
89
90 struct panfrost_blend_shader generated =
91 panfrost_compile_blend_shader(ctx, &blend->base, fmt, rt);
92
93 shader = mem_dup(&generated, sizeof(generated));
94 _mesa_hash_table_u64_insert(shaders, key, shader);
95 return shader;
96 }
97
98 /* Create a blend CSO. Essentially, try to compile a fixed-function
99 * expression and initialize blend shaders */
100
101 static void *
102 panfrost_create_blend_state(struct pipe_context *pipe,
103 const struct pipe_blend_state *blend)
104 {
105 struct panfrost_context *ctx = pan_context(pipe);
106 struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
107 so->base = *blend;
108
109 /* TODO: The following features are not yet implemented */
110 assert(!blend->alpha_to_coverage);
111 assert(!blend->alpha_to_one);
112
113 for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
114 struct panfrost_blend_rt *rt = &so->rt[c];
115
116 /* There are two paths. First, we would like to try a
117 * fixed-function if we can */
118
119 /* Without indep blending, the first RT settings replicate */
120
121 if (!blend->logicop_enable) {
122 unsigned g =
123 blend->independent_blend_enable ? c : 0;
124
125 rt->has_fixed_function =
126 panfrost_make_fixed_blend_mode(
127 &blend->rt[g],
128 &rt->equation,
129 &rt->constant_mask,
130 blend->rt[g].colormask);
131 }
132
133 /* Regardless if that works, we also need to initialize
134 * the blend shaders */
135
136 rt->shaders = _mesa_hash_table_u64_create(so);
137 }
138
139 return so;
140 }
141
142 static void
143 panfrost_bind_blend_state(struct pipe_context *pipe,
144 void *cso)
145 {
146 struct panfrost_context *ctx = pan_context(pipe);
147 struct pipe_blend_state *blend = (struct pipe_blend_state *) cso;
148 struct panfrost_blend_state *pblend = (struct panfrost_blend_state *) cso;
149 ctx->blend = pblend;
150
151 if (!blend)
152 return;
153 }
154
155 static void
156 panfrost_delete_blend_shader(struct hash_entry *entry)
157 {
158 struct panfrost_blend_shader *shader = (struct panfrost_blend_shader *)entry->data;
159 free(shader->buffer);
160 free(shader);
161 }
162
163 static void
164 panfrost_delete_blend_state(struct pipe_context *pipe,
165 void *cso)
166 {
167 struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso;
168
169 for (unsigned c = 0; c < 4; ++c) {
170 struct panfrost_blend_rt *rt = &blend->rt[c];
171 _mesa_hash_table_u64_clear(rt->shaders, panfrost_delete_blend_shader);
172 }
173 ralloc_free(blend);
174 }
175
176 static void
177 panfrost_set_blend_color(struct pipe_context *pipe,
178 const struct pipe_blend_color *blend_color)
179 {
180 struct panfrost_context *ctx = pan_context(pipe);
181
182 if (blend_color)
183 ctx->blend_color = *blend_color;
184 }
185
186 /* Given a vec4 of constants, reduce it to just a single constant according to
187 * the mask (if we can) */
188
189 static bool
190 panfrost_blend_constant(float *out, float *in, unsigned mask)
191 {
192 /* If there is no components used, it automatically works. Do set a
193 * dummy constant just to avoid reading uninitialized memory. */
194
195 if (!mask) {
196 *out = 0.0;
197 return true;
198 }
199
200 /* Find some starter mask */
201 unsigned first = ffs(mask) - 1;
202 float cons = in[first];
203 mask ^= (1 << first);
204
205 /* Ensure the rest are equal */
206 while (mask) {
207 unsigned i = u_bit_scan(&mask);
208
209 if (in[i] != cons) {
210 *out = 0.0;
211 return false;
212 }
213 }
214
215 /* Otherwise, we're good to go */
216 *out = cons;
217 return true;
218 }
219
220 /* Create a final blend given the context */
221
222 struct panfrost_blend_final
223 panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti, struct panfrost_bo **bo, unsigned *shader_offset)
224 {
225 struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);
226
227 /* Grab the format, falling back gracefully if called invalidly (which
228 * has to happen for no-color-attachment FBOs, for instance) */
229 struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
230 enum pipe_format fmt = PIPE_FORMAT_R8G8B8A8_UNORM;
231
232 if ((fb->nr_cbufs > rti) && fb->cbufs[rti])
233 fmt = fb->cbufs[rti]->format;
234
235 /* Grab the blend state */
236 struct panfrost_blend_state *blend = ctx->blend;
237 assert(blend);
238
239 struct panfrost_blend_rt *rt = &blend->rt[rti];
240
241 struct panfrost_blend_final final;
242
243 /* First, we'll try a fixed function path */
244 if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
245 if (panfrost_blend_constant(
246 &final.equation.constant,
247 ctx->blend_color.color,
248 rt->constant_mask)) {
249 /* There's an equation and suitable constant, so we're good to go */
250 final.is_shader = false;
251 final.equation.equation = &rt->equation;
252
253 final.no_blending =
254 (rt->equation.rgb_mode == 0x122) &&
255 (rt->equation.alpha_mode == 0x122) &&
256 (rt->equation.color_mask == 0xf);
257
258 return final;
259 }
260 }
261
262 /* Otherwise, we need to grab a shader */
263 struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
264 final.is_shader = true;
265 final.no_blending = false;
266 final.shader.work_count = shader->work_count;
267 final.shader.first_tag = shader->first_tag;
268
269 /* Upload the shader, sharing a BO */
270 if (!(*bo)) {
271 *bo = panfrost_batch_create_bo(batch, 4096,
272 PAN_BO_EXECUTE,
273 PAN_BO_ACCESS_PRIVATE |
274 PAN_BO_ACCESS_READ |
275 PAN_BO_ACCESS_VERTEX_TILER |
276 PAN_BO_ACCESS_FRAGMENT);
277 }
278
279 /* Size check */
280 assert((*shader_offset + shader->size) < 4096);
281
282 memcpy((*bo)->cpu + *shader_offset, shader->buffer, shader->size);
283 final.shader.gpu = (*bo)->gpu + *shader_offset;
284
285 if (shader->patch_index) {
286 /* We have to specialize the blend shader to use constants, so
287 * patch in the current constants */
288
289 float *patch = (float *) ((*bo)->cpu + *shader_offset + shader->patch_index);
290 memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
291 }
292
293 *shader_offset += shader->size;
294
295 return final;
296 }
297
298 void
299 panfrost_blend_context_init(struct pipe_context *pipe)
300 {
301 pipe->create_blend_state = panfrost_create_blend_state;
302 pipe->bind_blend_state = panfrost_bind_blend_state;
303 pipe->delete_blend_state = panfrost_delete_blend_state;
304
305 pipe->set_blend_color = panfrost_set_blend_color;
306 }