panfrost: Simplify bind_blend_state
[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 "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 *
70 panfrost_get_blend_shader(
71 struct panfrost_context *ctx,
72 struct panfrost_blend_state *blend,
73 enum pipe_format fmt,
74 unsigned rt)
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_u64 *shaders = blend->rt[rt].shaders;
81 unsigned key = (fmt << 3) | rt;
82
83 struct panfrost_blend_shader *shader =
84 _mesa_hash_table_u64_search(shaders, key);
85
86 if (shader)
87 return shader;
88
89 /* Cache miss. Build one instead, cache it, and go */
90
91 struct panfrost_blend_shader generated =
92 panfrost_compile_blend_shader(ctx, &blend->base, fmt, rt);
93
94 shader = mem_dup(&generated, sizeof(generated));
95 _mesa_hash_table_u64_insert(shaders, key, shader);
96 return shader;
97 }
98
99 /* Create a blend CSO. Essentially, try to compile a fixed-function
100 * expression and initialize blend shaders */
101
102 static void *
103 panfrost_create_blend_state(struct pipe_context *pipe,
104 const struct pipe_blend_state *blend)
105 {
106 struct panfrost_context *ctx = pan_context(pipe);
107 struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
108 so->base = *blend;
109
110 /* TODO: The following features are not yet implemented */
111 assert(!blend->alpha_to_one);
112
113 for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
114 unsigned g = blend->independent_blend_enable ? c : 0;
115 struct pipe_rt_blend_state pipe = blend->rt[g];
116
117 struct panfrost_blend_rt *rt = &so->rt[c];
118 rt->shaders = _mesa_hash_table_u64_create(so);
119
120 /* Logic ops are always shader */
121 if (blend->logicop_enable) {
122 rt->load_dest = true;
123 continue;
124 }
125
126 rt->has_fixed_function =
127 panfrost_make_fixed_blend_mode(
128 pipe,
129 &rt->equation,
130 &rt->constant_mask);
131
132 if (rt->has_fixed_function)
133 rt->opaque = (rt->equation.opaque[0] == 0xf0122122);
134
135 rt->load_dest = util_blend_uses_dest(pipe)
136 || pipe.colormask != 0xF;
137
138 rt->no_colour = pipe.colormask == 0x0;
139 }
140
141 return so;
142 }
143
144 static void
145 panfrost_bind_blend_state(struct pipe_context *pipe,
146 void *cso)
147 {
148 struct panfrost_context *ctx = pan_context(pipe);
149 ctx->blend = (struct panfrost_blend_state *) cso;
150 }
151
152 static void
153 panfrost_delete_blend_shader(struct hash_entry *entry)
154 {
155 struct panfrost_blend_shader *shader = (struct panfrost_blend_shader *)entry->data;
156 free(shader->buffer);
157 free(shader);
158 }
159
160 static void
161 panfrost_delete_blend_state(struct pipe_context *pipe,
162 void *cso)
163 {
164 struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso;
165
166 for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
167 struct panfrost_blend_rt *rt = &blend->rt[c];
168 _mesa_hash_table_u64_clear(rt->shaders, panfrost_delete_blend_shader);
169 }
170 ralloc_free(blend);
171 }
172
173 static void
174 panfrost_set_blend_color(struct pipe_context *pipe,
175 const struct pipe_blend_color *blend_color)
176 {
177 struct panfrost_context *ctx = pan_context(pipe);
178
179 if (blend_color)
180 ctx->blend_color = *blend_color;
181 }
182
183 /* Given a vec4 of constants, reduce it to just a single constant according to
184 * the mask (if we can) */
185
186 static bool
187 panfrost_blend_constant(float *out, float *in, unsigned mask)
188 {
189 /* If there is no components used, it automatically works */
190
191 if (!mask)
192 return true;
193
194 /* Find some starter mask */
195 unsigned first = ffs(mask) - 1;
196 float cons = in[first];
197 mask ^= (1 << first);
198
199 /* Ensure the rest are equal */
200 while (mask) {
201 unsigned i = u_bit_scan(&mask);
202
203 if (in[i] != cons)
204 return false;
205 }
206
207 /* Otherwise, we're good to go */
208 *out = cons;
209 return true;
210 }
211
212 /* Create a final blend given the context */
213
214 struct panfrost_blend_final
215 panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti)
216 {
217 struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx);
218 struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
219 enum pipe_format fmt = fb->cbufs[rti]->format;
220
221 /* Grab the blend state */
222 struct panfrost_blend_state *blend = ctx->blend;
223 struct panfrost_blend_rt *rt = &blend->rt[rti];
224
225 /* First, we'll try fixed function, matching equationn and constant */
226 if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
227 float constant = 0.0;
228
229 if (panfrost_blend_constant(
230 &constant,
231 ctx->blend_color.color,
232 rt->constant_mask)) {
233 struct panfrost_blend_final final = {
234 .equation = {
235 .equation = rt->equation,
236 .constant = constant
237 },
238 .load_dest = rt->load_dest,
239 .opaque = rt->opaque,
240 .no_colour = rt->no_colour
241 };
242
243 return final;
244 }
245 }
246
247 /* Otherwise, we need to grab a shader */
248 struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
249
250 struct panfrost_bo *bo = panfrost_batch_create_bo(batch, shader->size,
251 PAN_BO_EXECUTE,
252 PAN_BO_ACCESS_PRIVATE |
253 PAN_BO_ACCESS_READ |
254 PAN_BO_ACCESS_FRAGMENT);
255
256 memcpy(bo->cpu, shader->buffer, shader->size);
257
258 if (shader->patch_index) {
259 /* We have to specialize the blend shader to use constants, so
260 * patch in the current constants */
261
262 float *patch = (float *) (bo->cpu + shader->patch_index);
263 memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
264 }
265
266 struct panfrost_blend_final final = {
267 .is_shader = true,
268 .shader = {
269 .work_count = shader->work_count,
270 .first_tag = shader->first_tag,
271 .gpu = bo->gpu,
272 },
273 .load_dest = rt->load_dest,
274 };
275
276 return final;
277 }
278
279 void
280 panfrost_blend_context_init(struct pipe_context *pipe)
281 {
282 pipe->create_blend_state = panfrost_create_blend_state;
283 pipe->bind_blend_state = panfrost_bind_blend_state;
284 pipe->delete_blend_state = panfrost_delete_blend_state;
285
286 pipe->set_blend_color = panfrost_set_blend_color;
287 }