blob: fbb3bd1272f85bfe091a33ca5066933c55c18029 [file] [log] [blame]
/*
* Copyright 2018 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
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS 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.
*/
#include "zink_state.h"
#include "zink_context.h"
#include "zink_screen.h"
#include "util/u_memory.h"
#include <math.h>
static void *
zink_create_vertex_elements_state(struct pipe_context *pctx,
unsigned num_elements,
const struct pipe_vertex_element *elements)
{
struct zink_screen *screen = zink_screen(pctx->screen);
unsigned int i;
struct zink_vertex_elements_state *ves = CALLOC_STRUCT(zink_vertex_elements_state);
if (!ves)
return NULL;
int buffer_map[PIPE_MAX_ATTRIBS];
for (int i = 0; i < ARRAY_SIZE(buffer_map); ++i)
buffer_map[i] = -1;
int num_bindings = 0;
for (i = 0; i < num_elements; ++i) {
const struct pipe_vertex_element *elem = elements + i;
int binding = elem->vertex_buffer_index;
if (buffer_map[binding] < 0) {
ves->binding_map[num_bindings] = binding;
buffer_map[binding] = num_bindings++;
}
binding = buffer_map[binding];
ves->bindings[binding].binding = binding;
ves->bindings[binding].inputRate = elem->instance_divisor ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
assert(!elem->instance_divisor || zink_screen(pctx->screen)->have_EXT_vertex_attribute_divisor);
ves->divisor[binding] = elem->instance_divisor;
assert(elem->instance_divisor <= screen->max_vertex_attrib_divisor);
ves->hw_state.attribs[i].binding = binding;
ves->hw_state.attribs[i].location = i; // TODO: unsure
ves->hw_state.attribs[i].format = zink_get_format(screen,
elem->src_format);
assert(ves->hw_state.attribs[i].format != VK_FORMAT_UNDEFINED);
ves->hw_state.attribs[i].offset = elem->src_offset;
}
ves->hw_state.num_bindings = num_bindings;
ves->hw_state.num_attribs = num_elements;
return ves;
}
static void
zink_bind_vertex_elements_state(struct pipe_context *pctx,
void *cso)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_gfx_pipeline_state *state = &ctx->gfx_pipeline_state;
ctx->element_state = cso;
state->hash = 0;
state->divisors_present = 0;
if (cso) {
state->element_state = &ctx->element_state->hw_state;
struct zink_vertex_elements_state *ves = cso;
for (int i = 0; i < state->element_state->num_bindings; ++i) {
state->bindings[i].binding = ves->bindings[i].binding;
state->bindings[i].inputRate = ves->bindings[i].inputRate;
if (ves->divisor[i]) {
state->divisors[state->divisors_present].divisor = ves->divisor[i];
state->divisors[state->divisors_present].binding = state->bindings[i].binding;
state->divisors_present++;
}
}
} else
state->element_state = NULL;
}
static void
zink_delete_vertex_elements_state(struct pipe_context *pctx,
void *ves)
{
}
static VkBlendFactor
blend_factor(enum pipe_blendfactor factor)
{
switch (factor) {
case PIPE_BLENDFACTOR_ONE: return VK_BLEND_FACTOR_ONE;
case PIPE_BLENDFACTOR_SRC_COLOR: return VK_BLEND_FACTOR_SRC_COLOR;
case PIPE_BLENDFACTOR_SRC_ALPHA: return VK_BLEND_FACTOR_SRC_ALPHA;
case PIPE_BLENDFACTOR_DST_ALPHA: return VK_BLEND_FACTOR_DST_ALPHA;
case PIPE_BLENDFACTOR_DST_COLOR: return VK_BLEND_FACTOR_DST_COLOR;
case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE:
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
case PIPE_BLENDFACTOR_CONST_COLOR: return VK_BLEND_FACTOR_CONSTANT_COLOR;
case PIPE_BLENDFACTOR_CONST_ALPHA: return VK_BLEND_FACTOR_CONSTANT_ALPHA;
case PIPE_BLENDFACTOR_SRC1_COLOR: return VK_BLEND_FACTOR_SRC1_COLOR;
case PIPE_BLENDFACTOR_SRC1_ALPHA: return VK_BLEND_FACTOR_SRC1_ALPHA;
case PIPE_BLENDFACTOR_ZERO: return VK_BLEND_FACTOR_ZERO;
case PIPE_BLENDFACTOR_INV_SRC_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case PIPE_BLENDFACTOR_INV_SRC_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case PIPE_BLENDFACTOR_INV_DST_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case PIPE_BLENDFACTOR_INV_DST_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case PIPE_BLENDFACTOR_INV_CONST_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
case PIPE_BLENDFACTOR_INV_SRC1_COLOR:
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
case PIPE_BLENDFACTOR_INV_SRC1_ALPHA:
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
}
unreachable("unexpected blend factor");
}
static bool
need_blend_constants(enum pipe_blendfactor factor)
{
switch (factor) {
case PIPE_BLENDFACTOR_CONST_COLOR:
case PIPE_BLENDFACTOR_CONST_ALPHA:
case PIPE_BLENDFACTOR_INV_CONST_COLOR:
case PIPE_BLENDFACTOR_INV_CONST_ALPHA:
return true;
default:
return false;
}
}
static VkBlendOp
blend_op(enum pipe_blend_func func)
{
switch (func) {
case PIPE_BLEND_ADD: return VK_BLEND_OP_ADD;
case PIPE_BLEND_SUBTRACT: return VK_BLEND_OP_SUBTRACT;
case PIPE_BLEND_REVERSE_SUBTRACT: return VK_BLEND_OP_REVERSE_SUBTRACT;
case PIPE_BLEND_MIN: return VK_BLEND_OP_MIN;
case PIPE_BLEND_MAX: return VK_BLEND_OP_MAX;
}
unreachable("unexpected blend function");
}
static VkLogicOp
logic_op(enum pipe_logicop func)
{
switch (func) {
case PIPE_LOGICOP_CLEAR: return VK_LOGIC_OP_CLEAR;
case PIPE_LOGICOP_NOR: return VK_LOGIC_OP_NOR;
case PIPE_LOGICOP_AND_INVERTED: return VK_LOGIC_OP_AND_INVERTED;
case PIPE_LOGICOP_COPY_INVERTED: return VK_LOGIC_OP_COPY_INVERTED;
case PIPE_LOGICOP_AND_REVERSE: return VK_LOGIC_OP_AND_REVERSE;
case PIPE_LOGICOP_INVERT: return VK_LOGIC_OP_INVERT;
case PIPE_LOGICOP_XOR: return VK_LOGIC_OP_XOR;
case PIPE_LOGICOP_NAND: return VK_LOGIC_OP_NAND;
case PIPE_LOGICOP_AND: return VK_LOGIC_OP_AND;
case PIPE_LOGICOP_EQUIV: return VK_LOGIC_OP_EQUIVALENT;
case PIPE_LOGICOP_NOOP: return VK_LOGIC_OP_NO_OP;
case PIPE_LOGICOP_OR_INVERTED: return VK_LOGIC_OP_OR_INVERTED;
case PIPE_LOGICOP_COPY: return VK_LOGIC_OP_COPY;
case PIPE_LOGICOP_OR_REVERSE: return VK_LOGIC_OP_OR_REVERSE;
case PIPE_LOGICOP_OR: return VK_LOGIC_OP_OR;
case PIPE_LOGICOP_SET: return VK_LOGIC_OP_SET;
}
unreachable("unexpected logicop function");
}
static void *
zink_create_blend_state(struct pipe_context *pctx,
const struct pipe_blend_state *blend_state)
{
struct zink_blend_state *cso = CALLOC_STRUCT(zink_blend_state);
if (!cso)
return NULL;
if (blend_state->logicop_enable) {
cso->logicop_enable = VK_TRUE;
cso->logicop_func = logic_op(blend_state->logicop_func);
}
/* TODO: figure out what to do with dither (nothing is probably "OK" for now,
* as dithering is undefined in GL
*/
/* TODO: these are multisampling-state, and should be set there instead of
* here, as that's closer tied to the update-frequency
*/
cso->alpha_to_coverage = blend_state->alpha_to_coverage;
cso->alpha_to_one = blend_state->alpha_to_one;
cso->need_blend_constants = false;
for (int i = 0; i < PIPE_MAX_COLOR_BUFS; ++i) {
const struct pipe_rt_blend_state *rt = blend_state->rt;
if (blend_state->independent_blend_enable)
rt = blend_state->rt + i;
VkPipelineColorBlendAttachmentState att = { };
if (rt->blend_enable) {
att.blendEnable = VK_TRUE;
att.srcColorBlendFactor = blend_factor(rt->rgb_src_factor);
att.dstColorBlendFactor = blend_factor(rt->rgb_dst_factor);
att.colorBlendOp = blend_op(rt->rgb_func);
att.srcAlphaBlendFactor = blend_factor(rt->alpha_src_factor);
att.dstAlphaBlendFactor = blend_factor(rt->alpha_dst_factor);
att.alphaBlendOp = blend_op(rt->alpha_func);
if (need_blend_constants(rt->rgb_src_factor) ||
need_blend_constants(rt->rgb_dst_factor) ||
need_blend_constants(rt->alpha_src_factor) ||
need_blend_constants(rt->alpha_dst_factor))
cso->need_blend_constants = true;
}
if (rt->colormask & PIPE_MASK_R)
att.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT;
if (rt->colormask & PIPE_MASK_G)
att.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT;
if (rt->colormask & PIPE_MASK_B)
att.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT;
if (rt->colormask & PIPE_MASK_A)
att.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT;
cso->attachments[i] = att;
}
return cso;
}
static void
zink_bind_blend_state(struct pipe_context *pctx, void *cso)
{
struct zink_gfx_pipeline_state* state = &zink_context(pctx)->gfx_pipeline_state;
if (state->blend_state != cso) {
state->blend_state = cso;
state->hash = 0;
}
}
static void
zink_delete_blend_state(struct pipe_context *pctx, void *blend_state)
{
FREE(blend_state);
}
static VkCompareOp
compare_op(enum pipe_compare_func func)
{
switch (func) {
case PIPE_FUNC_NEVER: return VK_COMPARE_OP_NEVER;
case PIPE_FUNC_LESS: return VK_COMPARE_OP_LESS;
case PIPE_FUNC_EQUAL: return VK_COMPARE_OP_EQUAL;
case PIPE_FUNC_LEQUAL: return VK_COMPARE_OP_LESS_OR_EQUAL;
case PIPE_FUNC_GREATER: return VK_COMPARE_OP_GREATER;
case PIPE_FUNC_NOTEQUAL: return VK_COMPARE_OP_NOT_EQUAL;
case PIPE_FUNC_GEQUAL: return VK_COMPARE_OP_GREATER_OR_EQUAL;
case PIPE_FUNC_ALWAYS: return VK_COMPARE_OP_ALWAYS;
}
unreachable("unexpected func");
}
static VkStencilOp
stencil_op(enum pipe_stencil_op op)
{
switch (op) {
case PIPE_STENCIL_OP_KEEP: return VK_STENCIL_OP_KEEP;
case PIPE_STENCIL_OP_ZERO: return VK_STENCIL_OP_ZERO;
case PIPE_STENCIL_OP_REPLACE: return VK_STENCIL_OP_REPLACE;
case PIPE_STENCIL_OP_INCR: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case PIPE_STENCIL_OP_DECR: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case PIPE_STENCIL_OP_INCR_WRAP: return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case PIPE_STENCIL_OP_DECR_WRAP: return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case PIPE_STENCIL_OP_INVERT: return VK_STENCIL_OP_INVERT;
}
unreachable("unexpected op");
}
static VkStencilOpState
stencil_op_state(const struct pipe_stencil_state *src)
{
VkStencilOpState ret;
ret.failOp = stencil_op(src->fail_op);
ret.passOp = stencil_op(src->zpass_op);
ret.depthFailOp = stencil_op(src->zfail_op);
ret.compareOp = compare_op(src->func);
ret.compareMask = src->valuemask;
ret.writeMask = src->writemask;
ret.reference = 0; // not used: we'll use a dynamic state for this
return ret;
}
static void *
zink_create_depth_stencil_alpha_state(struct pipe_context *pctx,
const struct pipe_depth_stencil_alpha_state *depth_stencil_alpha)
{
struct zink_depth_stencil_alpha_state *cso = CALLOC_STRUCT(zink_depth_stencil_alpha_state);
if (!cso)
return NULL;
if (depth_stencil_alpha->depth.enabled) {
cso->depth_test = VK_TRUE;
cso->depth_compare_op = compare_op(depth_stencil_alpha->depth.func);
}
if (depth_stencil_alpha->depth.bounds_test) {
cso->depth_bounds_test = VK_TRUE;
cso->min_depth_bounds = depth_stencil_alpha->depth.bounds_min;
cso->max_depth_bounds = depth_stencil_alpha->depth.bounds_max;
}
if (depth_stencil_alpha->stencil[0].enabled) {
cso->stencil_test = VK_TRUE;
cso->stencil_front = stencil_op_state(depth_stencil_alpha->stencil);
}
if (depth_stencil_alpha->stencil[0].enabled)
cso->stencil_back = stencil_op_state(depth_stencil_alpha->stencil + 1);
else
cso->stencil_back = cso->stencil_front;
cso->depth_write = depth_stencil_alpha->depth.writemask;
return cso;
}
static void
zink_bind_depth_stencil_alpha_state(struct pipe_context *pctx, void *cso)
{
struct zink_gfx_pipeline_state* state = &zink_context(pctx)->gfx_pipeline_state;
if (state->depth_stencil_alpha_state != cso) {
state->depth_stencil_alpha_state = cso;
state->hash = 0;
}
}
static void
zink_delete_depth_stencil_alpha_state(struct pipe_context *pctx,
void *depth_stencil_alpha)
{
FREE(depth_stencil_alpha);
}
static float
round_to_granularity(float value, float granularity)
{
return roundf(value / granularity) * granularity;
}
static float
line_width(float width, float granularity, const float range[2])
{
assert(granularity >= 0);
assert(range[0] <= range[1]);
if (granularity > 0)
width = round_to_granularity(width, granularity);
return CLAMP(width, range[0], range[1]);
}
static void *
zink_create_rasterizer_state(struct pipe_context *pctx,
const struct pipe_rasterizer_state *rs_state)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_rasterizer_state *state = CALLOC_STRUCT(zink_rasterizer_state);
if (!state)
return NULL;
state->base = *rs_state;
assert(rs_state->depth_clip_far == rs_state->depth_clip_near);
state->hw_state.depth_clamp = rs_state->depth_clip_near == 0;
state->hw_state.rasterizer_discard = rs_state->rasterizer_discard;
assert(rs_state->fill_front <= PIPE_POLYGON_MODE_POINT);
if (rs_state->fill_back != rs_state->fill_front)
debug_printf("BUG: vulkan doesn't support different front and back fill modes\n");
state->hw_state.polygon_mode = (VkPolygonMode)rs_state->fill_front; // same values
state->hw_state.cull_mode = (VkCullModeFlags)rs_state->cull_face; // same bits
state->hw_state.front_face = rs_state->front_ccw ?
VK_FRONT_FACE_COUNTER_CLOCKWISE :
VK_FRONT_FACE_CLOCKWISE;
state->offset_point = rs_state->offset_point;
state->offset_line = rs_state->offset_line;
state->offset_tri = rs_state->offset_tri;
state->offset_units = rs_state->offset_units;
state->offset_clamp = rs_state->offset_clamp;
state->offset_scale = rs_state->offset_scale;
state->line_width = line_width(rs_state->line_width,
screen->props.limits.lineWidthGranularity,
screen->props.limits.lineWidthRange);
return state;
}
static void
zink_bind_rasterizer_state(struct pipe_context *pctx, void *cso)
{
struct zink_context *ctx = zink_context(pctx);
ctx->rast_state = cso;
if (ctx->rast_state) {
if (ctx->gfx_pipeline_state.rast_state != &ctx->rast_state->hw_state) {
ctx->gfx_pipeline_state.rast_state = &ctx->rast_state->hw_state;
ctx->gfx_pipeline_state.hash = 0;
}
if (ctx->line_width != ctx->rast_state->line_width) {
ctx->line_width = ctx->rast_state->line_width;
ctx->gfx_pipeline_state.hash = 0;
}
}
}
static void
zink_delete_rasterizer_state(struct pipe_context *pctx, void *rs_state)
{
FREE(rs_state);
}
void
zink_context_state_init(struct pipe_context *pctx)
{
pctx->create_vertex_elements_state = zink_create_vertex_elements_state;
pctx->bind_vertex_elements_state = zink_bind_vertex_elements_state;
pctx->delete_vertex_elements_state = zink_delete_vertex_elements_state;
pctx->create_blend_state = zink_create_blend_state;
pctx->bind_blend_state = zink_bind_blend_state;
pctx->delete_blend_state = zink_delete_blend_state;
pctx->create_depth_stencil_alpha_state = zink_create_depth_stencil_alpha_state;
pctx->bind_depth_stencil_alpha_state = zink_bind_depth_stencil_alpha_state;
pctx->delete_depth_stencil_alpha_state = zink_delete_depth_stencil_alpha_state;
pctx->create_rasterizer_state = zink_create_rasterizer_state;
pctx->bind_rasterizer_state = zink_bind_rasterizer_state;
pctx->delete_rasterizer_state = zink_delete_rasterizer_state;
}