Commit 35ffc9ac authored by Erik Faye-Lund 's avatar Erik Faye-Lund

zink: introduce opengl over vulkan

Here's zink, a so far pretty simple vulkan-gallium driver that is able
to translate some applications from OpenGL to Vulkan.

The compiler is quite limited for now, this will be improved on later.
Signed-off-by: Erik Faye-Lund 's avatarErik Faye-Lund <erik.faye-lund@collabora.com>
parent 53eff53d
......@@ -180,6 +180,7 @@ with_gallium_svga = gallium_drivers.contains('svga')
with_gallium_virgl = gallium_drivers.contains('virgl')
with_gallium_swr = gallium_drivers.contains('swr')
with_gallium_lima = gallium_drivers.contains('lima')
with_gallium_zink = gallium_drivers.contains('zink')
if cc.get_id() == 'intel'
if meson.version().version_compare('< 0.49.0')
......@@ -467,6 +468,10 @@ if vdpau_drivers_path == ''
vdpau_drivers_path = join_paths(get_option('libdir'), 'vdpau')
endif
if with_gallium_zink
dep_vulkan = dependency('vulkan')
endif
_xvmc = get_option('gallium-xvmc')
if not system_has_kms_drm
if _xvmc == 'true'
......
......@@ -60,7 +60,7 @@ option(
choices : [
'', 'auto', 'kmsro', 'radeonsi', 'r300', 'r600', 'nouveau', 'freedreno',
'swrast', 'v3d', 'vc4', 'etnaviv', 'tegra', 'i915', 'svga', 'virgl',
'swr', 'panfrost', 'iris', 'lima'
'swr', 'panfrost', 'iris', 'lima', 'zink'
],
description : 'List of gallium drivers to build. If this is set to auto all drivers applicable to the target OS/architecture will be built'
)
......
......@@ -55,6 +55,11 @@ sw_screen_create_named(struct sw_winsys *winsys, const char *driver)
screen = swr_create_screen(winsys);
#endif
#if defined(GALLIUM_ZINK)
if (screen == NULL && strcmp(driver, "zink") == 0)
screen = zink_create_screen(winsys);
#endif
return screen;
}
......@@ -71,6 +76,8 @@ sw_screen_create(struct sw_winsys *winsys)
default_driver = "softpipe";
#elif defined(GALLIUM_SWR)
default_driver = "swr";
#elif defined(GALLIUM_ZINK)
default_driver = "zink";
#else
default_driver = "";
#endif
......
......@@ -12,6 +12,10 @@
* llvmpipe, softpipe, swr.
*/
#ifdef GALLIUM_ZINK
#include "zink/zink_public.h"
#endif
#ifdef GALLIUM_SOFTPIPE
#include "softpipe/sp_public.h"
#endif
......@@ -57,6 +61,11 @@ sw_screen_create_named(struct sw_winsys *winsys, const char *driver)
screen = swr_create_screen(winsys);
#endif
#if defined(GALLIUM_ZINK)
if (screen == NULL && strcmp(driver, "zink") == 0)
screen = zink_create_screen(winsys);
#endif
return screen;
}
......@@ -73,6 +82,8 @@ sw_screen_create(struct sw_winsys *winsys)
default_driver = "softpipe";
#elif defined(GALLIUM_SWR)
default_driver = "swr";
#elif defined(GALLIUM_SWR)
default_driver = "zink";
#else
default_driver = "";
#endif
......
# 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 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 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.
files_libzink = files(
'nir_to_spirv/nir_to_spirv.c',
'nir_to_spirv/spirv_builder.c',
'zink_cmdbuf.c',
'zink_compiler.c',
'zink_context.c',
'zink_fence.c',
'zink_framebuffer.c',
'zink_pipeline.c',
'zink_program.c',
'zink_render_pass.c',
'zink_resource.c',
'zink_screen.c',
'zink_state.c',
'zink_surface.c',
)
libzink = static_library(
'zink',
files_libzink,
c_args : c_vis_args,
include_directories : inc_common,
dependencies: [dep_vulkan, idep_nir_headers],
)
driver_zink = declare_dependency(
compile_args : '-DGALLIUM_ZINK',
link_with : [libzink],
)
/*
* 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 "nir_to_spirv.h"
#include "spirv_builder.h"
#include "nir.h"
#include "pipe/p_state.h"
#include "util/u_memory.h"
struct ntv_context {
struct spirv_builder builder;
SpvId GLSL_std_450;
gl_shader_stage stage;
SpvId inputs[PIPE_MAX_SHADER_INPUTS][4];
SpvId outputs[PIPE_MAX_SHADER_OUTPUTS][4];
SpvId ubos[128];
size_t num_ubos;
SpvId samplers[PIPE_MAX_SAMPLERS];
size_t num_samplers;
SpvId entry_ifaces[PIPE_MAX_SHADER_INPUTS * 4 + PIPE_MAX_SHADER_OUTPUTS * 4];
size_t num_entry_ifaces;
SpvId *defs;
size_t num_defs;
};
static SpvId
get_bvec_type(struct ntv_context *ctx, int num_components)
{
SpvId bool_type = spirv_builder_type_bool(&ctx->builder);
if (num_components > 1)
return spirv_builder_type_vector(&ctx->builder, bool_type,
num_components);
assert(num_components == 1);
return bool_type;
}
static SpvId
get_fvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_components)
{
assert(bit_size == 32); // only 32-bit floats supported so far
SpvId float_type = spirv_builder_type_float(&ctx->builder, bit_size);
if (num_components > 1)
return spirv_builder_type_vector(&ctx->builder, float_type,
num_components);
assert(num_components == 1);
return float_type;
}
static SpvId
get_dest_type(struct ntv_context *ctx, nir_dest *dest)
{
return get_fvec_type(ctx, nir_dest_bit_size(*dest),
nir_dest_num_components(*dest));
}
static SpvId
get_glsl_basetype(struct ntv_context *ctx, enum glsl_base_type type)
{
switch (type) {
case GLSL_TYPE_FLOAT:
return spirv_builder_type_float(&ctx->builder, 32);
case GLSL_TYPE_INT:
return spirv_builder_type_int(&ctx->builder, 32);
case GLSL_TYPE_UINT:
return spirv_builder_type_uint(&ctx->builder, 32);
/* TODO: handle more types */
default:
unreachable("unknown GLSL type");
}
}
static SpvId
get_glsl_type(struct ntv_context *ctx, const struct glsl_type *type)
{
assert(type);
if (glsl_type_is_scalar(type))
return get_glsl_basetype(ctx, glsl_get_base_type(type));
if (glsl_type_is_vector(type))
return spirv_builder_type_vector(&ctx->builder,
get_glsl_basetype(ctx, glsl_get_base_type(type)),
glsl_get_vector_elements(type));
unreachable("we shouldn't get here, I think...");
}
static void
emit_input(struct ntv_context *ctx, struct nir_variable *var)
{
SpvId vec_type = get_glsl_type(ctx, var->type);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassInput,
vec_type);
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassInput);
if (var->name)
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
if (ctx->stage == MESA_SHADER_FRAGMENT) {
switch (var->data.location) {
case VARYING_SLOT_POS:
spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragCoord);
break;
default:
spirv_builder_emit_location(&ctx->builder, var_id,
var->data.driver_location);
break;
}
} else {
spirv_builder_emit_location(&ctx->builder, var_id,
var->data.driver_location);
}
if (var->data.location_frac)
spirv_builder_emit_component(&ctx->builder, var_id,
var->data.location_frac);
if (var->data.interpolation == INTERP_MODE_FLAT)
spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationFlat);
assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
assert(var->data.location_frac < 4);
assert(ctx->inputs[var->data.driver_location][var->data.location_frac] == 0);
ctx->inputs[var->data.driver_location][var->data.location_frac] = var_id;
assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
}
static void
emit_output(struct ntv_context *ctx, struct nir_variable *var)
{
SpvId vec_type = get_glsl_type(ctx, var->type);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassOutput,
vec_type);
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassOutput);
if (var->name)
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
if (ctx->stage == MESA_SHADER_VERTEX) {
switch (var->data.location) {
case VARYING_SLOT_POS:
spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPosition);
break;
case VARYING_SLOT_PSIZ:
spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointSize);
break;
default:
spirv_builder_emit_location(&ctx->builder, var_id,
var->data.driver_location - 1);
}
} else if (ctx->stage == MESA_SHADER_FRAGMENT) {
switch (var->data.location) {
case FRAG_RESULT_DEPTH:
spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragDepth);
break;
default:
spirv_builder_emit_location(&ctx->builder, var_id,
var->data.driver_location);
}
}
if (var->data.location_frac)
spirv_builder_emit_component(&ctx->builder, var_id,
var->data.location_frac);
assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
assert(var->data.location_frac < 4);
assert(ctx->outputs[var->data.driver_location][var->data.location_frac] == 0);
ctx->outputs[var->data.driver_location][var->data.location_frac] = var_id;
assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
}
static SpvDim
type_to_dim(enum glsl_sampler_dim gdim, bool *is_ms)
{
*is_ms = false;
switch (gdim) {
case GLSL_SAMPLER_DIM_1D:
return SpvDim1D;
case GLSL_SAMPLER_DIM_2D:
return SpvDim2D;
case GLSL_SAMPLER_DIM_RECT:
return SpvDimRect;
case GLSL_SAMPLER_DIM_CUBE:
return SpvDimCube;
case GLSL_SAMPLER_DIM_3D:
return SpvDim3D;
case GLSL_SAMPLER_DIM_MS:
*is_ms = true;
return SpvDim2D;
default:
fprintf(stderr, "unknown sampler type %d\n", gdim);
break;
}
return SpvDim2D;
}
static void
emit_sampler(struct ntv_context *ctx, struct nir_variable *var)
{
bool is_ms;
SpvDim dimension = type_to_dim(glsl_get_sampler_dim(var->type), &is_ms);
SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
SpvId image_type = spirv_builder_type_image(&ctx->builder, float_type,
dimension, false, glsl_sampler_type_is_array(var->type), is_ms, 1,
SpvImageFormatUnknown);
SpvId sampled_type = spirv_builder_type_sampled_image(&ctx->builder,
image_type);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassUniformConstant,
sampled_type);
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassUniformConstant);
if (var->name)
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
assert(ctx->num_samplers < ARRAY_SIZE(ctx->samplers));
ctx->samplers[ctx->num_samplers++] = var_id;
spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
var->data.descriptor_set);
spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
}
static void
emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
{
uint32_t size = glsl_count_attribute_slots(var->type, false);
SpvId vec4_type = get_fvec_type(ctx, 32, 4);
SpvId array_length = spirv_builder_const_uint(&ctx->builder, 32, size);
SpvId array_type = spirv_builder_type_array(&ctx->builder, vec4_type,
array_length);
spirv_builder_emit_array_stride(&ctx->builder, array_type, 16);
// wrap UBO-array in a struct
SpvId struct_type = spirv_builder_type_struct(&ctx->builder, &array_type, 1);
if (var->name) {
char struct_name[100];
snprintf(struct_name, sizeof(struct_name), "struct_%s", var->name);
spirv_builder_emit_name(&ctx->builder, struct_type, struct_name);
}
spirv_builder_emit_decoration(&ctx->builder, struct_type,
SpvDecorationBlock);
spirv_builder_emit_member_offset(&ctx->builder, struct_type, 0, 0);
SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
SpvStorageClassUniform,
struct_type);
SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
SpvStorageClassUniform);
if (var->name)
spirv_builder_emit_name(&ctx->builder, var_id, var->name);
assert(ctx->num_ubos < ARRAY_SIZE(ctx->ubos));
ctx->ubos[ctx->num_ubos++] = var_id;
spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
var->data.descriptor_set);
spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
}
static void
emit_uniform(struct ntv_context *ctx, struct nir_variable *var)
{
if (glsl_type_is_sampler(var->type))
emit_sampler(ctx, var);
else if (var->interface_type)
emit_ubo(ctx, var);
}
static SpvId
get_src(struct ntv_context *ctx, nir_src *src)
{
assert(src->is_ssa);
assert(src->ssa->index < ctx->num_defs);
assert(ctx->defs[src->ssa->index] != 0);
return ctx->defs[src->ssa->index];
}
static SpvId
get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
{
assert(!alu->src[src].negate);
assert(!alu->src[src].abs);
SpvId def = get_src(ctx, &alu->src[src].src);
unsigned used_channels = 0;
bool need_swizzle = false;
for (unsigned i = 0; i < NIR_MAX_VEC_COMPONENTS; i++) {
if (!nir_alu_instr_channel_used(alu, src, i))
continue;
used_channels++;
if (alu->src[src].swizzle[i] != i)
need_swizzle = true;
}
assert(used_channels != 0);
unsigned live_channels = nir_src_num_components(alu->src[src].src);
if (used_channels != live_channels)
need_swizzle = true;
if (!need_swizzle)
return def;
int bit_size = nir_src_bit_size(alu->src[src].src);
if (used_channels == 1) {
SpvId result_type = spirv_builder_type_float(&ctx->builder, bit_size);
uint32_t indices[] = { alu->src[src].swizzle[0] };
return spirv_builder_emit_composite_extract(&ctx->builder, result_type,
def, indices,
ARRAY_SIZE(indices));
} else if (live_channels == 1) {
SpvId type = get_fvec_type(ctx, bit_size, used_channels);
SpvId constituents[NIR_MAX_VEC_COMPONENTS];
for (unsigned i = 0; i < used_channels; ++i)
constituents[i] = def;
return spirv_builder_emit_composite_construct(&ctx->builder, type,
constituents,
used_channels);
} else {
uint32_t components[NIR_MAX_VEC_COMPONENTS];
size_t num_components = 0;
for (unsigned i = 0; i < NIR_MAX_VEC_COMPONENTS; i++) {
if (!nir_alu_instr_channel_used(alu, src, i))
continue;
components[num_components++] = alu->src[src].swizzle[i];
}
SpvId vecType = get_fvec_type(ctx, bit_size, used_channels);
return spirv_builder_emit_vector_shuffle(&ctx->builder, vecType,
def, def, components, num_components);
}
}
static void
store_ssa_def(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
{
assert(result != 0);
assert(ssa->index < ctx->num_defs);
ctx->defs[ssa->index] = result;
}
static void
store_dest(struct ntv_context *ctx, nir_dest *dest, SpvId result)
{
assert(dest->is_ssa);
store_ssa_def(ctx, &dest->ssa, result);
}
static void
store_alu_result(struct ntv_context *ctx, nir_alu_dest *dest, SpvId result)
{
assert(!dest->saturate);
return store_dest(ctx, &dest->dest, result);
}
static SpvId
emit_unop(struct ntv_context *ctx, SpvOp op, SpvId type, SpvId src)
{
return spirv_builder_emit_unop(&ctx->builder, op, type, src);
}
static SpvId
emit_binop(struct ntv_context *ctx, SpvOp op, SpvId type,
SpvId src0, SpvId src1)
{
return spirv_builder_emit_binop(&ctx->builder, op, type, src0, src1);
}
static SpvId
emit_triop(struct ntv_context *ctx, SpvOp op, SpvId type,
SpvId src0, SpvId src1, SpvId src2)
{
return spirv_builder_emit_triop(&ctx->builder, op, type, src0, src1, src2);
}
static SpvId
emit_builtin_unop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
SpvId src)
{
SpvId args[] = { src };
return spirv_builder_emit_ext_inst(&ctx->builder, type, ctx->GLSL_std_450,
op, args, ARRAY_SIZE(args));
}
static SpvId
emit_builtin_binop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
SpvId src0, SpvId src1)
{
SpvId args[] = { src0, src1 };
return spirv_builder_emit_ext_inst(&ctx->builder, type, ctx->GLSL_std_450,
op, args, ARRAY_SIZE(args));
}
static SpvId
get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
float values[])
{
assert(bit_size == 32);
if (num_components > 1) {
SpvId components[num_components];
for (int i = 0; i < num_components; i++)
components[i] = spirv_builder_const_float(&ctx->builder, bit_size,
values[i]);
SpvId type = get_fvec_type(ctx, bit_size, num_components);
return spirv_builder_const_composite(&ctx->builder, type, components,
num_components);
}
assert(num_components == 1);
return spirv_builder_const_float(&ctx->builder, bit_size, values[0]);
}
static void
emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
{
SpvId src[nir_op_infos[alu->op].num_inputs];
for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++)
src[i] = get_alu_src(ctx, alu, i);
SpvId dest_type = get_dest_type(ctx, &alu->dest.dest);
SpvId result = 0;
switch (alu->op) {
case nir_op_mov:
assert(nir_op_infos[alu->op].num_inputs == 1);
result = src[0];
break;
#define UNOP(nir_op, spirv_op) \
case nir_op: \
assert(nir_op_infos[alu->op].num_inputs == 1); \
result = emit_unop(ctx, spirv_op, dest_type, src[0]); \
break;
#define BUILTIN_UNOP(nir_op, spirv_op) \
case nir_op: \
assert(nir_op_infos[alu->op].num_inputs == 1); \
result = emit_builtin_unop(ctx, spirv_op, dest_type, src[0]); \
break;
UNOP(nir_op_fneg, SpvOpFNegate)
UNOP(nir_op_fddx, SpvOpDPdx)
UNOP(nir_op_fddy, SpvOpDPdy)
BUILTIN_UNOP(nir_op_fabs, GLSLstd450FAbs)
BUILTIN_UNOP(nir_op_fsqrt, GLSLstd450Sqrt)
BUILTIN_UNOP(nir_op_frsq, GLSLstd450InverseSqrt)
BUILTIN_UNOP(nir_op_flog2, GLSLstd450Log2)
BUILTIN_UNOP(nir_op_fexp2, GLSLstd450Exp2)
BUILTIN_UNOP(nir_op_ffract, GLSLstd450Fract)
BUILTIN_UNOP(nir_op_ffloor, GLSLstd450Floor)
BUILTIN_UNOP(nir_op_fceil, GLSLstd450Ceil)
BUILTIN_UNOP(nir_op_ftrunc, GLSLstd450Trunc)
BUILTIN_UNOP(nir_op_fround_even, GLSLstd450RoundEven)
BUILTIN_UNOP(nir_op_fsign, GLSLstd450FSign)
BUILTIN_UNOP(nir_op_fsin, GLSLstd450Sin)
BUILTIN_UNOP(nir_op_fcos, GLSLstd450Cos)
case nir_op_frcp: {
assert(nir_op_infos[alu->op].num_inputs == 1);
float one[4] = { 1, 1, 1, 1 };
src[1] = src[0];
src[0] = get_fvec_constant(ctx, nir_dest_bit_size(alu->dest.dest),
nir_dest_num_components(alu->dest.dest),
one);
result = emit_binop(ctx, SpvOpFDiv, dest_type, src[0], src[1]);
}
break;
#undef UNOP
#undef BUILTIN_UNOP
#define BINOP(nir_op, spirv_op) \
case nir_op: \
assert(nir_op_infos[alu->op].num_inputs == 2); \
result = emit_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
break;
#define BUILTIN_BINOP(nir_op, spirv_op) \
case nir_op: \
assert(nir_op_infos[alu->op].num_inputs == 2); \
result = emit_builtin_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
break;
BINOP(nir_op_fadd, SpvOpFAdd)
BINOP(nir_op_fsub, SpvOpFSub)
BINOP(nir_op_fmul, SpvOpFMul)
BINOP(nir_op_flt, SpvOpFUnordLessThan)
BINOP(nir_op_fge, SpvOpFUnordGreaterThanEqual)
BUILTIN_BINOP(nir_op_fmin, GLSLstd450FMin)
BUILTIN_BINOP(nir_op_fmax, GLSLstd450FMax)
#undef BINOP
#undef BUILTIN_BINOP
case nir_op_fdot2:
case nir_op_fdot3:
case nir_op_fdot4:
assert(nir_op_infos[alu->op].num_inputs == 2);
result = emit_binop(ctx, SpvOpDot, dest_type, src[0], src[1]);
break;
case nir_op_seq:
case nir_op_sne:
case nir_op_slt: