mirror of https://github.com/bkaradzic/bgfx
Updated glsl-optimizer.
This commit is contained in:
parent
cb0e58172f
commit
8ea16b01bb
|
@ -1,6 +1,16 @@
|
|||
GLSL optimizer Change Log
|
||||
=========================
|
||||
|
||||
|
||||
2016 10
|
||||
-------
|
||||
|
||||
* Metal/GLES: Fixed bad optimization (all code removed) when framebuffer fetch extension is used, but
|
||||
fragment shader does not actually read the incoming color value.
|
||||
* Fixed translation of texelSize().
|
||||
* Fixed translation of texelFetch().
|
||||
|
||||
|
||||
2016 09
|
||||
-------
|
||||
|
||||
|
|
|
@ -76,6 +76,12 @@ Notes
|
|||
1.20. Higher GLSL versions might work, but aren't tested now.
|
||||
* GLSL ES versions 1.00 and 3.00 are supported.
|
||||
|
||||
Status and Future
|
||||
-----------------
|
||||
|
||||
**Note**: As of mid-2016, the project is unlikely to have any significant developments. At Unity we are largely moving to a different
|
||||
shader compilation pipeline, with glsl-optimizer mostly not used. So from my side there won't be significant work done on it :(
|
||||
|
||||
|
||||
Dev Notes
|
||||
---------
|
||||
|
|
|
@ -3830,21 +3830,6 @@ builtin_builder::_all(const glsl_type *type)
|
|||
|
||||
UNOP(not, ir_unop_logic_not, always_available)
|
||||
|
||||
static bool
|
||||
has_lod(const glsl_type *sampler_type)
|
||||
{
|
||||
assert(sampler_type->is_sampler());
|
||||
|
||||
switch (sampler_type->sampler_dimensionality) {
|
||||
case GLSL_SAMPLER_DIM_RECT:
|
||||
case GLSL_SAMPLER_DIM_BUF:
|
||||
case GLSL_SAMPLER_DIM_MS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ir_function_signature *
|
||||
builtin_builder::_textureSize(builtin_available_predicate avail,
|
||||
const glsl_type *return_type,
|
||||
|
@ -3857,7 +3842,7 @@ builtin_builder::_textureSize(builtin_available_predicate avail,
|
|||
ir_texture *tex = new(mem_ctx) ir_texture(ir_txs);
|
||||
tex->set_sampler(new(mem_ctx) ir_dereference_variable(s), return_type);
|
||||
|
||||
if (has_lod(sampler_type)) {
|
||||
if (ir_texture::has_lod(sampler_type)) {
|
||||
ir_variable *lod = in_var(glsl_type::int_type, "lod");
|
||||
sig->parameters.push_tail(lod);
|
||||
tex->lod_info.lod = var_ref(lod);
|
||||
|
@ -4017,7 +4002,7 @@ builtin_builder::_texelFetch(builtin_available_predicate avail,
|
|||
sig->parameters.push_tail(sample);
|
||||
tex->lod_info.sample_index = var_ref(sample);
|
||||
tex->op = ir_txf_ms;
|
||||
} else if (has_lod(sampler_type)) {
|
||||
} else if (ir_texture::has_lod(sampler_type)) {
|
||||
ir_variable *lod = in_var(glsl_type::int_type, "lod");
|
||||
sig->parameters.push_tail(lod);
|
||||
tex->lod_info.lod = var_ref(lod);
|
||||
|
|
|
@ -48,7 +48,7 @@ initialize_mesa_context(struct gl_context *ctx, glslopt_target api)
|
|||
{
|
||||
default:
|
||||
case kGlslTargetOpenGL:
|
||||
ctx->Const.GLSLVersion = 140;
|
||||
ctx->Const.GLSLVersion = 150;
|
||||
break;
|
||||
case kGlslTargetOpenGLES20:
|
||||
ctx->Extensions.OES_standard_derivatives = true;
|
||||
|
|
|
@ -1410,6 +1410,22 @@ ir_texture::set_sampler(ir_dereference *sampler, const glsl_type *type)
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
ir_texture::has_lod(const glsl_type *sampler_type)
|
||||
{
|
||||
assert(sampler_type->is_sampler());
|
||||
|
||||
switch (sampler_type->sampler_dimensionality) {
|
||||
case GLSL_SAMPLER_DIM_RECT:
|
||||
case GLSL_SAMPLER_DIM_BUF:
|
||||
case GLSL_SAMPLER_DIM_MS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ir_swizzle::init_mask(const unsigned *comp, unsigned count)
|
||||
{
|
||||
|
|
|
@ -1922,6 +1922,8 @@ public:
|
|||
/** Set the sampler and type. */
|
||||
void set_sampler(ir_dereference *sampler, const glsl_type *type);
|
||||
|
||||
static bool has_lod(const glsl_type *sampler_type);
|
||||
|
||||
/**
|
||||
* Do a reverse-lookup to translate a string into an ir_texture_opcode.
|
||||
*/
|
||||
|
|
|
@ -826,6 +826,20 @@ void ir_print_glsl_visitor::visit(ir_texture *ir)
|
|||
const bool is_shadow = ir->sampler->type->sampler_shadow;
|
||||
const bool is_array = ir->sampler->type->sampler_array;
|
||||
const bool is_msaa = ir->op == ir_txf_ms;
|
||||
|
||||
if (ir->op == ir_txs)
|
||||
{
|
||||
buffer.asprintf_append("textureSize (");
|
||||
ir->sampler->accept(this);
|
||||
if (ir_texture::has_lod(ir->sampler->type))
|
||||
{
|
||||
buffer.asprintf_append(", ");
|
||||
ir->lod_info.lod->accept(this);
|
||||
}
|
||||
buffer.asprintf_append(")");
|
||||
return;
|
||||
}
|
||||
|
||||
const glsl_type* uv_type = ir->coordinate->type;
|
||||
const int uv_dim = uv_type->vector_elements;
|
||||
int sampler_uv_dim = tex_sampler_dim_size[sampler_dim];
|
||||
|
@ -835,7 +849,7 @@ void ir_print_glsl_visitor::visit(ir_texture *ir)
|
|||
sampler_uv_dim += 1;
|
||||
if (is_msaa)
|
||||
sampler_uv_dim += 1;
|
||||
const bool is_proj = (uv_dim > sampler_uv_dim);
|
||||
const bool is_proj = ((ir->op == ir_tex || ir->op == ir_txb || ir->op == ir_txl || ir->op == ir_txd) && uv_dim > sampler_uv_dim);
|
||||
const bool is_lod = (ir->op == ir_txl);
|
||||
|
||||
#if 0 // BK - disable LOD workarounds.
|
||||
|
@ -869,27 +883,23 @@ void ir_print_glsl_visitor::visit(ir_texture *ir)
|
|||
}
|
||||
#endif // 0
|
||||
|
||||
|
||||
// texture function name
|
||||
//ACS: shadow lookups and lookups with dimensionality included in the name were deprecated in 130
|
||||
if(state->language_version<130)
|
||||
{
|
||||
buffer.asprintf_append ("%s", is_shadow ? "shadow" : "texture");
|
||||
buffer.asprintf_append ("%s", tex_sampler_dim_name[sampler_dim]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ir->op == ir_txf
|
||||
|| ir->op == ir_txf_ms) {
|
||||
buffer.asprintf_append ("texelFetch");
|
||||
}
|
||||
else
|
||||
buffer.asprintf_append ("texture");
|
||||
}
|
||||
// texture function name
|
||||
//ACS: shadow lookups and lookups with dimensionality included in the name were deprecated in 130
|
||||
if(state->language_version<130)
|
||||
{
|
||||
buffer.asprintf_append ("%s", is_shadow ? "shadow" : "texture");
|
||||
buffer.asprintf_append ("%s", tex_sampler_dim_name[sampler_dim]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ir->op == ir_txf || ir->op == ir_txf_ms)
|
||||
buffer.asprintf_append ("texelFetch");
|
||||
else
|
||||
buffer.asprintf_append ("texture");
|
||||
}
|
||||
|
||||
if (is_array && state->EXT_texture_array_enable)
|
||||
buffer.asprintf_append ("Array");
|
||||
|
||||
if (ir->op == ir_tex && is_proj)
|
||||
buffer.asprintf_append ("Proj");
|
||||
if (ir->op == ir_txl)
|
||||
|
@ -932,6 +942,13 @@ void ir_print_glsl_visitor::visit(ir_texture *ir)
|
|||
ir->lod_info.lod->accept(this);
|
||||
}
|
||||
|
||||
// sample index
|
||||
if (ir->op == ir_txf_ms)
|
||||
{
|
||||
buffer.asprintf_append (", ");
|
||||
ir->lod_info.sample_index->accept(this);
|
||||
}
|
||||
|
||||
// grad
|
||||
if (ir->op == ir_txd)
|
||||
{
|
||||
|
|
|
@ -1260,6 +1260,18 @@ static void print_texture_uv (ir_print_metal_visitor* vis, ir_texture* ir, bool
|
|||
|
||||
void ir_print_metal_visitor::visit(ir_texture *ir)
|
||||
{
|
||||
if (ir->op == ir_txs)
|
||||
{
|
||||
ir->sampler->accept(this);
|
||||
buffer.asprintf_append (".get_width(");
|
||||
ir->lod_info.lod->accept(this);
|
||||
buffer.asprintf_append ("), ");
|
||||
ir->sampler->accept(this);
|
||||
buffer.asprintf_append (".get_height(");
|
||||
ir->lod_info.lod->accept(this);
|
||||
buffer.asprintf_append (")");
|
||||
return;
|
||||
}
|
||||
glsl_sampler_dim sampler_dim = (glsl_sampler_dim)ir->sampler->type->sampler_dimensionality;
|
||||
const bool is_shadow = ir->sampler->type->sampler_shadow;
|
||||
const bool is_array = ir->sampler->type->sampler_array;
|
||||
|
|
|
@ -40,12 +40,6 @@
|
|||
|
||||
static int glsl_version = 330;
|
||||
|
||||
//extern "C" void
|
||||
//_mesa_error_no_memory(const char *caller)
|
||||
//{
|
||||
// fprintf(stderr, "Mesa error: out of memory in %s", caller);
|
||||
//}
|
||||
|
||||
static void
|
||||
initialize_context(struct gl_context *ctx, gl_api api)
|
||||
{
|
||||
|
|
|
@ -81,7 +81,7 @@ do_dead_code(exec_list *instructions, bool uniform_locations_assigned)
|
|||
*/
|
||||
if (entry->var->data.mode != ir_var_function_out &&
|
||||
entry->var->data.mode != ir_var_function_inout &&
|
||||
entry->var->data.mode != ir_var_shader_out) {
|
||||
entry->var->data.mode != ir_var_shader_out && entry->var->data.mode != ir_var_shader_inout) {
|
||||
entry->assign->remove();
|
||||
progress = true;
|
||||
|
||||
|
|
14
3rdparty/glsl-optimizer/tests/fragment/bug-bad-framebufferfetch-metal-translation-inES3.txt
vendored
Normal file
14
3rdparty/glsl-optimizer/tests/fragment/bug-bad-framebufferfetch-metal-translation-inES3.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
#version 300 es
|
||||
#extension GL_EXT_shader_framebuffer_fetch : require
|
||||
|
||||
// had a bug where if shader framebuffer fetch extension is enabled,
|
||||
// but not actually used, then translated shader would end up
|
||||
// being empty, since all writes to gl_FragData were wrongly removed.
|
||||
|
||||
#define gl_FragData _glesFragData
|
||||
layout(location = 0) inout mediump vec4 _glesFragData[4];
|
||||
|
||||
in highp vec2 inUV;
|
||||
void main() {
|
||||
gl_FragData[0] = vec4(inUV.x);
|
||||
}
|
15
3rdparty/glsl-optimizer/tests/fragment/bug-bad-framebufferfetch-metal-translation-outES3.txt
vendored
Normal file
15
3rdparty/glsl-optimizer/tests/fragment/bug-bad-framebufferfetch-metal-translation-outES3.txt
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
#version 300 es
|
||||
#extension GL_EXT_shader_framebuffer_fetch : enable
|
||||
layout(location=0) inout mediump vec4 _glesFragData[4];
|
||||
in highp vec2 inUV;
|
||||
void main ()
|
||||
{
|
||||
highp vec4 tmpvar_1;
|
||||
tmpvar_1 = inUV.xxxx;
|
||||
_glesFragData[0] = tmpvar_1;
|
||||
}
|
||||
|
||||
|
||||
// stats: 0 alu 0 tex 0 flow
|
||||
// inputs: 1
|
||||
// #0: inUV (high float) 2x1 [-1]
|
|
@ -0,0 +1,25 @@
|
|||
#include <metal_stdlib>
|
||||
#pragma clang diagnostic ignored "-Wparentheses-equality"
|
||||
using namespace metal;
|
||||
struct xlatMtlShaderInput {
|
||||
float2 inUV;
|
||||
half4 _glesFragData_0 [[color(0)]];
|
||||
};
|
||||
struct xlatMtlShaderOutput {
|
||||
half4 _glesFragData_0 [[color(0)]];
|
||||
};
|
||||
struct xlatMtlShaderUniform {
|
||||
};
|
||||
fragment xlatMtlShaderOutput xlatMtlMain (xlatMtlShaderInput _mtl_i [[stage_in]], constant xlatMtlShaderUniform& _mtl_u [[buffer(0)]])
|
||||
{
|
||||
xlatMtlShaderOutput _mtl_o;
|
||||
float4 tmpvar_1 = 0;
|
||||
tmpvar_1 = _mtl_i.inUV.xxxx;
|
||||
_mtl_o._glesFragData_0 = half4(tmpvar_1);
|
||||
return _mtl_o;
|
||||
}
|
||||
|
||||
|
||||
// stats: 0 alu 0 tex 0 flow
|
||||
// inputs: 1
|
||||
// #0: inUV (high float) 2x1 [-1]
|
|
@ -68,7 +68,7 @@ fragment xlatMtlShaderOutput xlatMtlMain (xlatMtlShaderInput _mtl_i [[stage_in]]
|
|||
tmpvar_11 = _mtl_u._LightColor0.xyz;
|
||||
half shadow_12 = 0;
|
||||
half tmpvar_13 = 0;
|
||||
tmpvar_13 = _ShadowMapTexture.sample_compare(_mtl_xl_shadow_sampler, (float2)(_mtl_i.xlv_TEXCOORD6.xyz).xy, (float)(_mtl_i.xlv_TEXCOORD6.xyz).z);
|
||||
tmpvar_13 = _ShadowMapTexture.sample_compare(_mtl_xl_shadow_sampler, (float2)(_mtl_i.xlv_TEXCOORD6.xyz).xy, saturate((float)(_mtl_i.xlv_TEXCOORD6.xyz).z));
|
||||
half tmpvar_14 = 0;
|
||||
tmpvar_14 = tmpvar_13;
|
||||
shadow_12 = (_mtl_u._LightShadowData.x + (tmpvar_14 * ((half)(1.0) - _mtl_u._LightShadowData.x)));
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#version 300 es
|
||||
uniform highp sampler2D map;
|
||||
layout ( location = 0 ) out mediump vec4 out_color;
|
||||
void main () {
|
||||
vec2 map_size = vec2(textureSize(map, 0));
|
||||
out_color = vec4(map_size.x, map_size.y, 0.0, 0.0);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#version 300 es
|
||||
uniform highp sampler2D map;
|
||||
layout(location=0) out mediump vec4 out_color;
|
||||
void main ()
|
||||
{
|
||||
highp vec4 tmpvar_1;
|
||||
tmpvar_1.zw = vec2(0.0, 0.0);
|
||||
tmpvar_1.xy = vec2(textureSize (map, 0));
|
||||
out_color = tmpvar_1;
|
||||
}
|
||||
|
||||
|
||||
// stats: 2 alu 1 tex 0 flow
|
||||
// textures: 1
|
||||
// #0: map (high 2d) 0x0 [-1]
|
|
@ -0,0 +1,25 @@
|
|||
#include <metal_stdlib>
|
||||
#pragma clang diagnostic ignored "-Wparentheses-equality"
|
||||
using namespace metal;
|
||||
struct xlatMtlShaderInput {
|
||||
};
|
||||
struct xlatMtlShaderOutput {
|
||||
half4 out_color [[color(0)]];
|
||||
};
|
||||
struct xlatMtlShaderUniform {
|
||||
};
|
||||
fragment xlatMtlShaderOutput xlatMtlMain (xlatMtlShaderInput _mtl_i [[stage_in]], constant xlatMtlShaderUniform& _mtl_u [[buffer(0)]]
|
||||
, texture2d<float> map [[texture(0)]], sampler _mtlsmp_map [[sampler(0)]])
|
||||
{
|
||||
xlatMtlShaderOutput _mtl_o;
|
||||
float4 tmpvar_1 = 0;
|
||||
tmpvar_1.zw = float2(0.0, 0.0);
|
||||
tmpvar_1.xy = float2(map.get_width(0), map.get_height(0));
|
||||
_mtl_o.out_color = half4(tmpvar_1);
|
||||
return _mtl_o;
|
||||
}
|
||||
|
||||
|
||||
// stats: 2 alu 1 tex 0 flow
|
||||
// textures: 1
|
||||
// #0: map (high 2d) 0x0 [-1] loc 0
|
|
@ -0,0 +1,7 @@
|
|||
#version 150
|
||||
uniform sampler2DMS tex;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
void main() {
|
||||
color = texelFetch(tex, ivec2(uv), 3);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#version 150
|
||||
uniform sampler2DMS tex;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
void main ()
|
||||
{
|
||||
color = texelFetch (tex, ivec2(uv), 3);
|
||||
}
|
||||
|
||||
|
||||
// stats: 1 alu 1 tex 0 flow
|
||||
// inputs: 1
|
||||
// #0: uv (high float) 2x1 [-1]
|
||||
// textures: 1
|
||||
// #0: tex (high other) 0x0 [-1]
|
|
@ -48,6 +48,7 @@ static PFNGLGETSHADERIVPROC glGetShaderiv;
|
|||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/CGLTypes.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
static CGLContextObj s_GLContext;
|
||||
static CGLContextObj s_GLContext3;
|
||||
static bool s_GL3Active = false;
|
||||
|
|
|
@ -136,7 +136,6 @@ v2f xlat_main( in appdata_full v ) {
|
|||
return o;
|
||||
}
|
||||
attribute vec4 TANGENT;
|
||||
varying highp vec4 xlv_SV_POSITION;
|
||||
varying highp vec2 xlv_TEXCOORD0;
|
||||
varying lowp vec4 xlv_TEXCOORD1;
|
||||
void main() {
|
||||
|
@ -149,7 +148,7 @@ void main() {
|
|||
xlt_v.texcoord1 = vec4( gl_MultiTexCoord1);
|
||||
xlt_v.color = vec4( gl_Color);
|
||||
xl_retval = xlat_main( xlt_v);
|
||||
xlv_SV_POSITION = vec4( xl_retval.pos);
|
||||
gl_Position = vec4(xl_retval.pos);
|
||||
xlv_TEXCOORD0 = vec2( xl_retval.uv);
|
||||
xlv_TEXCOORD1 = vec4( xl_retval.color);
|
||||
}
|
||||
|
|
|
@ -143,7 +143,6 @@ in highp vec4 _uv0;
|
|||
in highp vec4 _uv1;
|
||||
in lowp vec4 _color;
|
||||
in vec4 TANGENT;
|
||||
out highp vec4 xlv_SV_POSITION;
|
||||
out highp vec2 xlv_TEXCOORD0;
|
||||
out lowp vec4 xlv_TEXCOORD1;
|
||||
void main() {
|
||||
|
@ -156,7 +155,7 @@ void main() {
|
|||
xlt_v.texcoord1 = _uv1;
|
||||
xlt_v.color = _color;
|
||||
xl_retval = xlat_main( xlt_v);
|
||||
xlv_SV_POSITION = vec4( xl_retval.pos);
|
||||
gl_Position = vec4(xl_retval.pos);
|
||||
xlv_TEXCOORD0 = vec2( xl_retval.uv);
|
||||
xlv_TEXCOORD1 = vec4( xl_retval.color);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ uniform highp float _VerticalBillboarding;
|
|||
uniform highp mat4 _World2Object;
|
||||
uniform highp vec3 _WorldSpaceCameraPos;
|
||||
uniform highp mat4 glstate_matrix_mvp;
|
||||
varying highp vec4 xlv_SV_POSITION;
|
||||
varying highp vec2 xlv_TEXCOORD0;
|
||||
varying lowp vec4 xlv_TEXCOORD1;
|
||||
void main ()
|
||||
|
@ -135,7 +134,7 @@ void main ()
|
|||
nfadeout_31 = (nfadeout_31 * nfadeout_31);
|
||||
nfadeout_31 = (nfadeout_31 * ffadeout_30);
|
||||
tmpvar_3 = ((nfadeout_31 * _Color) * (_Multiplier * wave_6));
|
||||
xlv_SV_POSITION = (glstate_matrix_mvp * tmpvar_29);
|
||||
gl_Position = (glstate_matrix_mvp * tmpvar_29);
|
||||
xlv_TEXCOORD0 = _glesMultiTexCoord0.xy;
|
||||
xlv_TEXCOORD1 = tmpvar_3;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ in mediump vec3 _inNormal;
|
|||
in highp vec4 _uv0;
|
||||
in highp vec4 _uv1;
|
||||
in lowp vec4 _color;
|
||||
out highp vec4 xlv_SV_POSITION;
|
||||
out highp vec2 xlv_TEXCOORD0;
|
||||
out lowp vec4 xlv_TEXCOORD1;
|
||||
void main ()
|
||||
|
@ -136,7 +135,7 @@ void main ()
|
|||
nfadeout_31 = (nfadeout_31 * nfadeout_31);
|
||||
nfadeout_31 = (nfadeout_31 * ffadeout_30);
|
||||
tmpvar_3 = ((nfadeout_31 * _Color) * (_Multiplier * wave_6));
|
||||
xlv_SV_POSITION = (glstate_matrix_mvp * tmpvar_29);
|
||||
gl_Position = (glstate_matrix_mvp * tmpvar_29);
|
||||
xlv_TEXCOORD0 = _uv0.xy;
|
||||
xlv_TEXCOORD1 = tmpvar_3;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ struct xlatMtlShaderInput {
|
|||
half4 _color [[attribute(4)]];
|
||||
};
|
||||
struct xlatMtlShaderOutput {
|
||||
float4 xlv_SV_POSITION;
|
||||
float4 gl_Position [[position]];
|
||||
float2 xlv_TEXCOORD0;
|
||||
half4 xlv_TEXCOORD1;
|
||||
};
|
||||
|
@ -145,7 +145,7 @@ vertex xlatMtlShaderOutput xlatMtlMain (xlatMtlShaderInput _mtl_i [[stage_in]],
|
|||
nfadeout_31 = (nfadeout_31 * nfadeout_31);
|
||||
nfadeout_31 = (nfadeout_31 * ffadeout_30);
|
||||
tmpvar_3 = half4(((nfadeout_31 * _mtl_u._Color) * (_mtl_u._Multiplier * wave_6)));
|
||||
_mtl_o.xlv_SV_POSITION = (_mtl_u.glstate_matrix_mvp * tmpvar_29);
|
||||
_mtl_o.gl_Position = (_mtl_u.glstate_matrix_mvp * tmpvar_29);
|
||||
_mtl_o.xlv_TEXCOORD0 = _mtl_i._uv0.xy;
|
||||
_mtl_o.xlv_TEXCOORD1 = tmpvar_3;
|
||||
return _mtl_o;
|
||||
|
|
Loading…
Reference in New Issue