| /* |
| * Copyright (C) 2019 Collabora, Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| * |
| * Authors (Collabora): |
| * Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com> |
| * |
| */ |
| |
| #include <stdio.h> |
| #include "util/u_memory.h" |
| #include "gallium/auxiliary/util/u_blend.h" |
| #include "pan_blend_shaders.h" |
| #include "pan_blending.h" |
| #include "pan_bo.h" |
| #include "panfrost-quirks.h" |
| |
| /* A given Gallium blend state can be encoded to the hardware in numerous, |
| * dramatically divergent ways due to the interactions of blending with |
| * framebuffer formats. Conceptually, there are two modes: |
| * |
| * - Fixed-function blending (for suitable framebuffer formats, suitable blend |
| * state, and suitable blend constant) |
| * |
| * - Blend shaders (for everything else) |
| * |
| * A given Gallium blend configuration will compile to exactly one |
| * fixed-function blend state, if it compiles to any, although the constant |
| * will vary across runs as that is tracked outside of the Gallium CSO. |
| * |
| * However, that same blend configuration will compile to many different blend |
| * shaders, depending on the framebuffer formats active. The rationale is that |
| * blend shaders override not just fixed-function blending but also |
| * fixed-function format conversion. As such, each blend shader must be |
| * hardcoded to a particular framebuffer format to correctly pack/unpack it. As |
| * a concrete example, to the hardware there is no difference (!) between RG16F |
| * and RG16UI -- both are simply 4-byte-per-pixel chunks. Thus both formats |
| * require a blend shader (even with blending is totally disabled!), required |
| * to do conversion as necessary (if necessary). |
| * |
| * All of this state is encapsulated in the panfrost_blend_state struct |
| * (our subclass of pipe_blend_state). |
| */ |
| |
| /* Given an initialized CSO and a particular framebuffer format, grab a |
| * blend shader, generating and compiling it if it doesn't exist |
| * (lazy-loading in a way). This routine, when the cache hits, should |
| * befast, suitable for calling every draw to avoid wacky dirty |
| * tracking paths. If the cache hits, boom, done. */ |
| |
| 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) |
| { |
| /* Prevent NULL collision issues.. */ |
| assert(fmt != 0); |
| |
| /* Check the cache. Key by the RT and format */ |
| struct hash_table *shaders = ctx->blend_shaders; |
| struct panfrost_blend_shader_key key = { |
| .rt = rt, |
| .format = fmt, |
| .has_constants = constants != NULL, |
| .logicop_enable = blend->base.logicop_enable, |
| }; |
| |
| if (blend->base.logicop_enable) { |
| key.logicop_func = blend->base.logicop_func; |
| } else { |
| unsigned idx = blend->base.independent_blend_enable ? rt : 0; |
| |
| if (blend->base.rt[idx].blend_enable) |
| key.equation = blend->base.rt[idx]; |
| } |
| |
| struct hash_entry *he = _mesa_hash_table_search(shaders, &key); |
| struct panfrost_blend_shader *shader = he ? he->data : NULL; |
| |
| if (!shader) { |
| /* Cache miss. Build one instead, cache it, and go */ |
| shader = panfrost_create_blend_shader(ctx, blend, &key); |
| _mesa_hash_table_insert(shaders, &shader->key, shader); |
| } |
| |
| panfrost_compile_blend_shader(shader, constants); |
| return shader; |
| } |
| |
| /* Create a blend CSO. Essentially, try to compile a fixed-function |
| * expression and initialize blend shaders */ |
| |
| static void * |
| panfrost_create_blend_state(struct pipe_context *pipe, |
| const struct pipe_blend_state *blend) |
| { |
| struct panfrost_device *dev = pan_device(pipe->screen); |
| struct panfrost_context *ctx = pan_context(pipe); |
| struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state); |
| unsigned version = dev->gpu_id >> 12; |
| so->base = *blend; |
| |
| /* TODO: The following features are not yet implemented */ |
| assert(!blend->alpha_to_one); |
| |
| for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) { |
| unsigned g = blend->independent_blend_enable ? c : 0; |
| struct pipe_rt_blend_state pipe = blend->rt[g]; |
| |
| struct panfrost_blend_rt *rt = &so->rt[c]; |
| |
| /* Logic ops are always shader */ |
| if (blend->logicop_enable) { |
| rt->load_dest = true; |
| continue; |
| } |
| |
| rt->constant_mask = panfrost_blend_constant_mask(&pipe); |
| rt->has_fixed_function = |
| panfrost_make_fixed_blend_mode(pipe, &rt->equation); |
| |
| /* v6 doesn't support blend constants in FF blend equations. */ |
| if (rt->has_fixed_function && version == 6 && rt->constant_mask) |
| rt->has_fixed_function = false; |
| |
| if (rt->has_fixed_function) { |
| rt->opaque = pipe.rgb_src_factor == PIPE_BLENDFACTOR_ONE && |
| pipe.rgb_dst_factor == PIPE_BLENDFACTOR_ZERO && |
| (pipe.rgb_func == PIPE_BLEND_ADD || |
| pipe.rgb_func == PIPE_BLEND_SUBTRACT) && |
| pipe.alpha_src_factor == PIPE_BLENDFACTOR_ONE && |
| pipe.alpha_dst_factor == PIPE_BLENDFACTOR_ZERO && |
| (pipe.alpha_func == PIPE_BLEND_ADD || |
| pipe.alpha_func == PIPE_BLEND_SUBTRACT) && |
| pipe.colormask == 0xf; |
| } |
| |
| rt->load_dest = util_blend_uses_dest(pipe) |
| || pipe.colormask != 0xF; |
| |
| rt->no_colour = pipe.colormask == 0x0; |
| } |
| |
| return so; |
| } |
| |
| static void |
| panfrost_bind_blend_state(struct pipe_context *pipe, |
| void *cso) |
| { |
| struct panfrost_context *ctx = pan_context(pipe); |
| ctx->blend = (struct panfrost_blend_state *) cso; |
| } |
| |
| static void |
| panfrost_delete_blend_state(struct pipe_context *pipe, |
| void *cso) |
| { |
| struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso; |
| ralloc_free(blend); |
| } |
| |
| static void |
| panfrost_set_blend_color(struct pipe_context *pipe, |
| const struct pipe_blend_color *blend_color) |
| { |
| struct panfrost_context *ctx = pan_context(pipe); |
| |
| if (blend_color) |
| ctx->blend_color = *blend_color; |
| } |
| |
| /* Given a vec4 of constants, reduce it to just a single constant according to |
| * the mask (if we can) */ |
| |
| static bool |
| panfrost_blend_constant(float *out, float *in, unsigned mask) |
| { |
| /* If there is no components used, it automatically works */ |
| |
| if (!mask) |
| return true; |
| |
| /* Find some starter mask */ |
| unsigned first = ffs(mask) - 1; |
| float cons = in[first]; |
| mask ^= (1 << first); |
| |
| /* Ensure the rest are equal */ |
| while (mask) { |
| unsigned i = u_bit_scan(&mask); |
| |
| if (in[i] != cons) |
| return false; |
| } |
| |
| /* Otherwise, we're good to go */ |
| *out = cons; |
| return true; |
| } |
| |
| /* Create a final blend given the context */ |
| |
| struct panfrost_blend_final |
| panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rti, struct panfrost_bo **bo, unsigned *shader_offset) |
| { |
| struct panfrost_batch *batch = panfrost_get_batch_for_fbo(ctx); |
| struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer; |
| enum pipe_format fmt = fb->cbufs[rti]->format; |
| |
| /* Grab the blend state */ |
| struct panfrost_blend_state *blend = ctx->blend; |
| struct panfrost_blend_rt *rt = &blend->rt[rti]; |
| |
| /* First, we'll try fixed function, matching equationn and constant */ |
| if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) { |
| float constant = 0.0; |
| |
| if (panfrost_blend_constant( |
| &constant, |
| ctx->blend_color.color, |
| rt->constant_mask)) { |
| struct panfrost_blend_final final = { |
| .equation = { |
| .equation = rt->equation, |
| .constant = constant |
| }, |
| .load_dest = rt->load_dest, |
| .opaque = rt->opaque, |
| .no_colour = rt->no_colour |
| }; |
| |
| return final; |
| } |
| } |
| |
| /* Otherwise, we need to grab a shader */ |
| struct panfrost_blend_shader *shader = |
| panfrost_get_blend_shader(ctx, blend, fmt, rti, |
| rt->constant_mask ? |
| ctx->blend_color.color : NULL); |
| |
| /* Upload the shader, sharing a BO */ |
| if (!(*bo)) { |
| *bo = panfrost_batch_create_bo(batch, 4096, |
| PAN_BO_EXECUTE, |
| PAN_BO_ACCESS_PRIVATE | |
| PAN_BO_ACCESS_READ | |
| PAN_BO_ACCESS_FRAGMENT); |
| } |
| |
| /* Size check */ |
| assert((*shader_offset + shader->size) < 4096); |
| |
| memcpy((*bo)->cpu + *shader_offset, shader->buffer, shader->size); |
| |
| struct panfrost_blend_final final = { |
| .is_shader = true, |
| .shader = { |
| .work_count = shader->work_count, |
| .first_tag = shader->first_tag, |
| .gpu = (*bo)->gpu + *shader_offset, |
| }, |
| .load_dest = rt->load_dest, |
| }; |
| |
| *shader_offset += shader->size; |
| |
| return final; |
| } |
| |
| void |
| panfrost_blend_context_init(struct pipe_context *pipe) |
| { |
| pipe->create_blend_state = panfrost_create_blend_state; |
| pipe->bind_blend_state = panfrost_bind_blend_state; |
| pipe->delete_blend_state = panfrost_delete_blend_state; |
| |
| pipe->set_blend_color = panfrost_set_blend_color; |
| } |