2 * Copyright (C) 2019 Collabora, Ltd.
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:
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
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
23 * Authors (Collabora):
24 * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
29 #include "util/u_memory.h"
30 #include "pan_blend_shaders.h"
31 #include "pan_blending.h"
33 #include "panfrost-quirks.h"
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:
39 * - Fixed-function blending (for suitable framebuffer formats, suitable blend
40 * state, and suitable blend constant)
42 * - Blend shaders (for everything else)
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.
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).
58 * All of this state is encapsulated in the panfrost_blend_state struct
59 * (our subclass of pipe_blend_state).
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. */
68 static struct panfrost_blend_shader
*
69 panfrost_get_blend_shader(
70 struct panfrost_context
*ctx
,
71 struct panfrost_blend_state
*blend
,
75 /* Prevent NULL collision issues.. */
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
;
82 struct panfrost_blend_shader
*shader
=
83 _mesa_hash_table_u64_search(shaders
, key
);
88 /* Cache miss. Build one instead, cache it, and go */
90 struct panfrost_blend_shader generated
=
91 panfrost_compile_blend_shader(ctx
, &blend
->base
, fmt
, rt
);
93 shader
= mem_dup(&generated
, sizeof(generated
));
94 _mesa_hash_table_u64_insert(shaders
, key
, shader
);
98 /* Create a blend CSO. Essentially, try to compile a fixed-function
99 * expression and initialize blend shaders */
102 panfrost_create_blend_state(struct pipe_context
*pipe
,
103 const struct pipe_blend_state
*blend
)
105 struct panfrost_context
*ctx
= pan_context(pipe
);
106 struct panfrost_blend_state
*so
= rzalloc(ctx
, struct panfrost_blend_state
);
109 /* TODO: The following features are not yet implemented */
110 assert(!blend
->logicop_enable
);
111 assert(!blend
->alpha_to_coverage
);
112 assert(!blend
->alpha_to_one
);
114 for (unsigned c
= 0; c
< PIPE_MAX_COLOR_BUFS
; ++c
) {
115 struct panfrost_blend_rt
*rt
= &so
->rt
[c
];
117 /* There are two paths. First, we would like to try a
118 * fixed-function if we can */
120 /* Without indep blending, the first RT settings replicate */
123 blend
->independent_blend_enable
? c
: 0;
125 rt
->has_fixed_function
=
126 panfrost_make_fixed_blend_mode(
130 blend
->rt
[g
].colormask
);
132 /* Regardless if that works, we also need to initialize
133 * the blend shaders */
135 rt
->shaders
= _mesa_hash_table_u64_create(so
);
142 panfrost_bind_blend_state(struct pipe_context
*pipe
,
145 struct panfrost_context
*ctx
= pan_context(pipe
);
146 struct panfrost_screen
*screen
= pan_screen(ctx
->base
.screen
);
147 struct pipe_blend_state
*blend
= (struct pipe_blend_state
*) cso
;
148 struct panfrost_blend_state
*pblend
= (struct panfrost_blend_state
*) cso
;
154 if (screen
->quirks
& MIDGARD_SFBD
) {
155 SET_BIT(ctx
->fragment_shader_core
.unknown2_4
, MALI_NO_DITHER
, !blend
->dither
);
158 /* Shader itself is not dirty, but the shader core is */
159 ctx
->dirty
|= PAN_DIRTY_FS
;
163 panfrost_delete_blend_shader(struct hash_entry
*entry
)
165 struct panfrost_blend_shader
*shader
= (struct panfrost_blend_shader
*)entry
->data
;
166 free(shader
->buffer
);
171 panfrost_delete_blend_state(struct pipe_context
*pipe
,
174 struct panfrost_blend_state
*blend
= (struct panfrost_blend_state
*) cso
;
176 for (unsigned c
= 0; c
< 4; ++c
) {
177 struct panfrost_blend_rt
*rt
= &blend
->rt
[c
];
178 _mesa_hash_table_u64_clear(rt
->shaders
, panfrost_delete_blend_shader
);
184 panfrost_set_blend_color(struct pipe_context
*pipe
,
185 const struct pipe_blend_color
*blend_color
)
187 struct panfrost_context
*ctx
= pan_context(pipe
);
190 ctx
->blend_color
= *blend_color
;
193 /* Given a vec4 of constants, reduce it to just a single constant according to
194 * the mask (if we can) */
197 panfrost_blend_constant(float *out
, float *in
, unsigned mask
)
199 /* If there is no components used, it automatically works. Do set a
200 * dummy constant just to avoid reading uninitialized memory. */
207 /* Find some starter mask */
208 unsigned first
= ffs(mask
) - 1;
209 float cons
= in
[first
];
210 mask
^= (1 << first
);
212 /* Ensure the rest are equal */
214 unsigned i
= u_bit_scan(&mask
);
222 /* Otherwise, we're good to go */
227 /* Create a final blend given the context */
229 struct panfrost_blend_final
230 panfrost_get_blend_for_context(struct panfrost_context
*ctx
, unsigned rti
)
232 struct panfrost_batch
*batch
= panfrost_get_batch_for_fbo(ctx
);
234 /* Grab the format, falling back gracefully if called invalidly (which
235 * has to happen for no-color-attachment FBOs, for instance) */
236 struct pipe_framebuffer_state
*fb
= &ctx
->pipe_framebuffer
;
237 enum pipe_format fmt
= PIPE_FORMAT_R8G8B8A8_UNORM
;
239 if ((fb
->nr_cbufs
> rti
) && fb
->cbufs
[rti
])
240 fmt
= fb
->cbufs
[rti
]->format
;
242 /* Grab the blend state */
243 struct panfrost_blend_state
*blend
= ctx
->blend
;
246 struct panfrost_blend_rt
*rt
= &blend
->rt
[rti
];
248 struct panfrost_blend_final final
;
250 /* First, we'll try a fixed function path */
251 if (rt
->has_fixed_function
&& panfrost_can_fixed_blend(fmt
)) {
252 if (panfrost_blend_constant(
253 &final
.equation
.constant
,
254 ctx
->blend_color
.color
,
255 rt
->constant_mask
)) {
256 /* There's an equation and suitable constant, so we're good to go */
257 final
.is_shader
= false;
258 final
.equation
.equation
= &rt
->equation
;
261 (rt
->equation
.rgb_mode
== 0x122) &&
262 (rt
->equation
.alpha_mode
== 0x122) &&
263 (rt
->equation
.color_mask
== 0xf);
269 /* Otherwise, we need to grab a shader */
270 struct panfrost_blend_shader
*shader
= panfrost_get_blend_shader(ctx
, blend
, fmt
, rti
);
271 final
.is_shader
= true;
272 final
.no_blending
= false;
273 final
.shader
.work_count
= shader
->work_count
;
274 final
.shader
.first_tag
= shader
->first_tag
;
276 /* Upload the shader */
277 final
.shader
.bo
= panfrost_batch_create_bo(batch
, shader
->size
,
279 PAN_BO_ACCESS_PRIVATE
|
281 PAN_BO_ACCESS_VERTEX_TILER
|
282 PAN_BO_ACCESS_FRAGMENT
);
283 memcpy(final
.shader
.bo
->cpu
, shader
->buffer
, shader
->size
);
285 if (shader
->patch_index
) {
286 /* We have to specialize the blend shader to use constants, so
287 * patch in the current constants */
289 float *patch
= (float *) (final
.shader
.bo
->cpu
+ shader
->patch_index
);
290 memcpy(patch
, ctx
->blend_color
.color
, sizeof(float) * 4);
297 panfrost_blend_context_init(struct pipe_context
*pipe
)
299 pipe
->create_blend_state
= panfrost_create_blend_state
;
300 pipe
->bind_blend_state
= panfrost_bind_blend_state
;
301 pipe
->delete_blend_state
= panfrost_delete_blend_state
;
303 pipe
->set_blend_color
= panfrost_set_blend_color
;