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 "gallium/auxiliary/util/u_blend.h"
31 #include "pan_blend_shaders.h"
32 #include "pan_blending.h"
33 #include "pan_bo.h"
34 #include "panfrost-quirks.h"
35 
36 /* A given Gallium blend state can be encoded to the hardware in numerous,
37  * dramatically divergent ways due to the interactions of blending with
38  * framebuffer formats. Conceptually, there are two modes:
39  *
40  * - Fixed-function blending (for suitable framebuffer formats, suitable blend
41  *   state, and suitable blend constant)
42  *
43  * - Blend shaders (for everything else)
44  *
45  * A given Gallium blend configuration will compile to exactly one
46  * fixed-function blend state, if it compiles to any, although the constant
47  * will vary across runs as that is tracked outside of the Gallium CSO.
48  *
49  * However, that same blend configuration will compile to many different blend
50  * shaders, depending on the framebuffer formats active. The rationale is that
51  * blend shaders override not just fixed-function blending but also
52  * fixed-function format conversion. As such, each blend shader must be
53  * hardcoded to a particular framebuffer format to correctly pack/unpack it. As
54  * a concrete example, to the hardware there is no difference (!) between RG16F
55  * and RG16UI -- both are simply 4-byte-per-pixel chunks. Thus both formats
56  * require a blend shader (even with blending is totally disabled!), required
57  * to do conversion as necessary (if necessary).
58  *
59  * All of this state is encapsulated in the panfrost_blend_state struct
60  * (our subclass of pipe_blend_state).
61  */
62 
63 /* Given an initialized CSO and a particular framebuffer format, grab a
64  * blend shader, generating and compiling it if it doesn't exist
65  * (lazy-loading in a way). This routine, when the cache hits, should
66  * befast, suitable for calling every draw to avoid wacky dirty
67  * tracking paths. If the cache hits, boom, done. */
68 
69 struct panfrost_blend_shader *
panfrost_get_blend_shader(struct panfrost_context * ctx,struct panfrost_blend_state * blend,enum pipe_format fmt,unsigned rt,const float * constants)70 panfrost_get_blend_shader(struct panfrost_context *ctx,
71                           struct panfrost_blend_state *blend,
72                           enum pipe_format fmt,
73                           unsigned rt,
74                           const float *constants)
75 {
76         /* Prevent NULL collision issues.. */
77         assert(fmt != 0);
78 
79         /* Check the cache. Key by the RT and format */
80         struct hash_table *shaders = ctx->blend_shaders;
81         struct panfrost_blend_shader_key key = {
82                 .rt = rt,
83                 .format = fmt,
84                 .has_constants = constants != NULL,
85                 .logicop_enable = blend->base.logicop_enable,
86         };
87 
88         if (blend->base.logicop_enable) {
89                 key.logicop_func = blend->base.logicop_func;
90         } else {
91                 unsigned idx = blend->base.independent_blend_enable ? rt : 0;
92 
93                 if (blend->base.rt[idx].blend_enable)
94                         key.equation = blend->base.rt[idx];
95         }
96 
97         struct hash_entry *he = _mesa_hash_table_search(shaders, &key);
98         struct panfrost_blend_shader *shader = he ? he->data : NULL;
99 
100         if (!shader) {
101                 /* Cache miss. Build one instead, cache it, and go */
102                 shader = panfrost_create_blend_shader(ctx, blend, &key);
103                 _mesa_hash_table_insert(shaders, &shader->key, shader);
104         }
105 
106         panfrost_compile_blend_shader(shader, constants);
107         return shader;
108 }
109 
110 /* Create a blend CSO. Essentially, try to compile a fixed-function
111  * expression and initialize blend shaders */
112 
113 static void *
panfrost_create_blend_state(struct pipe_context * pipe,const struct pipe_blend_state * blend)114 panfrost_create_blend_state(struct pipe_context *pipe,
115                             const struct pipe_blend_state *blend)
116 {
117         struct panfrost_device *dev = pan_device(pipe->screen);
118         struct panfrost_context *ctx = pan_context(pipe);
119         struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
120         unsigned version = dev->gpu_id >> 12;
121         so->base = *blend;
122 
123         /* TODO: The following features are not yet implemented */
124         assert(!blend->alpha_to_one);
125 
126         for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
127                 unsigned g = blend->independent_blend_enable ? c : 0;
128                 struct pipe_rt_blend_state pipe = blend->rt[g];
129 
130                 struct panfrost_blend_rt *rt = &so->rt[c];
131 
132                 /* Logic ops are always shader */
133                 if (blend->logicop_enable) {
134                         rt->load_dest = true;
135                         continue;
136                 }
137 
138                 rt->constant_mask = panfrost_blend_constant_mask(&pipe);
139                 rt->has_fixed_function =
140                         panfrost_make_fixed_blend_mode(pipe, &rt->equation);
141 
142                 /* v6 doesn't support blend constants in FF blend equations. */
143                 if (rt->has_fixed_function && version == 6 && rt->constant_mask)
144                         rt->has_fixed_function = false;
145 
146                 if (rt->has_fixed_function) {
147                         rt->opaque = pipe.rgb_src_factor == PIPE_BLENDFACTOR_ONE &&
148                                      pipe.rgb_dst_factor == PIPE_BLENDFACTOR_ZERO &&
149                                      (pipe.rgb_func == PIPE_BLEND_ADD ||
150                                       pipe.rgb_func == PIPE_BLEND_SUBTRACT) &&
151                                      pipe.alpha_src_factor == PIPE_BLENDFACTOR_ONE &&
152                                      pipe.alpha_dst_factor == PIPE_BLENDFACTOR_ZERO &&
153                                      (pipe.alpha_func == PIPE_BLEND_ADD ||
154                                       pipe.alpha_func == PIPE_BLEND_SUBTRACT) &&
155                                      pipe.colormask == 0xf;
156                 }
157 
158                 rt->load_dest = util_blend_uses_dest(pipe)
159                         || pipe.colormask != 0xF;
160 
161                 rt->no_colour = pipe.colormask == 0x0;
162         }
163 
164         return so;
165 }
166 
167 static void
panfrost_bind_blend_state(struct pipe_context * pipe,void * cso)168 panfrost_bind_blend_state(struct pipe_context *pipe,
169                           void *cso)
170 {
171         struct panfrost_context *ctx = pan_context(pipe);
172         ctx->blend = (struct panfrost_blend_state *) cso;
173 }
174 
175 static void
panfrost_delete_blend_state(struct pipe_context * pipe,void * cso)176 panfrost_delete_blend_state(struct pipe_context *pipe,
177                             void *cso)
178 {
179         struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso;
180         ralloc_free(blend);
181 }
182 
183 static void
panfrost_set_blend_color(struct pipe_context * pipe,const struct pipe_blend_color * blend_color)184 panfrost_set_blend_color(struct pipe_context *pipe,
185                          const struct pipe_blend_color *blend_color)
186 {
187         struct panfrost_context *ctx = pan_context(pipe);
188 
189         if (blend_color)
190                 ctx->blend_color = *blend_color;
191 }
192 
193 /* Given a vec4 of constants, reduce it to just a single constant according to
194  * the mask (if we can) */
195 
196 static bool
panfrost_blend_constant(float * out,float * in,unsigned mask)197 panfrost_blend_constant(float *out, float *in, unsigned mask)
198 {
199         /* If there is no components used, it automatically works */
200 
201         if (!mask)
202                 return true;
203 
204         /* Find some starter mask */
205         unsigned first = ffs(mask) - 1;
206         float cons = in[first];
207         mask ^= (1 << first);
208 
209         /* Ensure the rest are equal */
210         while (mask) {
211                 unsigned i = u_bit_scan(&mask);
212 
213                 if (in[i] != cons)
214                         return false;
215         }
216 
217         /* Otherwise, we're good to go */
218         *out = cons;
219         return true;
220 }
221 
222 /* Create a final blend given the context */
223 
224 struct panfrost_blend_final
panfrost_get_blend_for_context(struct panfrost_context * ctx,unsigned rti,struct panfrost_bo ** bo,unsigned * shader_offset)225 panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti, struct panfrost_bo **bo, unsigned *shader_offset)
226 {
227         struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);
228         struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
229         enum pipe_format fmt = fb->cbufs[rti]->format;
230 
231         /* Grab the blend state */
232         struct panfrost_blend_state *blend = ctx->blend;
233         struct panfrost_blend_rt *rt = &blend->rt[rti];
234 
235         /* First, we'll try fixed function, matching equationn and constant */
236         if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
237                 float constant = 0.0;
238 
239                 if (panfrost_blend_constant(
240                             &constant,
241                             ctx->blend_color.color,
242                             rt->constant_mask)) {
243                         struct panfrost_blend_final final = {
244                                 .equation = {
245                                         .equation = rt->equation,
246                                         .constant = constant
247                                 },
248                                 .load_dest = rt->load_dest,
249                                 .opaque = rt->opaque,
250                                 .no_colour = rt->no_colour
251                         };
252 
253                         return final;
254                 }
255         }
256 
257         /* Otherwise, we need to grab a shader */
258         struct panfrost_blend_shader *shader =
259                 panfrost_get_blend_shader(ctx, blend, fmt, rti,
260                                           rt->constant_mask ?
261                                           ctx->blend_color.color : NULL);
262 
263         /* Upload the shader, sharing a BO */
264         if (!(*bo)) {
265                 *bo = panfrost_batch_create_bo(batch, 4096,
266                    PAN_BO_EXECUTE,
267                    PAN_BO_ACCESS_PRIVATE |
268                    PAN_BO_ACCESS_READ |
269                    PAN_BO_ACCESS_FRAGMENT);
270         }
271 
272         /* Size check */
273         assert((*shader_offset + shader->size) < 4096);
274 
275         memcpy((*bo)->ptr.cpu + *shader_offset, shader->buffer, shader->size);
276 
277         struct panfrost_blend_final final = {
278                 .is_shader = true,
279                 .shader = {
280                         .work_count = shader->work_count,
281                         .first_tag = shader->first_tag,
282                         .gpu = (*bo)->ptr.gpu + *shader_offset,
283                 },
284                 .load_dest = rt->load_dest,
285         };
286 
287         *shader_offset += shader->size;
288 
289         return final;
290 }
291 
292 void
panfrost_blend_context_init(struct pipe_context * pipe)293 panfrost_blend_context_init(struct pipe_context *pipe)
294 {
295         pipe->create_blend_state = panfrost_create_blend_state;
296         pipe->bind_blend_state   = panfrost_bind_blend_state;
297         pipe->delete_blend_state = panfrost_delete_blend_state;
298 
299         pipe->set_blend_color = panfrost_set_blend_color;
300 }
301