blob: ffda1196d046dedbfc6fc4b23aeb4e62ead7d9c3 [file] [log] [blame]
/*
* 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 "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)
{
/* Prevent NULL collision issues.. */
assert(fmt != 0);
/* Check the cache. Key by the RT and format */
struct hash_table_u64 *shaders = blend->rt[rt].shaders;
unsigned key = (fmt << 3) | rt;
struct panfrost_blend_shader *shader =
_mesa_hash_table_u64_search(shaders, key);
if (shader)
return shader;
/* Cache miss. Build one instead, cache it, and go */
struct panfrost_blend_shader generated =
panfrost_compile_blend_shader(ctx, &blend->base, fmt, rt);
shader = mem_dup(&generated, sizeof(generated));
_mesa_hash_table_u64_insert(shaders, key, shader);
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_context *ctx = pan_context(pipe);
struct panfrost_blend_state *so = rzalloc(ctx, struct panfrost_blend_state);
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) {
struct panfrost_blend_rt *rt = &so->rt[c];
/* There are two paths. First, we would like to try a
* fixed-function if we can */
/* Without indep blending, the first RT settings replicate */
if (!blend->logicop_enable) {
unsigned g =
blend->independent_blend_enable ? c : 0;
rt->has_fixed_function =
panfrost_make_fixed_blend_mode(
&blend->rt[g],
&rt->equation,
&rt->constant_mask,
blend->rt[g].colormask);
}
/* Regardless if that works, we also need to initialize
* the blend shaders */
rt->shaders = _mesa_hash_table_u64_create(so);
}
return so;
}
static void
panfrost_bind_blend_state(struct pipe_context *pipe,
void *cso)
{
struct panfrost_context *ctx = pan_context(pipe);
struct pipe_blend_state *blend = (struct pipe_blend_state *) cso;
struct panfrost_blend_state *pblend = (struct panfrost_blend_state *) cso;
ctx->blend = pblend;
if (!blend)
return;
}
static void
panfrost_delete_blend_shader(struct hash_entry *entry)
{
struct panfrost_blend_shader *shader = (struct panfrost_blend_shader *)entry->data;
free(shader->buffer);
free(shader);
}
static void
panfrost_delete_blend_state(struct pipe_context *pipe,
void *cso)
{
struct panfrost_blend_state *blend = (struct panfrost_blend_state *) cso;
for (unsigned c = 0; c < PIPE_MAX_COLOR_BUFS; ++c) {
struct panfrost_blend_rt *rt = &blend->rt[c];
_mesa_hash_table_u64_clear(rt->shaders, panfrost_delete_blend_shader);
}
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_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)) {
bool no_blending =
(rt->equation.rgb_mode == 0x122) &&
(rt->equation.alpha_mode == 0x122) &&
(rt->equation.color_mask == 0xf);
struct panfrost_blend_final final = {
.equation = {
.equation = &rt->equation,
.constant = constant
},
.no_blending = no_blending,
.no_colour = (rt->equation.color_mask == 0x0)
};
return final;
}
}
/* Otherwise, we need to grab a shader */
struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
struct panfrost_bo *bo = panfrost_batch_create_bo(batch, shader->size,
PAN_BO_EXECUTE,
PAN_BO_ACCESS_PRIVATE |
PAN_BO_ACCESS_READ |
PAN_BO_ACCESS_FRAGMENT);
memcpy(bo->cpu, shader->buffer, shader->size);
if (shader->patch_index) {
/* We have to specialize the blend shader to use constants, so
* patch in the current constants */
float *patch = (float *) (bo->cpu + shader->patch_index);
memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
}
struct panfrost_blend_final final = {
.is_shader = true,
.shader = {
.work_count = shader->work_count,
.first_tag = shader->first_tag,
.gpu = bo->gpu,
}
};
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;
}