a96e7b02cd4a2a04504e3a930ec6823534318c56
[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
33 /* A given Gallium blend state can be encoded to the hardware in numerous,
34 * dramatically divergent ways due to the interactions of blending with
35 * framebuffer formats. Conceptually, there are two modes:
36 *
37 * - Fixed-function blending (for suitable framebuffer formats, suitable blend
38 * state, and suitable blend constant)
39 *
40 * - Blend shaders (for everything else)
41 *
42 * A given Gallium blend configuration will compile to exactly one
43 * fixed-function blend state, if it compiles to any, although the constant
44 * will vary across runs as that is tracked outside of the Gallium CSO.
45 *
46 * However, that same blend configuration will compile to many different blend
47 * shaders, depending on the framebuffer formats active. The rationale is that
48 * blend shaders override not just fixed-function blending but also
49 * fixed-function format conversion. As such, each blend shader must be
50 * hardcoded to a particular framebuffer format to correctly pack/unpack it. As
51 * a concrete example, to the hardware there is no difference (!) between RG16F
52 * and RG16UI -- both are simply 4-byte-per-pixel chunks. Thus both formats
53 * require a blend shader (even with blending is totally disabled!), required
54 * to do conversion as necessary (if necessary).
55 *
56 * All of this state is encapsulated in the panfrost_blend_state struct
57 * (our subclass of pipe_blend_state).
58 */
59
60 /* Given an initialized CSO and a particular framebuffer format, grab a
61 * blend shader, generating and compiling it if it doesn't exist
62 * (lazy-loading in a way). This routine, when the cache hits, should
63 * befast, suitable for calling every draw to avoid wacky dirty
64 * tracking paths. If the cache hits, boom, done. */
65
66 static struct panfrost_blend_shader *
67 panfrost_get_blend_shader(
68 struct panfrost_context *ctx,
69 struct panfrost_blend_state *blend,
70 enum pipe_format fmt,
71 unsigned rt)
72 {
73 /* Prevent NULL collision issues.. */
74 assert(fmt != 0);
75
76 /* Check the cache */
77 struct hash_table_u64 *shaders = blend->rt[rt].shaders;
78
79 struct panfrost_blend_shader *shader =
80 _mesa_hash_table_u64_search(shaders, fmt);
81
82 if (shader)
83 return shader;
84
85 /* Cache miss. Build one instead, cache it, and go */
86
87 struct panfrost_blend_shader generated =
88 panfrost_compile_blend_shader(ctx, &blend->base, fmt);
89
90 shader = mem_dup(&generated, sizeof(generated));
91 _mesa_hash_table_u64_insert(shaders, fmt, shader);
92 return shader;
93 }
94
95 /* Create a blend CSO. Essentially, try to compile a fixed-function
96 * expression and initialize blend shaders */
97
98 static void *
99 panfrost_create_blend_state(struct pipe_context *pipe,
100 const struct pipe_blend_state *blend)
101 {
102 struct panfrost_context *ctx = pan_context(pipe);
103 struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
104 so->base = *blend;
105
106 /* TODO: The following features are not yet implemented */
107 assert(!blend->logicop_enable);
108 assert(!blend->alpha_to_coverage);
109 assert(!blend->alpha_to_one);
110
111 for (unsigned c = 0; c < 4; ++c) {
112 struct panfrost_blend_rt *rt = &so->rt[c];
113
114 /* There are two paths. First, we would like to try a
115 * fixed-function if we can */
116
117 rt->has_fixed_function =
118 panfrost_make_fixed_blend_mode(
119 &blend->rt[c],
120 &rt->equation,
121 &rt->constant_mask,
122 blend->rt[c].colormask);
123
124 /* Regardless if that works, we also need to initialize
125 * the blend shaders */
126
127 rt->shaders = _mesa_hash_table_u64_create(so);
128 }
129
130 return so;
131 }
132
133 static void
134 panfrost_bind_blend_state(struct pipe_context *pipe,
135 void *cso)
136 {
137 struct panfrost_context *ctx = pan_context(pipe);
138 struct pipe_blend_state *blend = (struct pipe_blend_state *) cso;
139 struct panfrost_blend_state *pblend = (struct panfrost_blend_state *) cso;
140 ctx->blend = pblend;
141
142 if (!blend)
143 return;
144
145 if (ctx->require_sfbd) {
146 SET_BIT(ctx->fragment_shader_core.unknown2_4, MALI_NO_DITHER, !blend->dither);
147 }
148
149 /* Shader itself is not dirty, but the shader core is */
150 ctx->dirty |= PAN_DIRTY_FS;
151 }
152
153 static void
154 panfrost_delete_blend_state(struct pipe_context *pipe,
155 void *blend)
156 {
157 /* TODO: Free shader binary? */
158 ralloc_free(blend);
159 }
160
161 static void
162 panfrost_set_blend_color(struct pipe_context *pipe,
163 const struct pipe_blend_color *blend_color)
164 {
165 struct panfrost_context *ctx = pan_context(pipe);
166
167 if (blend_color)
168 ctx->blend_color = *blend_color;
169 }
170
171 /* Given a vec4 of constants, reduce it to just a single constant according to
172 * the mask (if we can) */
173
174 static bool
175 panfrost_blend_constant(float *out, float *in, unsigned mask)
176 {
177 /* If there is no components used, it automatically works. Do set a
178 * dummy constant just to avoid reading uninitialized memory. */
179
180 if (!mask) {
181 *out = 0.0;
182 return true;
183 }
184
185 /* Find some starter mask */
186 unsigned first = ffs(mask) - 1;
187 float cons = in[first];
188 mask ^= (1 << first);
189
190 /* Ensure the rest are equal */
191 while (mask) {
192 unsigned i = u_bit_scan(&mask);
193
194 if (in[i] != cons) {
195 *out = 0.0;
196 return false;
197 }
198 }
199
200 /* Otherwise, we're good to go */
201 *out = cons;
202 return true;
203 }
204
205 /* Create a final blend given the context */
206
207 struct panfrost_blend_final
208 panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti)
209 {
210 /* Grab the format, falling back gracefully if called invalidly (which
211 * has to happen for no-color-attachment FBOs, for instance) */
212 struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
213 enum pipe_format fmt = PIPE_FORMAT_R8G8B8A8_UNORM;
214
215 if ((fb->nr_cbufs > rti) && fb->cbufs[rti])
216 fmt = fb->cbufs[rti]->format;
217
218 /* Grab the blend state */
219 struct panfrost_blend_state *blend = ctx->blend;
220 assert(blend);
221
222 struct panfrost_blend_rt *rt = &blend->rt[rti];
223
224 struct panfrost_blend_final final;
225
226 /* First, we'll try a fixed function path */
227 if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
228 if (panfrost_blend_constant(
229 &final.equation.constant,
230 ctx->blend_color.color,
231 rt->constant_mask)) {
232 /* There's an equation and suitable constant, so we're good to go */
233 final.is_shader = false;
234 final.equation.equation = &rt->equation;
235 return final;
236 }
237 }
238
239 /* Otherwise, we need to grab a shader */
240 struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
241 final.is_shader = true;
242 final.shader.work_count = shader->work_count;
243
244 if (shader->patch_index) {
245 /* We have to specialize the blend shader to use constants, so
246 * patch in the current constants and upload to transient
247 * memory */
248
249 float *patch = (float *) (shader->shader.cpu + shader->patch_index);
250 memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
251
252 final.shader.gpu = panfrost_upload_transient(
253 ctx, shader->shader.cpu, shader->size);
254 } else {
255 /* No need to specialize further, use the preuploaded */
256 final.shader.gpu = shader->shader.gpu;
257 }
258
259 final.shader.gpu |= shader->first_tag;
260 return final;
261 }
262
263 void
264 panfrost_blend_context_init(struct pipe_context *pipe)
265 {
266 pipe->create_blend_state = panfrost_create_blend_state;
267 pipe->bind_blend_state = panfrost_bind_blend_state;
268 pipe->delete_blend_state = panfrost_delete_blend_state;
269
270 pipe->set_blend_color = panfrost_set_blend_color;
271 }