panfrost: Refactor blend infrastructure
We would like to permit keying blend shaders against the framebuffer format, which requires some new blending abstractions. Signed-off-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
This commit is contained in:
@@ -56,6 +56,7 @@ files_panfrost = files(
|
||||
'pan_format.c',
|
||||
'pan_blending.c',
|
||||
'pan_blend_shaders.c',
|
||||
'pan_blend_cso.c',
|
||||
'pan_pretty_print.c',
|
||||
'pan_fragment.c',
|
||||
'pan_invocation.c',
|
||||
|
109
src/gallium/drivers/panfrost/pan_blend.h
Normal file
109
src/gallium/drivers/panfrost/pan_blend.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Collabora
|
||||
*
|
||||
* 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>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PAN_BLEND_H
|
||||
#define __PAN_BLEND_H
|
||||
|
||||
#include "util/hash_table.h"
|
||||
|
||||
/* An internal blend shader descriptor, from the compiler */
|
||||
|
||||
struct panfrost_blend_shader {
|
||||
/* The compiled shader in GPU memory */
|
||||
struct panfrost_transfer shader;
|
||||
|
||||
/* Byte count of the shader */
|
||||
unsigned size;
|
||||
|
||||
/* Number of 128-bit work registers required by the shader */
|
||||
unsigned work_count;
|
||||
|
||||
/* Offset into the shader to patch constants. Zero to disable patching
|
||||
* (it is illogical to have constants at offset 0). */
|
||||
unsigned patch_index;
|
||||
|
||||
/* First instruction tag (for tagging the pointer) */
|
||||
unsigned first_tag;
|
||||
};
|
||||
|
||||
/* A blend shader descriptor ready for actual use */
|
||||
|
||||
struct panfrost_blend_shader_final {
|
||||
/* The upload, possibly to transient memory */
|
||||
mali_ptr gpu;
|
||||
|
||||
/* Same meaning as panfrost_blend_shader */
|
||||
unsigned work_count;
|
||||
};
|
||||
|
||||
struct panfrost_blend_equation_final {
|
||||
struct mali_blend_equation *equation;
|
||||
float constant;
|
||||
};
|
||||
|
||||
struct panfrost_blend_rt {
|
||||
/* If has_fixed_function is set, equation is the
|
||||
* fixed-function configuration for this blend state */
|
||||
|
||||
bool has_fixed_function;
|
||||
struct mali_blend_equation equation;
|
||||
|
||||
/* Mask of blend color components read */
|
||||
unsigned constant_mask;
|
||||
|
||||
/* Regardless of fixed-function blending, this is a map of pipe_format
|
||||
* to panfrost_blend_shader */
|
||||
|
||||
struct hash_table_u64 *shaders;
|
||||
};
|
||||
|
||||
struct panfrost_blend_state {
|
||||
struct pipe_blend_state base;
|
||||
|
||||
struct panfrost_blend_rt rt[PIPE_MAX_COLOR_BUFS];
|
||||
};
|
||||
|
||||
/* Container for a final blend state, specialized to constants and a
|
||||
* framebuffer formats. */
|
||||
|
||||
struct panfrost_blend_final {
|
||||
/* Set for a shader, clear for an equation */
|
||||
bool is_shader;
|
||||
|
||||
union {
|
||||
struct panfrost_blend_shader_final shader;
|
||||
struct panfrost_blend_equation_final equation;
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
panfrost_blend_context_init(struct pipe_context *pipe);
|
||||
|
||||
struct panfrost_blend_final
|
||||
panfrost_get_blend_for_context(struct panfrost_context *ctx, unsigned rt);
|
||||
|
||||
#endif
|
268
src/gallium/drivers/panfrost/pan_blend_cso.c
Normal file
268
src/gallium/drivers/panfrost/pan_blend_cso.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Collabora
|
||||
*
|
||||
* 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"
|
||||
|
||||
/* 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. */
|
||||
|
||||
static 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 */
|
||||
struct hash_table_u64 *shaders = blend->rt[rt].shaders;
|
||||
|
||||
struct panfrost_blend_shader *shader =
|
||||
_mesa_hash_table_u64_search(shaders, fmt);
|
||||
|
||||
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);
|
||||
|
||||
shader = mem_dup(&generated, sizeof(generated));
|
||||
_mesa_hash_table_u64_insert(shaders, fmt, 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->logicop_enable);
|
||||
assert(!blend->alpha_to_coverage);
|
||||
assert(!blend->alpha_to_one);
|
||||
|
||||
for (unsigned c = 0; c < 4; ++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 */
|
||||
|
||||
rt->has_fixed_function =
|
||||
panfrost_make_fixed_blend_mode(
|
||||
&blend->rt[c],
|
||||
&rt->equation,
|
||||
&rt->constant_mask,
|
||||
blend->rt[c].colormask);
|
||||
|
||||
/* Regardless if that works, we also need to initialize
|
||||
* the blend shaders */
|
||||
|
||||
rt->shaders = _mesa_hash_table_u64_create(NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
SET_BIT(ctx->fragment_shader_core.unknown2_4, MALI_NO_DITHER, !blend->dither);
|
||||
|
||||
/* Shader itself is not dirty, but the shader core is */
|
||||
ctx->dirty |= PAN_DIRTY_FS;
|
||||
}
|
||||
|
||||
static void
|
||||
panfrost_delete_blend_state(struct pipe_context *pipe,
|
||||
void *blend)
|
||||
{
|
||||
/* TODO: leaks internally? */
|
||||
|
||||
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. Do set a
|
||||
* dummy constant just to avoid reading uninitialized memory. */
|
||||
|
||||
if (!mask) {
|
||||
*out = 0.0;
|
||||
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) {
|
||||
*out = 0.0;
|
||||
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)
|
||||
{
|
||||
/* Grab the format */
|
||||
struct pipe_framebuffer_state *fb = &ctx->pipe_framebuffer;
|
||||
assert(fb->nr_cbufs > rti);
|
||||
enum pipe_format fmt = fb->cbufs[rti]->format;
|
||||
|
||||
/* Grab the blend state */
|
||||
struct panfrost_blend_state *blend = ctx->blend;
|
||||
assert(blend);
|
||||
|
||||
struct panfrost_blend_rt *rt = &blend->rt[rti];
|
||||
|
||||
struct panfrost_blend_final final;
|
||||
|
||||
/* First, we'll try a fixed function path */
|
||||
if (rt->has_fixed_function && panfrost_can_fixed_blend(fmt)) {
|
||||
if (panfrost_blend_constant(
|
||||
&final.equation.constant,
|
||||
ctx->blend_color.color,
|
||||
rt->constant_mask))
|
||||
{
|
||||
/* There's an equation and suitable constant, so we're good to go */
|
||||
final.is_shader = false;
|
||||
final.equation.equation = &rt->equation;
|
||||
return final;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, we need to grab a shader */
|
||||
struct panfrost_blend_shader *shader = panfrost_get_blend_shader(ctx, blend, fmt, rti);
|
||||
final.is_shader = true;
|
||||
final.shader.work_count = shader->work_count;
|
||||
|
||||
if (shader->patch_index) {
|
||||
/* We have to specialize the blend shader to use constants, so
|
||||
* patch in the current constants and upload to transient
|
||||
* memory */
|
||||
|
||||
float *patch = (float *) (shader->shader.cpu + shader->patch_index);
|
||||
memcpy(patch, ctx->blend_color.color, sizeof(float) * 4);
|
||||
|
||||
final.shader.gpu = panfrost_upload_transient(
|
||||
ctx, shader->shader.cpu, shader->size);
|
||||
} else {
|
||||
/* No need to specialize further, use the preuploaded */
|
||||
final.shader.gpu = shader->shader.gpu;
|
||||
}
|
||||
|
||||
final.shader.gpu |= shader->first_tag;
|
||||
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;
|
||||
}
|
@@ -29,6 +29,7 @@
|
||||
#include "compiler/nir/nir_builder.h"
|
||||
#include "midgard/nir_lower_blend.h"
|
||||
#include "gallium/auxiliary/util/u_blend.h"
|
||||
#include "util/u_memory.h"
|
||||
|
||||
/*
|
||||
* Implements the command stream portion of programmatic blend shaders.
|
||||
@@ -124,13 +125,14 @@ nir_make_options(const struct pipe_blend_state *blend, unsigned nr_cbufs)
|
||||
return options;
|
||||
}
|
||||
|
||||
void
|
||||
panfrost_make_blend_shader(
|
||||
struct panfrost_blend_shader
|
||||
panfrost_compile_blend_shader(
|
||||
struct panfrost_context *ctx,
|
||||
struct panfrost_blend_state *cso,
|
||||
const struct pipe_blend_color *blend_color,
|
||||
struct pipe_blend_state *cso,
|
||||
enum pipe_format format)
|
||||
{
|
||||
struct panfrost_blend_shader res;
|
||||
|
||||
/* Build the shader */
|
||||
|
||||
nir_shader *shader = nir_shader_create(NULL, MESA_SHADER_FRAGMENT, &midgard_nir_options, NULL);
|
||||
@@ -160,7 +162,7 @@ panfrost_make_blend_shader(
|
||||
nir_store_var(b, c_out, s_src, 0xFF);
|
||||
|
||||
nir_lower_blend_options options =
|
||||
nir_make_options(&cso->base, 1);
|
||||
nir_make_options(cso, 1);
|
||||
NIR_PASS_V(shader, nir_lower_blend, options);
|
||||
|
||||
NIR_PASS_V(shader, nir_lower_framebuffer, format);
|
||||
@@ -175,20 +177,16 @@ panfrost_make_blend_shader(
|
||||
int size = program.compiled.size;
|
||||
uint8_t *dst = program.compiled.data;
|
||||
|
||||
/* Hot patch in constant color */
|
||||
|
||||
if (program.blend_patch_offset >= 0) {
|
||||
float *hot_color = (float *) (dst + program.blend_patch_offset);
|
||||
|
||||
for (int c = 0; c < 4; ++c)
|
||||
hot_color[c] = blend_color->color[c];
|
||||
}
|
||||
|
||||
cso->blend_shader = panfrost_upload(&ctx->shaders, dst, size, true) | program.first_tag;
|
||||
|
||||
/* We need to switch to shader mode */
|
||||
cso->has_blend_shader = true;
|
||||
res.shader.cpu = mem_dup(dst, size);
|
||||
res.shader.gpu = panfrost_upload(&ctx->shaders, dst, size, true);
|
||||
|
||||
/* At least two work registers are needed due to an encoding quirk */
|
||||
cso->blend_work_count = MAX2(program.work_register_count, 2);
|
||||
res.work_count = MAX2(program.work_register_count, 2);
|
||||
|
||||
/* Allow us to patch later */
|
||||
res.patch_index = program.blend_patch_offset;
|
||||
res.first_tag = program.first_tag;
|
||||
res.size = size;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@@ -29,12 +29,12 @@
|
||||
#include "pipe/p_defines.h"
|
||||
#include <panfrost-job.h>
|
||||
#include "pan_context.h"
|
||||
#include "pan_blend.h"
|
||||
|
||||
void
|
||||
panfrost_make_blend_shader(
|
||||
struct panfrost_blend_shader
|
||||
panfrost_compile_blend_shader(
|
||||
struct panfrost_context *ctx,
|
||||
struct panfrost_blend_state *cso,
|
||||
const struct pipe_blend_color *blend_color,
|
||||
struct pipe_blend_state *cso,
|
||||
enum pipe_format format);
|
||||
|
||||
#endif
|
||||
|
@@ -101,8 +101,8 @@
|
||||
|
||||
/* Not all formats can be blended by fixed-function hardware */
|
||||
|
||||
static bool
|
||||
panfrost_can_blend(enum pipe_format format)
|
||||
bool
|
||||
panfrost_can_fixed_blend(enum pipe_format format)
|
||||
{
|
||||
/* Fixed-function can handle sRGB */
|
||||
format = util_format_linear(format);
|
||||
@@ -318,53 +318,24 @@ panfrost_make_fixed_blend_part(unsigned func, unsigned src_factor, unsigned dst_
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We can upload a single constant for all of the factors. So, scan the factors
|
||||
* for constants used, and scan the constants for the constants used. If there
|
||||
* is a single unique constant, output that. If there are multiple,
|
||||
* fixed-function operation breaks down. */
|
||||
/* We can upload a single constant for all of the factors. So, scan
|
||||
* the factors for constants used to create a mask to check later. */
|
||||
|
||||
static bool
|
||||
panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pipe_blend_color *blend_color, void *out)
|
||||
static unsigned
|
||||
panfrost_constant_mask(unsigned *factors, unsigned num_factors)
|
||||
{
|
||||
/* Color components used */
|
||||
bool cc[4] = { false };
|
||||
unsigned mask = 0;
|
||||
|
||||
for (unsigned i = 0; i < num_factors; ++i) {
|
||||
unsigned factor = uncomplement_factor(factors[i]);
|
||||
|
||||
if (factor == PIPE_BLENDFACTOR_CONST_COLOR)
|
||||
cc[0] = cc[1] = cc[2] = true;
|
||||
mask |= 0b0111; /* RGB */
|
||||
else if (factor == PIPE_BLENDFACTOR_CONST_ALPHA)
|
||||
cc[3] = true;
|
||||
mask |= 0b1000; /* A */
|
||||
}
|
||||
|
||||
/* Find the actual constant associated with the components used*/
|
||||
|
||||
float constant = 0.0;
|
||||
bool has_constant = false;
|
||||
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
/* If the component is unused, nothing to do */
|
||||
if (!cc[i]) continue;
|
||||
|
||||
float value = blend_color->color[i];
|
||||
|
||||
/* Either there's a second constant, in which case we fail, or
|
||||
* there's no constant / a first constant, in which case we use
|
||||
* that constant */
|
||||
|
||||
if (has_constant && constant != value) {
|
||||
return false;
|
||||
} else {
|
||||
has_constant = true;
|
||||
constant = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have the constant -- success! */
|
||||
|
||||
memcpy(out, &constant, sizeof(float));
|
||||
return true;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* Create the descriptor for a fixed blend mode given the corresponding Gallium
|
||||
@@ -376,19 +347,13 @@ panfrost_make_constant(unsigned *factors, unsigned num_factors, const struct pip
|
||||
bool
|
||||
panfrost_make_fixed_blend_mode(
|
||||
const struct pipe_rt_blend_state *blend,
|
||||
struct panfrost_blend_state *so,
|
||||
unsigned colormask,
|
||||
const struct pipe_blend_color *blend_color,
|
||||
enum pipe_format format)
|
||||
struct mali_blend_equation *out,
|
||||
unsigned *constant_mask,
|
||||
unsigned colormask)
|
||||
{
|
||||
struct mali_blend_equation *out = &so->equation;
|
||||
/* Gallium and Mali represent colour masks identically. XXX: Static
|
||||
* assert for future proof */
|
||||
|
||||
/* Check if the format supports fixed-function blending at all */
|
||||
|
||||
if (!panfrost_can_blend(format))
|
||||
return false;
|
||||
|
||||
/* Gallium and Mali represent colour masks identically. XXX: Static assert for future proof */
|
||||
out->color_mask = colormask;
|
||||
|
||||
/* If no blending is enabled, default back on `replace` mode */
|
||||
@@ -399,16 +364,18 @@ panfrost_make_fixed_blend_mode(
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We have room only for a single float32 constant between the four
|
||||
* components. If we need more, spill to the programmable pipeline. */
|
||||
/* At draw-time, we'll need to analyze the blend constant, so
|
||||
* precompute a mask for it -- even if we don't end up able to use
|
||||
* fixed-function blending */
|
||||
|
||||
unsigned factors[] = {
|
||||
blend->rgb_src_factor, blend->rgb_dst_factor,
|
||||
blend->alpha_src_factor, blend->alpha_dst_factor,
|
||||
};
|
||||
|
||||
if (!panfrost_make_constant(factors, ARRAY_SIZE(factors), blend_color, &so->constant))
|
||||
return false;
|
||||
*constant_mask = panfrost_constant_mask(factors, ARRAY_SIZE(factors));
|
||||
|
||||
/* Try to compile the actual fixed-function blend */
|
||||
|
||||
unsigned rgb_mode = 0;
|
||||
unsigned alpha_mode = 0;
|
||||
|
@@ -31,11 +31,14 @@
|
||||
|
||||
struct panfrost_blend_state;
|
||||
|
||||
bool panfrost_make_fixed_blend_mode(
|
||||
bool
|
||||
panfrost_make_fixed_blend_mode(
|
||||
const struct pipe_rt_blend_state *blend,
|
||||
struct panfrost_blend_state *so,
|
||||
unsigned colormask,
|
||||
const struct pipe_blend_color *blend_color,
|
||||
enum pipe_format format);
|
||||
struct mali_blend_equation *out,
|
||||
unsigned *constant_mask,
|
||||
unsigned colormask);
|
||||
|
||||
bool
|
||||
panfrost_can_fixed_blend(enum pipe_format format);
|
||||
|
||||
#endif
|
||||
|
@@ -1071,9 +1071,14 @@ panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
|
||||
COPY(midgard1.unknown2);
|
||||
|
||||
#undef COPY
|
||||
|
||||
/* Get blending setup */
|
||||
struct panfrost_blend_final blend =
|
||||
panfrost_get_blend_for_context(ctx, 0);
|
||||
|
||||
/* If there is a blend shader, work registers are shared */
|
||||
|
||||
if (ctx->blend->has_blend_shader)
|
||||
if (blend.is_shader)
|
||||
ctx->fragment_shader_core.midgard1.work_count = /*MAX2(ctx->fragment_shader_core.midgard1.work_count, ctx->blend->blend_work_count)*/16;
|
||||
|
||||
/* Set late due to depending on render state */
|
||||
@@ -1112,18 +1117,19 @@ panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
|
||||
/* Check if we're using the default blend descriptor (fast path) */
|
||||
|
||||
bool no_blending =
|
||||
!ctx->blend->has_blend_shader &&
|
||||
(ctx->blend->equation.rgb_mode == 0x122) &&
|
||||
(ctx->blend->equation.alpha_mode == 0x122) &&
|
||||
(ctx->blend->equation.color_mask == 0xf);
|
||||
!blend.is_shader &&
|
||||
(blend.equation.equation->rgb_mode == 0x122) &&
|
||||
(blend.equation.equation->alpha_mode == 0x122) &&
|
||||
(blend.equation.equation->color_mask == 0xf);
|
||||
|
||||
/* Even on MFBD, the shader descriptor gets blend shaders. It's
|
||||
* *also* copied to the blend_meta appended (by convention),
|
||||
* but this is the field actually read by the hardware. (Or
|
||||
* maybe both are read...?) */
|
||||
|
||||
if (ctx->blend->has_blend_shader) {
|
||||
ctx->fragment_shader_core.blend.shader = ctx->blend->blend_shader;
|
||||
if (blend.is_shader) {
|
||||
ctx->fragment_shader_core.blend.shader =
|
||||
blend.shader.gpu;
|
||||
} else {
|
||||
ctx->fragment_shader_core.blend.shader = 0;
|
||||
}
|
||||
@@ -1134,9 +1140,11 @@ panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
|
||||
* additionally need to signal CAN_DISCARD for nontrivial blend
|
||||
* modes (so we're able to read back the destination buffer) */
|
||||
|
||||
if (!ctx->blend->has_blend_shader) {
|
||||
ctx->fragment_shader_core.blend.equation = ctx->blend->equation;
|
||||
ctx->fragment_shader_core.blend.constant = ctx->blend->constant;
|
||||
if (!blend.is_shader) {
|
||||
ctx->fragment_shader_core.blend.equation =
|
||||
*blend.equation.equation;
|
||||
ctx->fragment_shader_core.blend.constant =
|
||||
blend.equation.constant;
|
||||
}
|
||||
|
||||
if (!no_blending) {
|
||||
@@ -1155,13 +1163,13 @@ panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
|
||||
|
||||
unsigned blend_count = 0x200;
|
||||
|
||||
if (ctx->blend->has_blend_shader) {
|
||||
if (blend.is_shader) {
|
||||
/* For a blend shader, the bottom nibble corresponds to
|
||||
* the number of work registers used, which signals the
|
||||
* -existence- of a blend shader */
|
||||
|
||||
assert(ctx->blend->blend_work_count >= 2);
|
||||
blend_count |= MIN2(ctx->blend->blend_work_count, 3);
|
||||
assert(blend.shader.work_count >= 2);
|
||||
blend_count |= MIN2(blend.shader.work_count, 3);
|
||||
} else {
|
||||
/* Otherwise, the bottom bit simply specifies if
|
||||
* blending (anything other than REPLACE) is enabled */
|
||||
@@ -1191,13 +1199,13 @@ panfrost_emit_for_draw(struct panfrost_context *ctx, bool with_vertex_data)
|
||||
* native Midgard ops for helping here, but
|
||||
* they're not well-understood yet. */
|
||||
|
||||
assert(!(is_srgb && ctx->blend->has_blend_shader));
|
||||
assert(!(is_srgb && blend.is_shader));
|
||||
|
||||
if (ctx->blend->has_blend_shader) {
|
||||
rts[i].blend.shader = ctx->blend->blend_shader;
|
||||
if (blend.is_shader) {
|
||||
rts[i].blend.shader = blend.shader.gpu;
|
||||
} else {
|
||||
rts[i].blend.equation = ctx->blend->equation;
|
||||
rts[i].blend.constant = ctx->blend->constant;
|
||||
rts[i].blend.equation = *blend.equation.equation;
|
||||
rts[i].blend.constant = blend.equation.constant;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2363,88 +2371,6 @@ panfrost_set_framebuffer_state(struct pipe_context *pctx,
|
||||
}
|
||||
}
|
||||
|
||||
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->logicop_enable);
|
||||
assert(!blend->alpha_to_coverage);
|
||||
assert(!blend->alpha_to_one);
|
||||
|
||||
/* Compile the blend state, first as fixed-function if we can */
|
||||
|
||||
/* TODO: Key by format */
|
||||
enum pipe_format format = ctx->pipe_framebuffer.nr_cbufs ?
|
||||
ctx->pipe_framebuffer.cbufs[0]->format :
|
||||
PIPE_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
if (panfrost_make_fixed_blend_mode(&blend->rt[0], so, blend->rt[0].colormask, &ctx->blend_color, format))
|
||||
return so;
|
||||
|
||||
/* If we can't, compile a blend shader instead */
|
||||
|
||||
panfrost_make_blend_shader(ctx, so, &ctx->blend_color, format);
|
||||
|
||||
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;
|
||||
|
||||
SET_BIT(ctx->fragment_shader_core.unknown2_4, MALI_NO_DITHER, !blend->dither);
|
||||
|
||||
/* TODO: Attach color */
|
||||
|
||||
/* Shader itself is not dirty, but the shader core is */
|
||||
ctx->dirty |= PAN_DIRTY_FS;
|
||||
}
|
||||
|
||||
static void
|
||||
panfrost_delete_blend_state(struct pipe_context *pipe,
|
||||
void *blend)
|
||||
{
|
||||
struct panfrost_blend_state *so = (struct panfrost_blend_state *) blend;
|
||||
|
||||
if (so->has_blend_shader) {
|
||||
DBG("Deleting blend state leak blend shaders bytecode\n");
|
||||
}
|
||||
|
||||
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 is we're unbinding, so ctx->blend_color is now undefined -> nothing to do */
|
||||
|
||||
if (blend_color) {
|
||||
ctx->blend_color = *blend_color;
|
||||
|
||||
/* The blend mode depends on the blend constant color, due to the
|
||||
* fixed/programmable split. So, we're forced to regenerate the blend
|
||||
* equation */
|
||||
|
||||
/* TODO: Attach color */
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
panfrost_create_depth_stencil_state(struct pipe_context *pipe,
|
||||
const struct pipe_depth_stencil_alpha_state *depth_stencil)
|
||||
@@ -2795,12 +2721,6 @@ panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
|
||||
gallium->delete_sampler_state = panfrost_generic_cso_delete;
|
||||
gallium->bind_sampler_states = panfrost_bind_sampler_states;
|
||||
|
||||
gallium->create_blend_state = panfrost_create_blend_state;
|
||||
gallium->bind_blend_state = panfrost_bind_blend_state;
|
||||
gallium->delete_blend_state = panfrost_delete_blend_state;
|
||||
|
||||
gallium->set_blend_color = panfrost_set_blend_color;
|
||||
|
||||
gallium->create_depth_stencil_alpha_state = panfrost_create_depth_stencil_state;
|
||||
gallium->bind_depth_stencil_alpha_state = panfrost_bind_depth_stencil_state;
|
||||
gallium->delete_depth_stencil_alpha_state = panfrost_delete_depth_stencil_state;
|
||||
@@ -2824,6 +2744,7 @@ panfrost_create_context(struct pipe_screen *screen, void *priv, unsigned flags)
|
||||
gallium->set_stream_output_targets = panfrost_set_stream_output_targets;
|
||||
|
||||
panfrost_resource_context_init(gallium);
|
||||
panfrost_blend_context_init(gallium);
|
||||
|
||||
panfrost_drm_init_context(ctx);
|
||||
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include <assert.h>
|
||||
#include "pan_resource.h"
|
||||
#include "pan_job.h"
|
||||
#include "pan_blend.h"
|
||||
|
||||
#include "pipe/p_compiler.h"
|
||||
#include "pipe/p_config.h"
|
||||
@@ -228,21 +229,6 @@ struct panfrost_rasterizer {
|
||||
unsigned tiler_gl_enables;
|
||||
};
|
||||
|
||||
struct panfrost_blend_state {
|
||||
struct pipe_blend_state base;
|
||||
|
||||
/* Whether a blend shader is in use */
|
||||
bool has_blend_shader;
|
||||
|
||||
/* Compiled fixed function command */
|
||||
struct mali_blend_equation equation;
|
||||
float constant;
|
||||
|
||||
/* Compiled blend shader */
|
||||
mali_ptr blend_shader;
|
||||
int blend_work_count;
|
||||
};
|
||||
|
||||
/* Variants bundle together to form the backing CSO, bundling multiple
|
||||
* shaders with varying emulated features baked in (alpha test
|
||||
* parameters, etc) */
|
||||
|
Reference in New Issue
Block a user