mesa/src/gallium/drivers/d3d12/d3d12_gs_variant.cpp
Jesse Natalie de438f381f d3d12: Handle passthrough TCS in the case where eval is bound
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Bill Kristiansen <billkris@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/14399>
2022-01-26 01:31:35 +00:00

490 lines
18 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © Microsoft Corporation
*
* 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.
*/
#include "d3d12_compiler.h"
#include "d3d12_context.h"
#include "d3d12_debug.h"
#include "d3d12_screen.h"
#include "nir_to_dxil.h"
#include "nir.h"
#include "compiler/nir/nir_builder.h"
#include "compiler/nir/nir_builtin_builder.h"
#include "util/u_memory.h"
#include "util/u_simple_shaders.h"
static nir_ssa_def *
nir_cull_face(nir_builder *b, nir_variable *vertices, bool ccw)
{
nir_ssa_def *v0 =
nir_load_deref(b, nir_build_deref_array(b, nir_build_deref_var(b, vertices), nir_imm_int(b, 0)));
nir_ssa_def *v1 =
nir_load_deref(b, nir_build_deref_array(b, nir_build_deref_var(b, vertices), nir_imm_int(b, 1)));
nir_ssa_def *v2 =
nir_load_deref(b, nir_build_deref_array(b, nir_build_deref_var(b, vertices), nir_imm_int(b, 2)));
nir_ssa_def *dir = nir_fdot(b, nir_cross4(b, nir_fsub(b, v1, v0),
nir_fsub(b, v2, v0)),
nir_imm_vec4(b, 0.0, 0.0, -1.0, 0.0));
if (ccw)
return nir_fge(b, nir_imm_int(b, 0), dir);
else
return nir_flt(b, nir_imm_int(b, 0), dir);
}
static d3d12_shader_selector*
d3d12_make_passthrough_gs(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
struct d3d12_shader_selector *gs;
uint64_t varyings = key->varyings.mask;
nir_shader *nir;
struct pipe_shader_state templ;
nir_builder b = nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY,
dxil_get_nir_compiler_options(),
"passthrough");
nir = b.shader;
nir->info.inputs_read = varyings;
nir->info.outputs_written = varyings;
nir->info.gs.input_primitive = GL_POINTS;
nir->info.gs.output_primitive = GL_POINTS;
nir->info.gs.vertices_in = 1;
nir->info.gs.vertices_out = 1;
nir->info.gs.invocations = 1;
nir->info.gs.active_stream_mask = 1;
/* Copy inputs to outputs. */
while (varyings) {
nir_variable *in, *out;
char tmp[100];
const int i = u_bit_scan64(&varyings);
snprintf(tmp, ARRAY_SIZE(tmp), "in_%d", key->varyings.vars[i].driver_location);
in = nir_variable_create(nir,
nir_var_shader_in,
glsl_array_type(key->varyings.vars[i].type, 1, false),
tmp);
in->data.location = i;
in->data.driver_location = key->varyings.vars[i].driver_location;
in->data.interpolation = key->varyings.vars[i].interpolation;
snprintf(tmp, ARRAY_SIZE(tmp), "out_%d", key->varyings.vars[i].driver_location);
out = nir_variable_create(nir,
nir_var_shader_out,
key->varyings.vars[i].type,
tmp);
out->data.location = i;
out->data.driver_location = key->varyings.vars[i].driver_location;
out->data.interpolation = key->varyings.vars[i].interpolation;
nir_deref_instr *in_value = nir_build_deref_array(&b, nir_build_deref_var(&b, in),
nir_imm_int(&b, 0));
nir_copy_deref(&b, nir_build_deref_var(&b, out), in_value);
}
nir_emit_vertex(&b, 0);
nir_end_primitive(&b, 0);
NIR_PASS_V(nir, nir_lower_var_copies);
nir_validate_shader(nir, "in d3d12_create_passthrough_gs");
templ.type = PIPE_SHADER_IR_NIR;
templ.ir.nir = nir;
templ.stream_output.num_outputs = 0;
gs = d3d12_create_shader(ctx, PIPE_SHADER_GEOMETRY, &templ);
return gs;
}
struct emit_primitives_context
{
struct d3d12_context *ctx;
nir_builder b;
unsigned num_vars;
nir_variable *in[MAX_VARYING];
nir_variable *out[MAX_VARYING];
nir_variable *front_facing_var;
nir_loop *loop;
nir_deref_instr *loop_index_deref;
nir_ssa_def *loop_index;
nir_ssa_def *edgeflag_cmp;
nir_ssa_def *front_facing;
};
static bool
d3d12_begin_emit_primitives_gs(struct emit_primitives_context *emit_ctx,
struct d3d12_context *ctx,
struct d3d12_gs_variant_key *key,
uint16_t output_primitive,
unsigned vertices_out)
{
nir_builder *b = &emit_ctx->b;
nir_variable *edgeflag_var = NULL;
nir_variable *pos_var = NULL;
uint64_t varyings = key->varyings.mask;
emit_ctx->ctx = ctx;
emit_ctx->b = nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY,
dxil_get_nir_compiler_options(),
"edgeflags");
nir_shader *nir = b->shader;
nir->info.inputs_read = varyings;
nir->info.outputs_written = varyings;
nir->info.gs.input_primitive = GL_TRIANGLES;
nir->info.gs.output_primitive = output_primitive;
nir->info.gs.vertices_in = 3;
nir->info.gs.vertices_out = vertices_out;
nir->info.gs.invocations = 1;
nir->info.gs.active_stream_mask = 1;
while (varyings) {
char tmp[100];
const int i = u_bit_scan64(&varyings);
snprintf(tmp, ARRAY_SIZE(tmp), "in_%d", emit_ctx->num_vars);
emit_ctx->in[emit_ctx->num_vars] = nir_variable_create(nir,
nir_var_shader_in,
glsl_array_type(key->varyings.vars[i].type, 3, 0),
tmp);
emit_ctx->in[emit_ctx->num_vars]->data.location = i;
emit_ctx->in[emit_ctx->num_vars]->data.driver_location = key->varyings.vars[i].driver_location;
emit_ctx->in[emit_ctx->num_vars]->data.interpolation = key->varyings.vars[i].interpolation;
/* Don't create an output for the edge flag variable */
if (i == VARYING_SLOT_EDGE) {
edgeflag_var = emit_ctx->in[emit_ctx->num_vars];
continue;
} else if (i == VARYING_SLOT_POS) {
pos_var = emit_ctx->in[emit_ctx->num_vars];
}
snprintf(tmp, ARRAY_SIZE(tmp), "out_%d", emit_ctx->num_vars);
emit_ctx->out[emit_ctx->num_vars] = nir_variable_create(nir,
nir_var_shader_out,
key->varyings.vars[i].type,
tmp);
emit_ctx->out[emit_ctx->num_vars]->data.location = i;
emit_ctx->out[emit_ctx->num_vars]->data.driver_location = key->varyings.vars[i].driver_location;
emit_ctx->out[emit_ctx->num_vars]->data.interpolation = key->varyings.vars[i].interpolation;
emit_ctx->num_vars++;
}
if (key->has_front_face) {
emit_ctx->front_facing_var = nir_variable_create(nir,
nir_var_shader_out,
glsl_uint_type(),
"gl_FrontFacing");
emit_ctx->front_facing_var->data.location = VARYING_SLOT_VAR12;
emit_ctx->front_facing_var->data.driver_location = emit_ctx->num_vars;
emit_ctx->front_facing_var->data.interpolation = INTERP_MODE_FLAT;
}
/* Temporary variable "loop_index" to loop over input vertices */
nir_function_impl *impl = nir_shader_get_entrypoint(nir);
nir_variable *loop_index_var =
nir_local_variable_create(impl, glsl_uint_type(), "loop_index");
emit_ctx->loop_index_deref = nir_build_deref_var(b, loop_index_var);
nir_store_deref(b, emit_ctx->loop_index_deref, nir_imm_int(b, 0), 1);
nir_ssa_def *diagonal_vertex = NULL;
if (key->edge_flag_fix) {
nir_ssa_def *prim_id = nir_load_primitive_id(b);
nir_ssa_def *odd = nir_build_alu(b, nir_op_imod,
prim_id,
nir_imm_int(b, 2),
NULL, NULL);
diagonal_vertex = nir_bcsel(b, nir_i2b(b, odd),
nir_imm_int(b, 2),
nir_imm_int(b, 1));
}
if (key->cull_mode != PIPE_FACE_NONE || key->has_front_face) {
if (key->cull_mode == PIPE_FACE_BACK)
emit_ctx->edgeflag_cmp = nir_cull_face(b, pos_var, key->front_ccw);
else if (key->cull_mode == PIPE_FACE_FRONT)
emit_ctx->edgeflag_cmp = nir_cull_face(b, pos_var, !key->front_ccw);
if (key->has_front_face) {
if (key->cull_mode == PIPE_FACE_BACK)
emit_ctx->front_facing = emit_ctx->edgeflag_cmp;
else
emit_ctx->front_facing = nir_cull_face(b, pos_var, key->front_ccw);
emit_ctx->front_facing = nir_i2i32(b, emit_ctx->front_facing);
}
}
/**
* while {
* if (loop_index >= 3)
* break;
*/
emit_ctx->loop = nir_push_loop(b);
emit_ctx->loop_index = nir_load_deref(b, emit_ctx->loop_index_deref);
nir_ssa_def *cmp = nir_ige(b, emit_ctx->loop_index,
nir_imm_int(b, 3));
nir_if *loop_check = nir_push_if(b, cmp);
nir_jump(b, nir_jump_break);
nir_pop_if(b, loop_check);
if (edgeflag_var) {
nir_ssa_def *edge_flag =
nir_load_deref(b, nir_build_deref_array(b, nir_build_deref_var(b, edgeflag_var), emit_ctx->loop_index));
nir_ssa_def *is_edge = nir_feq(b, nir_channel(b, edge_flag, 0), nir_imm_float(b, 1.0));
if (emit_ctx->edgeflag_cmp)
emit_ctx->edgeflag_cmp = nir_iand(b, emit_ctx->edgeflag_cmp, is_edge);
else
emit_ctx->edgeflag_cmp = is_edge;
}
if (key->edge_flag_fix) {
nir_ssa_def *is_edge = nir_ine(b, emit_ctx->loop_index, diagonal_vertex);
if (emit_ctx->edgeflag_cmp)
emit_ctx->edgeflag_cmp = nir_iand(b, emit_ctx->edgeflag_cmp, is_edge);
else
emit_ctx->edgeflag_cmp = is_edge;
}
return true;
}
static struct d3d12_shader_selector *
d3d12_finish_emit_primitives_gs(struct emit_primitives_context *emit_ctx, bool end_primitive)
{
struct pipe_shader_state templ;
nir_builder *b = &emit_ctx->b;
nir_shader *nir = b->shader;
/**
* loop_index++;
* }
*/
nir_store_deref(b, emit_ctx->loop_index_deref, nir_iadd_imm(b, emit_ctx->loop_index, 1), 1);
nir_pop_loop(b, emit_ctx->loop);
if (end_primitive)
nir_end_primitive(b, 0);
nir_validate_shader(nir, "in d3d12_lower_edge_flags");
NIR_PASS_V(nir, nir_lower_var_copies);
templ.type = PIPE_SHADER_IR_NIR;
templ.ir.nir = nir;
templ.stream_output.num_outputs = 0;
return d3d12_create_shader(emit_ctx->ctx, PIPE_SHADER_GEOMETRY, &templ);
}
static d3d12_shader_selector*
d3d12_emit_points(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
struct emit_primitives_context emit_ctx = {0};
nir_builder *b = &emit_ctx.b;
d3d12_begin_emit_primitives_gs(&emit_ctx, ctx, key, GL_POINTS, 3);
/**
* if (edge_flag)
* out_position = in_position;
* else
* out_position = vec4(-2.0, -2.0, 0.0, 1.0); // Invalid position
*
* [...] // Copy other variables
*
* EmitVertex();
*/
for (unsigned i = 0; i < emit_ctx.num_vars; ++i) {
nir_ssa_def *index = (key->flat_varyings & (1ull << emit_ctx.in[i]->data.location)) ?
nir_imm_int(b, (key->flatshade_first ? 0 : 2)) : emit_ctx.loop_index;
nir_deref_instr *in_value = nir_build_deref_array(b, nir_build_deref_var(b, emit_ctx.in[i]), index);
if (emit_ctx.in[i]->data.location == VARYING_SLOT_POS && emit_ctx.edgeflag_cmp) {
nir_if *edge_check = nir_push_if(b, emit_ctx.edgeflag_cmp);
nir_copy_deref(b, nir_build_deref_var(b, emit_ctx.out[i]), in_value);
nir_if *edge_else = nir_push_else(b, edge_check);
nir_store_deref(b, nir_build_deref_var(b, emit_ctx.out[i]),
nir_imm_vec4(b, -2.0, -2.0, 0.0, 1.0), 0xf);
nir_pop_if(b, edge_else);
} else {
nir_copy_deref(b, nir_build_deref_var(b, emit_ctx.out[i]), in_value);
}
}
if (key->has_front_face)
nir_store_var(b, emit_ctx.front_facing_var, emit_ctx.front_facing, 0x1);
nir_emit_vertex(b, 0);
return d3d12_finish_emit_primitives_gs(&emit_ctx, false);
}
static d3d12_shader_selector*
d3d12_emit_lines(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
struct emit_primitives_context emit_ctx = {0};
nir_builder *b = &emit_ctx.b;
d3d12_begin_emit_primitives_gs(&emit_ctx, ctx, key, GL_LINE_STRIP, 6);
nir_ssa_def *next_index = nir_imod(b, nir_iadd_imm(b, emit_ctx.loop_index, 1), nir_imm_int(b, 3));
/* First vertex */
for (unsigned i = 0; i < emit_ctx.num_vars; ++i) {
nir_ssa_def *index = (key->flat_varyings & (1ull << emit_ctx.in[i]->data.location)) ?
nir_imm_int(b, (key->flatshade_first ? 0 : 2)) : emit_ctx.loop_index;
nir_deref_instr *in_value = nir_build_deref_array(b, nir_build_deref_var(b, emit_ctx.in[i]), index);
nir_copy_deref(b, nir_build_deref_var(b, emit_ctx.out[i]), in_value);
}
if (key->has_front_face)
nir_store_var(b, emit_ctx.front_facing_var, emit_ctx.front_facing, 0x1);
nir_emit_vertex(b, 0);
/* Second vertex. If not an edge, use same position as first vertex */
for (unsigned i = 0; i < emit_ctx.num_vars; ++i) {
nir_ssa_def *index = next_index;
if (emit_ctx.in[i]->data.location == VARYING_SLOT_POS)
index = nir_bcsel(b, emit_ctx.edgeflag_cmp, next_index, emit_ctx.loop_index);
else if (key->flat_varyings & (1ull << emit_ctx.in[i]->data.location))
index = nir_imm_int(b, 2);
nir_copy_deref(b, nir_build_deref_var(b, emit_ctx.out[i]),
nir_build_deref_array(b, nir_build_deref_var(b, emit_ctx.in[i]), index));
}
if (key->has_front_face)
nir_store_var(b, emit_ctx.front_facing_var, emit_ctx.front_facing, 0x1);
nir_emit_vertex(b, 0);
nir_end_primitive(b, 0);
return d3d12_finish_emit_primitives_gs(&emit_ctx, false);
}
static d3d12_shader_selector*
d3d12_emit_triangles(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
struct emit_primitives_context emit_ctx = {0};
nir_builder *b = &emit_ctx.b;
d3d12_begin_emit_primitives_gs(&emit_ctx, ctx, key, GL_TRIANGLE_STRIP, 3);
/**
* [...] // Copy variables
*
* EmitVertex();
*/
nir_ssa_def *incr = NULL;
if (key->provoking_vertex > 0)
incr = nir_imm_int(b, key->provoking_vertex);
else
incr = nir_imm_int(b, 3);
if (key->alternate_tri) {
nir_ssa_def *odd = nir_imod(b, nir_load_primitive_id(b), nir_imm_int(b, 2));
incr = nir_isub(b, incr, odd);
}
assert(incr != NULL);
nir_ssa_def *index = nir_imod(b, nir_iadd(b, emit_ctx.loop_index, incr), nir_imm_int(b, 3));
for (unsigned i = 0; i < emit_ctx.num_vars; ++i) {
nir_deref_instr *in_value = nir_build_deref_array(b, nir_build_deref_var(b, emit_ctx.in[i]), index);
nir_copy_deref(b, nir_build_deref_var(b, emit_ctx.out[i]), in_value);
}
nir_emit_vertex(b, 0);
return d3d12_finish_emit_primitives_gs(&emit_ctx, true);
}
static uint32_t
hash_gs_variant_key(const void *key)
{
return _mesa_hash_data(key, sizeof(struct d3d12_gs_variant_key));
}
static bool
equals_gs_variant_key(const void *a, const void *b)
{
return memcmp(a, b, sizeof(struct d3d12_gs_variant_key)) == 0;
}
void
d3d12_gs_variant_cache_init(struct d3d12_context *ctx)
{
ctx->gs_variant_cache = _mesa_hash_table_create(NULL, NULL, equals_gs_variant_key);
}
static void
delete_entry(struct hash_entry *entry)
{
d3d12_shader_free((d3d12_shader_selector *)entry->data);
}
void
d3d12_gs_variant_cache_destroy(struct d3d12_context *ctx)
{
_mesa_hash_table_destroy(ctx->gs_variant_cache, delete_entry);
}
static struct d3d12_shader_selector *
create_geometry_shader_variant(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
d3d12_shader_selector *gs = NULL;
if (key->passthrough)
gs = d3d12_make_passthrough_gs(ctx, key);
else if (key->provoking_vertex > 0 || key->alternate_tri)
gs = d3d12_emit_triangles(ctx, key);
else if (key->fill_mode == PIPE_POLYGON_MODE_POINT)
gs = d3d12_emit_points(ctx, key);
else if (key->fill_mode == PIPE_POLYGON_MODE_LINE)
gs = d3d12_emit_lines(ctx, key);
if (gs) {
gs->is_variant = true;
gs->gs_key = *key;
}
return gs;
}
d3d12_shader_selector *
d3d12_get_gs_variant(struct d3d12_context *ctx, struct d3d12_gs_variant_key *key)
{
uint32_t hash = hash_gs_variant_key(key);
struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(ctx->gs_variant_cache,
hash, key);
if (!entry) {
d3d12_shader_selector *gs = create_geometry_shader_variant(ctx, key);
entry = _mesa_hash_table_insert_pre_hashed(ctx->gs_variant_cache,
hash, &gs->gs_key, gs);
assert(entry);
}
return (d3d12_shader_selector *)entry->data;
}