Files
Xenia-Canary/src/xenia/gpu/glsl_shader_translator.cc

1607 lines
49 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/gpu/glsl_shader_translator.h"
#include <unordered_set>
namespace xe {
namespace gpu {
using namespace xe::gpu::ucode;
constexpr int kMaxInterpolators = 16;
constexpr int kMaxTemporaryRegisters = 64;
#define EmitSource(...) source_.AppendFormat(__VA_ARGS__)
#define EmitSourceDepth(...) \
source_.Append(" "); \
source_.Append(depth_prefix_); \
source_.AppendFormat(__VA_ARGS__)
const char* GetVertexFormatTypeName(VertexFormat format, bool is_signed) {
switch (format) {
case VertexFormat::k_32:
case VertexFormat::k_32_FLOAT:
return "float";
case VertexFormat::k_16_16:
case VertexFormat::k_32_32:
case VertexFormat::k_16_16_FLOAT:
case VertexFormat::k_32_32_FLOAT:
return "vec2";
case VertexFormat::k_10_11_11:
return is_signed ? "int" : "uint";
case VertexFormat::k_2_10_10_10:
return is_signed ? "int" : "uint";
case VertexFormat::k_11_11_10:
case VertexFormat::k_32_32_32_FLOAT:
return "vec3";
case VertexFormat::k_8_8_8_8:
case VertexFormat::k_16_16_16_16:
case VertexFormat::k_32_32_32_32:
case VertexFormat::k_16_16_16_16_FLOAT:
case VertexFormat::k_32_32_32_32_FLOAT:
return "vec4";
default:
assert_always();
return "vec4";
}
}
GlslShaderTranslator::GlslShaderTranslator(Dialect dialect)
: dialect_(dialect) {}
GlslShaderTranslator::~GlslShaderTranslator() = default;
void GlslShaderTranslator::Reset() {
ShaderTranslator::Reset();
depth_ = 0;
depth_prefix_[0] = 0;
source_.Reset();
}
void GlslShaderTranslator::EmitTranslationError(const char* message) {
ShaderTranslator::EmitTranslationError(message);
EmitSourceDepth("// TRANSLATION ERROR: %s\n", message);
}
void GlslShaderTranslator::EmitUnimplementedTranslationError() {
ShaderTranslator::EmitUnimplementedTranslationError();
EmitSourceDepth("// UNIMPLEMENTED TRANSLATION\n");
}
void GlslShaderTranslator::Indent() {
depth_prefix_[depth_] = ' ';
depth_prefix_[depth_ + 1] = ' ';
depth_prefix_[depth_ + 2] = 0;
depth_ += 2;
}
void GlslShaderTranslator::Unindent() {
depth_prefix_[depth_] = 0;
depth_prefix_[depth_ - 1] = 0;
depth_prefix_[depth_ - 2] = 0;
depth_ -= 2;
}
void GlslShaderTranslator::StartTranslation() {
// Tons of boilerplate for shaders, here.
// We have a large amount of shared state defining uniforms and some common
// utility functions used in both vertex and pixel shaders.
EmitSource(R"(#version 450
#extension all : warn
#extension GL_ARB_bindless_texture : require
#extension GL_ARB_explicit_uniform_location : require
#extension GL_ARB_shader_draw_parameters : require
#extension GL_ARB_shader_storage_buffer_object : require
#extension GL_ARB_shading_language_420pack : require
#extension GL_ARB_fragment_coord_conventions : require
#define FLT_MAX 3.402823466e+38
precision highp float;
precision highp int;
layout(std140, column_major) uniform;
layout(std430, column_major) buffer;
// This must match DrawBatcher::CommonHeader.
struct StateData {
vec4 window_scale; // 0x0
vec4 vtx_fmt; // 0x10
vec4 alpha_test; // 0x20
uint ps_param_gen; // 0x30
uint padding[3]; // 0x34
// TODO(benvanik): variable length.
uvec2 texture_samplers[32]; // 0x40
uint texture_swizzles[32]; // 0x140
vec4 float_consts[512]; // 0x1C0
int bool_consts[8]; // 0x21C0
int loop_consts[32]; // 0x2240
};
layout(binding = 0) readonly buffer State {
StateData states[];
};
struct VertexData {
vec4 o[16];
};
)");
// http://www.nvidia.com/object/cube_map_ogl_tutorial.html
// http://developer.amd.com/wordpress/media/2012/10/R600_Instruction_Set_Architecture.pdf
// src0 = Rn.zzxy, src1 = Rn.yxzz
// dst.W = FaceId;
// dst.Z = 2.0f * MajorAxis;
// dst.Y = S cube coordinate;
// dst.X = T cube coordinate;
/*
major axis
direction target sc tc ma
---------- ------------------------------------ --- --- ---
+rx GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT=0 -rz -ry rx
-rx GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT=1 +rz -ry rx
+ry GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT=2 +rx +rz ry
-ry GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT=3 +rx -rz ry
+rz GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT=4 +rx -ry rz
-rz GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT=5 -rx -ry rz
*/
EmitSource(R"(
vec4 cube(vec4 src0, vec4 src1) {
vec3 src = vec3(src1.y, src1.x, src1.z);
vec3 abs_src = abs(src);
int face_id;
float sc;
float tc;
float ma;
if (abs_src.x > abs_src.y && abs_src.x > abs_src.z) {
if (src.x > 0.0) {
face_id = 0; sc = -abs_src.z; tc = -abs_src.y; ma = abs_src.x;
} else {
face_id = 1; sc = abs_src.z; tc = -abs_src.y; ma = abs_src.x;
}
} else if (abs_src.y > abs_src.x && abs_src.y > abs_src.z) {
if (src.y > 0.0) {
face_id = 2; sc = abs_src.x; tc = abs_src.z; ma = abs_src.y;
} else {
face_id = 3; sc = abs_src.x; tc = -abs_src.z; ma = abs_src.y;
}
} else {
if (src.z > 0.0) {
face_id = 4; sc = abs_src.x; tc = -abs_src.y; ma = abs_src.z;
} else {
face_id = 5; sc = -abs_src.x; tc = -abs_src.y; ma = abs_src.z;
}
}
float s = (sc / ma + 1.0) / 2.0;
float t = (tc / ma + 1.0) / 2.0;
return vec4(t, s, 2.0 * ma, float(face_id));
}
)");
if (is_vertex_shader()) {
EmitSource(R"(
out gl_PerVertex {
vec4 gl_Position;
float gl_PointSize;
float gl_ClipDistance[];
};
layout(location = 0) flat out uint draw_id;
layout(location = 1) out VertexData vtx;
vec3 get_10_11_11_u(const uint data_in) {
vec3 vec;
vec.x = bitfieldExtract(data_in, 22, 10);
vec.y = bitfieldExtract(data_in, 11, 11);
vec.z = bitfieldExtract(data_in, 0, 11);
return vec;
}
vec3 get_10_11_11_s(const int data_in) {
vec3 vec;
vec.x = bitfieldExtract(data_in, 22, 10);
vec.y = bitfieldExtract(data_in, 11, 11);
vec.z = bitfieldExtract(data_in, 0, 11);
return vec;
}
vec4 get_2_10_10_10_u(const uint data_in) {
vec4 vec;
vec.x = bitfieldExtract(data_in, 20, 10);
vec.y = bitfieldExtract(data_in, 10, 10);
vec.z = bitfieldExtract(data_in, 0, 10);
vec.w = bitfieldExtract(data_in, 30, 2);
return vec;
}
vec4 get_2_10_10_10_s(const int data_in) {
vec4 vec;
vec.x = bitfieldExtract(data_in, 20, 10);
vec.y = bitfieldExtract(data_in, 10, 10);
vec.z = bitfieldExtract(data_in, 0, 10);
vec.w = bitfieldExtract(data_in, 30, 2);
return vec;
}
vec4 applyTransform(const in StateData state, vec4 pos) {
if (state.vtx_fmt.w == 0.0) {
// w is 1/W0, so fix it.
pos.w = 1.0 / pos.w;
}
// Already multiplied by 1/W0, so pull it out.
pos.xyz = mix(pos.xyz, pos.xyz / pos.w, notEqual(state.vtx_fmt.xyz, vec3(0.0)));
pos.xy *= state.window_scale.xy;
return pos;
}
void processVertex(const in StateData state);
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 1.0;
const StateData state = states[gl_DrawIDARB];
processVertex(state);
gl_Position = applyTransform(state, gl_Position);
draw_id = gl_DrawIDARB;
}
)");
} else {
EmitSource(R"(
float getWeights1D(sampler1D tex, float texCoord) {
return fract(texCoord * textureSize(tex, 0));
}
vec2 getWeights2D(sampler2D tex, vec2 texCoord) {
return fract(texCoord * textureSize(tex, 0));
}
vec3 getWeights3D(sampler3D tex, vec3 texCoord) {
return fract(texCoord * textureSize(tex, 0));
}
layout(origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;
layout(location = 0) flat in uint draw_id;
layout(location = 1) in VertexData vtx;
layout(location = 0) out vec4 oC[4];
void applyAlphaTest(int alpha_func, float alpha_ref) {
bool passes = false;
switch (alpha_func) {
case 0: break;
case 1: if (oC[0].a < alpha_ref) passes = true; break;
case 2: if (oC[0].a == alpha_ref) passes = true; break;
case 3: if (oC[0].a <= alpha_ref) passes = true; break;
case 4: if (oC[0].a > alpha_ref) passes = true; break;
case 5: if (oC[0].a != alpha_ref) passes = true; break;
case 6: if (oC[0].a >= alpha_ref) passes = true; break;
case 7: passes = true; break;
};
if (!passes) discard;
}
void processFragment(const in StateData state);
void main() {
const StateData state = states[draw_id];
processFragment(state);
if (state.alpha_test.x != 0.0) {
applyAlphaTest(int(state.alpha_test.y), state.alpha_test.z);
}
}
)");
}
// Add vertex shader input declarations.
if (is_vertex_shader()) {
std::unordered_set<uint64_t> defined_locations;
for (auto& binding : vertex_bindings()) {
for (auto& attrib : binding.attributes) {
uint64_t key = (static_cast<uint64_t>(binding.fetch_constant) << 32) |
attrib.fetch_instr.attributes.offset;
if (defined_locations.count(key)) {
// Already defined.
continue;
}
defined_locations.insert(key);
const char* type_name =
GetVertexFormatTypeName(attrib.fetch_instr.attributes.data_format,
attrib.fetch_instr.attributes.is_signed);
EmitSource("layout(location = %d) in %s vf%u_%d;\n",
attrib.attrib_index, type_name, binding.fetch_constant,
attrib.fetch_instr.attributes.offset);
}
}
}
// Enter the main function, where all of our shader lives.
if (is_vertex_shader()) {
EmitSource("void processVertex(const in StateData state) {\n");
} else {
EmitSource("void processFragment(const in StateData state) {\n");
}
// Predicate temp, clause-local.
EmitSource(" bool p0 = false;\n");
// Address register when using absolute addressing.
EmitSource(" int a0 = 0;\n");
// Loop index stack - .x is the active loop, shifted right to yzw on push.
EmitSource(" ivec4 aL = ivec4(0);\n");
// Loop counter stack, .x is the active loop.
// Represents number of times remaining to loop.
EmitSource(" ivec4 loop_count = ivec4(0);\n");
// Previous Vector result (used as a scratch).
EmitSource(" vec4 pv;\n");
// Previous Scalar result (used for RETAIN_PREV).
EmitSource(" float ps;\n");
// Temps for source register values.
EmitSource(" vec4 src0;\n");
EmitSource(" vec4 src1;\n");
EmitSource(" vec4 src2;\n");
// Temporary registers.
if (is_vertex_shader()) {
EmitSource(" vec4 r[64];\n");
// FIXME: We're probably supposed to use a vs_param_gen here.
EmitSource(" r[0].x = gl_VertexID;\n");
} else {
// Bring interpolators from vertex shader into temporary registers.
EmitSource(" vec4 r[64];\n");
for (int i = 0; i < kMaxInterpolators; ++i) {
EmitSource(" r[%d] = vtx.o[%d];\n", i, i);
}
EmitSource(" if (state.ps_param_gen < 16) {\n");
EmitSource(
" vec4 ps_param_gen = vec4(gl_FragCoord.xy, gl_PointCoord.xy);\n");
EmitSource(" ps_param_gen.x *= (gl_FrontFacing ? 1.0 : -1.0);\n");
// This is insane, but r[ps_param_gen] causes nvidia to fully deopt?
// EmitSource(" r[state.ps_param_gen] = ps_param_gen;\n");
// FIXME: Branches are still generated for registers that are never used!
// May need a usage map?
for (int i = 0; i < kMaxInterpolators; i++) {
EmitSource(
" r[%d] = mix(r[%d], ps_param_gen, bvec4(state.ps_param_gen == "
"%d));\n",
i, i, i);
}
EmitSource(" }\n");
}
// Master loop and switch for flow control.
EmitSourceDepth("int pc = 0;\n");
EmitSourceDepth("do {\n");
Indent();
EmitSourceDepth("switch (pc) {\n");
EmitSourceDepth("case 0x0:\n");
}
std::vector<uint8_t> GlslShaderTranslator::CompleteTranslation() {
// End of master switch.
EmitSourceDepth("default: pc = 0xFFFF; break;\n");
EmitSourceDepth("}; // switch\n");
Unindent();
EmitSourceDepth("} while (pc != 0xFFFF); // do while\n");
// End of process*() function.
EmitSource("}\n");
return source_.ToBytes();
}
void GlslShaderTranslator::ProcessLabel(uint32_t cf_index) {
// Case 0x0 is already defined at this point.
if (cf_index != 0x0) {
EmitSourceDepth("case 0x%X:\n", cf_index);
}
}
void GlslShaderTranslator::ProcessControlFlowNopInstruction(uint32_t cf_index) {
EmitSource("// cnop\n");
}
void GlslShaderTranslator::ProcessControlFlowInstructionBegin(
uint32_t cf_index) {
cf_wrote_pc_ = false;
Indent();
}
void GlslShaderTranslator::ProcessControlFlowInstructionEnd(uint32_t cf_index) {
if (!cf_wrote_pc_) {
EmitSourceDepth("// Falling through to L%u\n", cf_index + 1);
}
Unindent();
}
void GlslShaderTranslator::ProcessExecInstructionBegin(
const ParsedExecInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
cf_exec_pred_ = false;
switch (instr.type) {
case ParsedExecInstruction::Type::kUnconditional:
EmitSourceDepth("{\n");
break;
case ParsedExecInstruction::Type::kConditional:
EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) %c= 0) {\n",
instr.bool_constant_index / 32,
instr.bool_constant_index % 32,
instr.condition ? '!' : '=');
break;
case ParsedExecInstruction::Type::kPredicated:
cf_exec_pred_ = true;
cf_exec_pred_cond_ = instr.condition;
EmitSourceDepth("if (%cp0) {\n", instr.condition ? ' ' : '!');
break;
}
Indent();
}
void GlslShaderTranslator::ProcessExecInstructionEnd(
const ParsedExecInstruction& instr) {
if (instr.is_end) {
EmitSourceDepth("pc = 0xFFFF;\n");
EmitSourceDepth("break;\n");
cf_wrote_pc_ = true;
}
Unindent();
EmitSourceDepth("}\n");
}
void GlslShaderTranslator::ProcessLoopStartInstruction(
const ParsedLoopStartInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
// Setup counter.
EmitSourceDepth(
"loop_count = ivec4(state.loop_consts[%u].x & 0xFF, "
"loop_count.x, loop_count.y, loop_count.z);\n",
instr.loop_constant_index);
// Setup relative indexing.
if (instr.is_repeat) {
// Reuse the current loop index.
EmitSourceDepth("aL = ivec4(aL.x, aL.x, aL.y, aL.z);\n");
} else {
// Push new loop starting index.
EmitSourceDepth(
"aL = ivec4((state.loop_consts[%u] >> 8) & 0xFF, aL.x, aL.y, aL.z);\n",
instr.loop_constant_index);
}
// Quick skip loop if zero count.
EmitSourceDepth("if (loop_count.x == 0) {\n");
EmitSourceDepth(" pc = 0x%X; // Skip loop to L%d\n",
instr.loop_skip_address, instr.loop_skip_address);
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pc = 0x%X; // Fallthrough to loop body L%d\n",
instr.dword_index + 1, instr.dword_index + 1);
EmitSourceDepth("}\n");
EmitSourceDepth("break;\n");
cf_wrote_pc_ = true;
}
void GlslShaderTranslator::ProcessLoopEndInstruction(
const ParsedLoopEndInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
// Decrement loop counter, and if we are done break out.
EmitSourceDepth("if (--loop_count.x == 0");
if (instr.is_predicated_break) {
// If the predicate condition is met we 'break;' out of the loop.
// Need to restore stack and fall through to the next cf.
EmitSource(" || %cp0) {\n", instr.predicate_condition ? ' ' : '!');
} else {
EmitSource(") {\n");
}
Indent();
// Loop completed - pop and fall through to next cf.
EmitSourceDepth(
"loop_count = ivec4(loop_count.y, loop_count.z, loop_count.w, 0);\n");
EmitSourceDepth("aL = ivec4(aL.y, aL.z, aL.w, 0);\n");
uint32_t next_address = instr.dword_index + 1;
EmitSourceDepth("pc = 0x%X; // Exit loop to L%d\n", instr.dword_index + 1,
instr.dword_index + 1);
Unindent();
EmitSourceDepth("} else {\n");
Indent();
// Still looping. Adjust index and jump back to body.
EmitSourceDepth("aL.x += (state.loop_consts[%u] << 8) >> 24;\n",
instr.loop_constant_index);
EmitSourceDepth("pc = 0x%X; // Loop back to body L%d\n",
instr.loop_body_address, instr.loop_body_address);
Unindent();
EmitSourceDepth("}\n");
EmitSourceDepth("break;\n");
cf_wrote_pc_ = true;
}
void GlslShaderTranslator::ProcessCallInstruction(
const ParsedCallInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
EmitUnimplementedTranslationError();
}
void GlslShaderTranslator::ProcessReturnInstruction(
const ParsedReturnInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
EmitUnimplementedTranslationError();
}
void GlslShaderTranslator::ProcessJumpInstruction(
const ParsedJumpInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
bool needs_fallthrough = false;
switch (instr.type) {
case ParsedJumpInstruction::Type::kUnconditional:
EmitSourceDepth("{\n");
break;
case ParsedJumpInstruction::Type::kConditional:
EmitSourceDepth("if ((state.bool_consts[%d] & (1 << %d)) %c= 0) {\n",
instr.bool_constant_index / 32,
instr.bool_constant_index % 32,
instr.condition ? '!' : '=');
needs_fallthrough = true;
break;
case ParsedJumpInstruction::Type::kPredicated:
EmitSourceDepth("if (%cp0) {\n", instr.condition ? ' ' : '!');
needs_fallthrough = true;
break;
}
Indent();
EmitSourceDepth("pc = 0x%X; // L%d\n", instr.target_address,
instr.target_address);
EmitSourceDepth("break;\n");
Unindent();
if (needs_fallthrough) {
uint32_t next_address = instr.dword_index + 1;
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pc = 0x%X; // Fallthrough to L%d\n", next_address,
next_address);
EmitSourceDepth("}\n");
} else {
EmitSourceDepth("}\n");
}
}
void GlslShaderTranslator::ProcessAllocInstruction(
const ParsedAllocInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
}
void GlslShaderTranslator::ProcessVertexFetchInstruction(
const ParsedVertexFetchInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
if (instr.is_predicated) {
EmitSourceDepth("if (%cp0) {\n", instr.predicate_condition ? ' ' : '!');
Indent();
}
if (instr.result.stores_non_constants()) {
for (size_t i = 0; i < instr.operand_count; ++i) {
if (instr.operands[i].storage_source !=
InstructionStorageSource::kVertexFetchConstant) {
EmitLoadOperand(i, instr.operands[i]);
}
}
switch (instr.opcode) {
case FetchOpcode::kVertexFetch: {
EmitSourceDepth("if (src0.x == gl_VertexID) {\n");
Indent();
EmitSourceDepth("pv.");
for (int i = 0;
i < GetVertexFormatComponentCount(instr.attributes.data_format);
++i) {
EmitSource("%c", GetCharForComponentIndex(i));
}
auto format = instr.attributes.data_format;
if (format == VertexFormat::k_10_11_11) {
// GL doesn't support this format as a fetch type, so convert it.
EmitSource(" = get_10_11_11_%c(vf%u_%d);\n",
instr.attributes.is_signed ? 's' : 'u',
instr.operands[1].storage_index, instr.attributes.offset);
} else if (format == VertexFormat::k_2_10_10_10) {
EmitSource(" = get_2_10_10_10_%c(vf%u_%d);\n",
instr.attributes.is_signed ? 's' : 'u',
instr.operands[1].storage_index, instr.attributes.offset);
} else {
EmitSource(" = vf%u_%d;\n", instr.operands[1].storage_index,
instr.attributes.offset);
}
Unindent();
EmitSourceDepth("} else {\n");
Indent();
EmitSourceDepth("// UNIMPLEMENTED: Indexed fetch.\n");
EmitSourceDepth("pv = vec4(0.0, 0.0, 0.0, 1.0);\n");
Unindent();
EmitSourceDepth("}\n");
} break;
default:
assert_always();
break;
}
}
EmitStoreVectorResult(instr.result);
if (instr.is_predicated) {
Unindent();
EmitSourceDepth("}\n");
}
}
void GlslShaderTranslator::ProcessTextureFetchInstruction(
const ParsedTextureFetchInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
if (instr.is_predicated) {
EmitSourceDepth("if (%cp0) {\n", instr.predicate_condition ? ' ' : '!');
Indent();
}
for (size_t i = 0; i < instr.operand_count; ++i) {
if (instr.operands[i].storage_source !=
InstructionStorageSource::kTextureFetchConstant) {
EmitLoadOperand(i, instr.operands[i]);
}
}
switch (instr.opcode) {
case FetchOpcode::kTextureFetch:
EmitSourceDepth("{\n");
Indent();
switch (instr.dimension) {
case TextureDimension::k1D:
EmitSourceDepth("if (state.texture_samplers[%d] != uvec2(0)) {\n",
instr.operands[1].storage_index);
EmitSourceDepth(
" pv = texture(sampler1D(state.texture_samplers[%d]), "
"src0.x);\n",
instr.operands[1].storage_index);
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(src0.x, 0.0, 0.0, 1.0);\n");
EmitSourceDepth("}\n");
break;
case TextureDimension::k2D:
EmitSourceDepth("if (state.texture_samplers[%d] != uvec2(0)) {\n",
instr.operands[1].storage_index);
EmitSourceDepth(
" sampler2D samp = sampler2D(state.texture_samplers[%d]);\n",
instr.operands[1].storage_index);
if (instr.attributes.offset_x == 0.f &&
instr.attributes.offset_y == 0.f) {
EmitSourceDepth(" pv = texture(samp, src0.xy);\n",
instr.operands[1].storage_index);
} else {
// FIXME: This offset is still wrong, somehow.
EmitSourceDepth(
" pv = texture(samp, src0.xy + (vec2(%.2f, %.2f) / "
"textureSize(samp, 0)));\n",
instr.attributes.offset_x, instr.attributes.offset_y);
}
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(src0.x, src0.y, 0.0, 1.0);\n");
EmitSourceDepth("}\n");
break;
case TextureDimension::k3D:
EmitSourceDepth("if (state.texture_samplers[%d] != uvec2(0)) {\n",
instr.operands[1].storage_index);
EmitSourceDepth(
" pv = texture(sampler3D(state.texture_samplers[%d]), "
"src0.xyz);\n",
instr.operands[1].storage_index);
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(src0.x, src0.y, src0.z, 1.0);\n");
EmitSourceDepth("}\n");
break;
case TextureDimension::kCube:
// TODO(benvanik): undo CUBEv logic on t? (s,t,faceid)
EmitSourceDepth("if (state.texture_samplers[%d] != uvec2(0)) {\n",
instr.operands[1].storage_index);
EmitSourceDepth(
" pv = texture(samplerCube(state.texture_samplers[%d]), "
"src0.xyz);\n",
instr.operands[1].storage_index);
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(src0.x, src0.y, src0.z, 1.0);\n");
EmitSourceDepth("}\n");
break;
}
EmitSourceDepth("uint swiz = state.texture_swizzles[%d];\n",
instr.operands[1].storage_index);
EmitSourceDepth("vec4 orig = pv;\n");
EmitSourceDepth("ivec4 sv = ivec4(bitfieldExtract(swiz, 0, 3),\n");
EmitSourceDepth(" bitfieldExtract(swiz, 3, 3),\n");
EmitSourceDepth(" bitfieldExtract(swiz, 6, 3),\n");
EmitSourceDepth(" bitfieldExtract(swiz, 9, 3));\n");
// This is a little uglier than using an array, but much, much faster.
EmitSourceDepth("pv = mix(pv, orig.xxxx, equal(sv, ivec4(0)));\n");
EmitSourceDepth("pv = mix(pv, orig.yyyy, equal(sv, ivec4(1)));\n");
EmitSourceDepth("pv = mix(pv, orig.zzzz, equal(sv, ivec4(2)));\n");
EmitSourceDepth("pv = mix(pv, orig.wwww, equal(sv, ivec4(3)));\n");
EmitSourceDepth("pv = mix(pv, vec4(0.0), equal(sv, ivec4(4)));\n");
EmitSourceDepth("pv = mix(pv, vec4(1.0), equal(sv, ivec4(5)));\n");
Unindent();
EmitSourceDepth("}\n");
break;
case FetchOpcode::kGetTextureBorderColorFrac:
EmitUnimplementedTranslationError();
EmitSourceDepth("pv = vec4(0.0);\n");
break;
case FetchOpcode::kGetTextureComputedLod:
EmitUnimplementedTranslationError();
EmitSourceDepth("pv = vec4(0.0);\n");
break;
case FetchOpcode::kGetTextureGradients:
EmitUnimplementedTranslationError();
EmitSourceDepth("pv = vec4(0.0);\n");
break;
case FetchOpcode::kGetTextureWeights:
switch (instr.dimension) {
case TextureDimension::k1D:
EmitSourceDepth(
"pv.x = getWeights1D(sampler1D(state.texture_samplers[%d]), "
"src0.x);\n",
instr.operands[1].storage_index);
break;
case TextureDimension::k2D:
EmitSourceDepth(
"pv.xy = getWeights2D(sampler2D(state.texture_samplers[%d]), "
"src0.xy);\n",
instr.operands[1].storage_index);
break;
case TextureDimension::k3D:
EmitSourceDepth(
"pv.xyz = getWeights3D(sampler3D(state.texture_samplers[%d]), "
"src0.xyz);\n",
instr.operands[1].storage_index);
break;
default:
EmitUnimplementedTranslationError();
EmitSourceDepth("pv = vec4(0.0);\n");
}
break;
case FetchOpcode::kSetTextureLod:
EmitUnimplementedTranslationError();
break;
case FetchOpcode::kSetTextureGradientsHorz:
EmitUnimplementedTranslationError();
break;
case FetchOpcode::kSetTextureGradientsVert:
EmitUnimplementedTranslationError();
break;
case FetchOpcode::kUnknownTextureOp:
EmitUnimplementedTranslationError();
EmitSourceDepth("pv = vec4(0.0);\n");
break;
case FetchOpcode::kVertexFetch:
assert_always();
break;
}
EmitStoreVectorResult(instr.result);
if (instr.is_predicated) {
Unindent();
EmitSourceDepth("}\n");
}
}
void GlslShaderTranslator::ProcessAluInstruction(
const ParsedAluInstruction& instr) {
EmitSource("// ");
instr.Disassemble(&source_);
switch (instr.type) {
case ParsedAluInstruction::Type::kNop:
break;
case ParsedAluInstruction::Type::kVector:
ProcessVectorAluInstruction(instr);
break;
case ParsedAluInstruction::Type::kScalar:
ProcessScalarAluInstruction(instr);
break;
}
}
void GlslShaderTranslator::EmitLoadOperand(size_t i,
const InstructionOperand& op) {
EmitSourceDepth("src%d = ", i);
if (op.is_negated) {
EmitSource("-");
}
if (op.is_absolute_value) {
EmitSource("abs(");
}
int storage_index_offset = 0;
bool has_components = true;
switch (op.storage_source) {
case InstructionStorageSource::kRegister:
EmitSource("r");
break;
case InstructionStorageSource::kConstantFloat:
storage_index_offset = is_pixel_shader() ? 256 : 0;
EmitSource("state.float_consts");
break;
case InstructionStorageSource::kConstantInt:
EmitSource("state.loop_consts");
break;
case InstructionStorageSource::kConstantBool:
EmitSource("state.bool_consts");
break;
case InstructionStorageSource::kTextureFetchConstant:
case InstructionStorageSource::kVertexFetchConstant:
assert_always();
break;
}
switch (op.storage_addressing_mode) {
case InstructionStorageAddressingMode::kStatic:
if (storage_index_offset) {
EmitSource("[%d+%d]", storage_index_offset, op.storage_index);
} else {
EmitSource("[%d]", op.storage_index);
}
break;
case InstructionStorageAddressingMode::kAddressAbsolute:
if (storage_index_offset) {
EmitSource("[%d+%d+a0]", storage_index_offset, op.storage_index);
} else {
EmitSource("[%d+a0]", op.storage_index);
}
break;
case InstructionStorageAddressingMode::kAddressRelative:
if (storage_index_offset) {
EmitSource("[%d+%d+aL.x]", storage_index_offset, op.storage_index);
} else {
EmitSource("[%d+aL.x]", op.storage_index);
}
break;
}
if (op.is_absolute_value) {
EmitSource(")");
}
if (!op.is_standard_swizzle()) {
EmitSource(".");
if (op.component_count == 1) {
char a = GetCharForSwizzle(op.components[0]);
EmitSource("%c%c%c%c", a, a, a, a);
} else if (op.component_count == 2) {
char a = GetCharForSwizzle(op.components[0]);
char b = GetCharForSwizzle(op.components[1]);
EmitSource("%c%c%c%c", a, b, b, b);
} else {
for (int j = 0; j < op.component_count; ++j) {
EmitSource("%c", GetCharForSwizzle(op.components[j]));
}
for (int j = op.component_count; j < 4; ++j) {
EmitSource("%c",
GetCharForSwizzle(op.components[op.component_count - 1]));
}
}
}
EmitSource(";\n");
}
void GlslShaderTranslator::EmitStoreVectorResult(
const InstructionResult& result) {
EmitStoreResult(result, "pv");
}
void GlslShaderTranslator::EmitStoreScalarResult(
const InstructionResult& result) {
EmitStoreResult(result, "vec4(ps)");
}
void GlslShaderTranslator::EmitStoreResult(const InstructionResult& result,
const char* temp) {
if (!result.has_any_writes()) {
return;
}
// Special gl_pointSize discard
if (result.storage_target == InstructionStorageTarget::kPointSize &&
!result.is_standard_swizzle()) {
EmitUnimplementedTranslationError();
return;
}
bool uses_storage_index = false;
switch (result.storage_target) {
case InstructionStorageTarget::kRegister:
EmitSourceDepth("r");
uses_storage_index = true;
break;
case InstructionStorageTarget::kInterpolant:
EmitSourceDepth("vtx.o");
uses_storage_index = true;
break;
case InstructionStorageTarget::kPosition:
EmitSourceDepth("gl_Position");
break;
case InstructionStorageTarget::kPointSize:
EmitSourceDepth("gl_PointSize");
break;
case InstructionStorageTarget::kColorTarget:
EmitSourceDepth("oC");
uses_storage_index = true;
break;
case InstructionStorageTarget::kDepth:
EmitSourceDepth("gl_FragDepth");
break;
case InstructionStorageTarget::kNone:
return;
}
if (uses_storage_index) {
switch (result.storage_addressing_mode) {
case InstructionStorageAddressingMode::kStatic:
EmitSource("[%d]", result.storage_index);
break;
case InstructionStorageAddressingMode::kAddressAbsolute:
EmitSource("[%d+a0]", result.storage_index);
break;
case InstructionStorageAddressingMode::kAddressRelative:
EmitSource("[%d+aL.x]", result.storage_index);
break;
}
}
bool has_const_writes = false;
int component_write_count = 0;
if (!result.is_standard_swizzle()) {
EmitSource(".");
for (int j = 0; j < 4; ++j) {
if (result.write_mask[j]) {
if (result.components[j] == SwizzleSource::k0 ||
result.components[j] == SwizzleSource::k1) {
has_const_writes = true;
}
++component_write_count;
EmitSource("%c", GetCharForSwizzle(GetSwizzleFromComponentIndex(j)));
}
}
}
EmitSource(" = ");
if (result.is_clamped) {
EmitSource("clamp(");
}
if (has_const_writes) {
if (component_write_count > 1) {
EmitSource("vec%d(", component_write_count);
}
bool has_written = false;
for (int j = 0; j < 4; ++j) {
if (result.write_mask[j]) {
if (has_written) {
EmitSource(", ");
}
has_written = true;
switch (result.components[j]) {
case SwizzleSource::k0:
EmitSource("0.0");
break;
case SwizzleSource::k1:
EmitSource("1.0");
break;
default:
EmitSource("%s.%c", temp, GetCharForSwizzle(result.components[j]));
break;
}
}
}
if (component_write_count > 1) {
EmitSource(")");
}
} else {
EmitSource(temp);
if (!result.is_standard_swizzle()) {
EmitSource(".");
for (int j = 0; j < 4; ++j) {
if (result.write_mask[j]) {
EmitSource("%c", GetCharForSwizzle(result.components[j]));
}
}
}
}
if (result.is_clamped) {
EmitSource(", 0.0, 1.0)");
}
EmitSource(";\n");
}
void GlslShaderTranslator::ProcessVectorAluInstruction(
const ParsedAluInstruction& instr) {
// Emit if statement only if we have a different predicate condition than our
// containing block.
bool conditional = false;
if (instr.is_predicated &&
(!cf_exec_pred_ || (cf_exec_pred_cond_ != instr.predicate_condition))) {
conditional = true;
EmitSourceDepth("if (%cp0) {\n", instr.predicate_condition ? ' ' : '!');
Indent();
}
for (size_t i = 0; i < instr.operand_count; ++i) {
EmitLoadOperand(i, instr.operands[i]);
}
switch (instr.vector_opcode) {
// add dest, src0, src1
case AluVectorOpcode::kAdd:
EmitSourceDepth("pv = src0 + src1;\n");
break;
// mul dest, src0, src1
case AluVectorOpcode::kMul:
EmitSourceDepth("pv = src0 * src1;\n");
break;
// max dest, src0, src1
case AluVectorOpcode::kMax:
EmitSourceDepth("pv = max(src0, src1);\n");
break;
// min dest, src0, src1
case AluVectorOpcode::kMin:
EmitSourceDepth("pv = min(src0, src1);\n");
break;
// seq dest, src0, src1
case AluVectorOpcode::kSeq:
EmitSourceDepth("pv = vec4(equal(src0, src1));\n");
break;
// sgt dest, src0, src1
case AluVectorOpcode::kSgt:
EmitSourceDepth("pv = vec4(greaterThan(src0, src1));\n");
break;
// sge dest, src0, src1
case AluVectorOpcode::kSge:
EmitSourceDepth("pv = vec4(greaterThanEqual(src0, src1));\n");
break;
// sne dest, src0, src1
case AluVectorOpcode::kSne:
EmitSourceDepth("pv = vec4(notEqual(src0, src1));\n");
break;
// frc dest, src0
case AluVectorOpcode::kFrc:
EmitSourceDepth("pv = fract(src0);\n");
break;
// trunc dest, src0
case AluVectorOpcode::kTrunc:
EmitSourceDepth("pv = trunc(src0);\n");
break;
// floor dest, src0
case AluVectorOpcode::kFloor:
EmitSourceDepth("pv = floor(src0);\n");
break;
// mad dest, src0, src1, src2
case AluVectorOpcode::kMad:
EmitSourceDepth("pv = (src0 * src1) + src2;\n");
break;
// cndeq dest, src0, src1, src2
case AluVectorOpcode::kCndEq:
// src0 == 0 ? src1 : src2;
EmitSourceDepth("pv = mix(src2, src1, equal(src0, vec4(0)));\n");
break;
// cndge dest, src0, src1, src2
case AluVectorOpcode::kCndGe:
// src0 >= 0 ? src1 : src2;
EmitSourceDepth(
"pv = mix(src2, src1, greaterThanEqual(src0, vec4(0)));\n");
break;
// cndgt dest, src0, src1, src2
case AluVectorOpcode::kCndGt:
// src0 > 0 ? src1 : src2;
EmitSourceDepth("pv = mix(src2, src1, greaterThan(src0, vec4(0)));\n");
break;
// dp4 dest, src0, src1
case AluVectorOpcode::kDp4:
EmitSourceDepth("pv = dot(src0, src1).xxxx;\n");
break;
// dp3 dest, src0, src1
case AluVectorOpcode::kDp3:
EmitSourceDepth("pv = dot(vec4(src0).xyz, vec4(src1).xyz).xxxx;\n");
break;
// dp2add dest, src0, src1, src2
case AluVectorOpcode::kDp2Add:
EmitSourceDepth(
"pv = vec4(src0.x * src1.x + src0.y * src1.y + src2.x).xxxx;\n");
break;
// cube dest, src0, src1
case AluVectorOpcode::kCube:
EmitSourceDepth("pv = cube(src0, src1);\n");
break;
// max4 dest, src0
case AluVectorOpcode::kMax4:
EmitSourceDepth(
"pv = max(src0.x, max(src0.y, max(src0.z, src0.w))).xxxx;\n");
break;
// setp_eq_push dest, src0, src1
case AluVectorOpcode::kSetpEqPush:
cf_exec_pred_ = false;
EmitSourceDepth("p0 = src0.w == 0.0 && src1.w == 0.0 ? true : false;\n");
EmitSourceDepth(
"pv = vec4(src0.x == 0.0 && src1.x == 0.0 ? 0.0 : src0.x + 1.0);\n");
break;
// setp_ne_push dest, src0, src1
case AluVectorOpcode::kSetpNePush:
cf_exec_pred_ = false;
EmitSourceDepth("p0 = src0.w == 0.0 && src1.w != 0.0 ? true : false;\n");
EmitSourceDepth(
"pv = vec4(src0.x == 0.0 && src1.x != 0.0 ? 0.0 : src0.x + 1.0);\n");
break;
// setp_gt_push dest, src0, src1
case AluVectorOpcode::kSetpGtPush:
cf_exec_pred_ = false;
EmitSourceDepth("p0 = src0.w == 0.0 && src1.w > 0.0 ? true : false;\n");
EmitSourceDepth(
"pv = vec4(src0.x == 0.0 && src1.x > 0.0 ? 0.0 : src0.x + 1.0);\n");
break;
// setp_ge_push dest, src0, src1
case AluVectorOpcode::kSetpGePush:
cf_exec_pred_ = false;
EmitSourceDepth("p0 = src0.w == 0.0 && src1.w >= 0.0 ? true : false;\n");
EmitSourceDepth(
"pv = vec4(src0.x == 0.0 && src1.x >= 0.0 ? 0.0 : src0.x + 1.0);\n");
break;
// kill_eq dest, src0, src1
case AluVectorOpcode::kKillEq:
EmitSourceDepth("if (any(equal(src0, src1))) {\n");
EmitSourceDepth(" pv = vec4(1.0);\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(0.0);\n");
EmitSourceDepth("}\n");
break;
// kill_gt dest, src0, src1
case AluVectorOpcode::kKillGt:
EmitSourceDepth("if (any(greaterThan(src0, src1))) {\n");
EmitSourceDepth(" pv = vec4(1.0);\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(0.0);\n");
EmitSourceDepth("}\n");
break;
// kill_ge dest, src0, src1
case AluVectorOpcode::kKillGe:
EmitSourceDepth("if (any(greaterThanEqual(src0, src1))) {\n");
EmitSourceDepth(" pv = vec4(1.0);\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(0.0);\n");
EmitSourceDepth("}\n");
break;
// kill_ne dest, src0, src1
case AluVectorOpcode::kKillNe:
EmitSourceDepth("if (any(notEqual(src0, src1))) {\n");
EmitSourceDepth(" pv = vec4(1.0);\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" pv = vec4(0.0);\n");
EmitSourceDepth("}\n");
break;
// dst dest, src0, src1
case AluVectorOpcode::kDst:
EmitSourceDepth("pv.x = 1.0;\n");
EmitSourceDepth("pv.y = src0.y * src1.y;\n");
EmitSourceDepth("pv.z = src0.z;\n");
EmitSourceDepth("pv.w = src1.w;\n");
break;
// maxa dest, src0, src1
case AluVectorOpcode::kMaxA:
EmitSourceDepth("a0 = clamp(int(floor(src0.w + 0.5)), -256, 255);\n");
EmitSourceDepth("pv = max(src0, src1);\n");
break;
}
EmitStoreVectorResult(instr.result);
if (conditional) {
Unindent();
EmitSourceDepth("}\n");
}
}
void GlslShaderTranslator::ProcessScalarAluInstruction(
const ParsedAluInstruction& instr) {
bool conditional = false;
if (instr.is_predicated &&
(!cf_exec_pred_ || (cf_exec_pred_cond_ != instr.predicate_condition))) {
conditional = true;
EmitSourceDepth("if (%cp0) {\n", instr.predicate_condition ? ' ' : '!');
Indent();
}
for (size_t i = 0; i < instr.operand_count; ++i) {
EmitLoadOperand(i, instr.operands[i]);
}
switch (instr.scalar_opcode) {
// adds dest, src0.ab
case AluScalarOpcode::kAdds:
EmitSourceDepth("ps = src0.x + src0.y;\n");
break;
// adds_prev dest, src0.a
case AluScalarOpcode::kAddsPrev:
EmitSourceDepth("ps = src0.x + ps;\n");
break;
// muls dest, src0.ab
case AluScalarOpcode::kMuls:
EmitSourceDepth("ps = src0.x * src0.y;\n");
break;
// muls_prev dest, src0.a
case AluScalarOpcode::kMulsPrev:
EmitSourceDepth("ps = src0.x * ps;\n");
break;
// muls_prev2 dest, src0.ab
case AluScalarOpcode::kMulsPrev2:
EmitSourceDepth(
"ps = ps == -FLT_MAX || isinf(ps) || isnan(ps) || isnan(src0.y) || "
"src0.y <= 0.0 ? -FLT_MAX : src0.x * ps;\n");
break;
// maxs dest, src0.ab
case AluScalarOpcode::kMaxs:
EmitSourceDepth("ps = max(src0.x, src0.y);\n");
break;
// mins dest, src0.ab
case AluScalarOpcode::kMins:
EmitSourceDepth("ps = min(src0.x, src0.y);\n");
break;
// seqs dest, src0.a
case AluScalarOpcode::kSeqs:
EmitSourceDepth("ps = float(src0.x == 0.0);\n");
break;
// sgts dest, src0.a
case AluScalarOpcode::kSgts:
EmitSourceDepth("ps = float(src0.x > 0.0);\n");
break;
// sges dest, src0.a
case AluScalarOpcode::kSges:
EmitSourceDepth("ps = float(src0.x >= 0.0);\n");
break;
// snes dest, src0.a
case AluScalarOpcode::kSnes:
EmitSourceDepth("ps = float(src0.x != 0.0);\n");
break;
// frcs dest, src0.a
case AluScalarOpcode::kFrcs:
EmitSourceDepth("ps = fract(src0.x);\n");
break;
// truncs dest, src0.a
case AluScalarOpcode::kTruncs:
EmitSourceDepth("ps = trunc(src0.x);\n");
break;
// floors dest, src0.a
case AluScalarOpcode::kFloors:
EmitSourceDepth("ps = floor(src0.x);\n");
break;
// exp dest, src0.a
case AluScalarOpcode::kExp:
EmitSourceDepth("ps = exp2(src0.x);\n");
break;
// logc dest, src0.a
case AluScalarOpcode::kLogc:
EmitSourceDepth("ps = log2(src0.x);\n");
EmitSourceDepth("ps = isinf(ps) ? -FLT_MAX : ps;\n");
break;
// log dest, src0.a
case AluScalarOpcode::kLog:
EmitSourceDepth("ps = log2(src0.x);\n");
break;
// rcpc dest, src0.a
case AluScalarOpcode::kRcpc:
EmitSourceDepth("ps = 1.0 / src0.x;\n");
EmitSourceDepth("if (isinf(ps)) ps = FLT_MAX;\n");
break;
// rcpf dest, src0.a
case AluScalarOpcode::kRcpf:
EmitSourceDepth("ps = 1.0 / src0.x;\n");
EmitSourceDepth("if (isinf(ps)) ps = 0.0;\n");
break;
// rcp dest, src0.a
case AluScalarOpcode::kRcp:
// Prevent divide by zero.
EmitSourceDepth("ps = src0.x != 0.0 ? 1.0 / src0.x : 0.0;\n");
break;
// rsqc dest, src0.a
case AluScalarOpcode::kRsqc:
EmitSourceDepth("ps = inversesqrt(src0.x);\n");
EmitSourceDepth("if (isinf(ps)) ps = FLT_MAX;\n");
break;
// rsqc dest, src0.a
case AluScalarOpcode::kRsqf:
EmitSourceDepth("ps = inversesqrt(src0.x);\n");
EmitSourceDepth("if (isinf(ps)) ps = 0.0;\n");
break;
// rsq dest, src0.a
case AluScalarOpcode::kRsq:
// Prevent divide by zero.
EmitSourceDepth("ps = src0.x != 0.0 ? inversesqrt(src0.x) : 0.0;\n");
break;
// maxas dest, src0.ab
// movas dest, src0.aa
case AluScalarOpcode::kMaxAs:
EmitSourceDepth("a0 = clamp(int(floor(src0.x + 0.5)), -256, 255);\n");
EmitSourceDepth("ps = max(src0.x, src0.y);\n");
break;
// maxasf dest, src0.ab
// movasf dest, src0.aa
case AluScalarOpcode::kMaxAsf:
EmitSourceDepth("a0 = clamp(int(floor(src0.x)), -256, 255);\n");
EmitSourceDepth("ps = max(src0.x, src0.y);\n");
break;
// subs dest, src0.ab
case AluScalarOpcode::kSubs:
EmitSourceDepth("ps = src0.x - src0.y;\n");
break;
// subs_prev dest, src0.a
case AluScalarOpcode::kSubsPrev:
EmitSourceDepth("ps = src0.x - ps;\n");
break;
// setp_eq dest, src0.a
case AluScalarOpcode::kSetpEq:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x == 0.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_ne dest, src0.a
case AluScalarOpcode::kSetpNe:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x != 0.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_gt dest, src0.a
case AluScalarOpcode::kSetpGt:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x > 0.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_ge dest, src0.a
case AluScalarOpcode::kSetpGe:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x >= 0.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_inv dest, src0.a
case AluScalarOpcode::kSetpInv:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x == 1.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = src0.x == 0.0 ? 1.0 : src0.x;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_pop dest, src0.a
case AluScalarOpcode::kSetpPop:
cf_exec_pred_ = false;
EmitSourceDepth("if (src0.x - 1.0 <= 0.0) {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth(" p0 = true;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = src0.x - 1.0;\n");
EmitSourceDepth(" p0 = false;\n");
EmitSourceDepth("}\n");
break;
// setp_clr dest
case AluScalarOpcode::kSetpClr:
cf_exec_pred_ = false;
EmitSourceDepth("ps = FLT_MAX;\n");
EmitSourceDepth("p0 = false;\n");
break;
// setp_rstr dest, src0.a
case AluScalarOpcode::kSetpRstr:
cf_exec_pred_ = false;
EmitSourceDepth("ps = src0.x;\n");
EmitSourceDepth("p0 = src0.x == 0.0 ? true : false;\n");
break;
// kills_eq dest, src0.a
case AluScalarOpcode::kKillsEq:
EmitSourceDepth("if (src0.x == 0.0) {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth("}\n");
break;
// kills_gt dest, src0.a
case AluScalarOpcode::kKillsGt:
EmitSourceDepth("if (src0.x > 0.0) {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth("}\n");
break;
// kills_ge dest, src0.a
case AluScalarOpcode::kKillsGe:
EmitSourceDepth("if (src0.x >= 0.0) {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth("}\n");
break;
// kills_ne dest, src0.a
case AluScalarOpcode::kKillsNe:
EmitSourceDepth("if (src0.x != 0.0) {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth("}\n");
break;
// kills_one dest, src0.a
case AluScalarOpcode::kKillsOne:
EmitSourceDepth("if (src0.x == 1.0) {\n");
EmitSourceDepth(" ps = 1.0;\n");
EmitSourceDepth(" discard;\n");
EmitSourceDepth("} else {\n");
EmitSourceDepth(" ps = 0.0;\n");
EmitSourceDepth("}\n");
break;
// sqrt dest, src0.a
case AluScalarOpcode::kSqrt:
EmitSourceDepth("ps = sqrt(src0.x);\n");
break;
// mulsc dest, src0.a, src0.b
case AluScalarOpcode::kMulsc0:
case AluScalarOpcode::kMulsc1:
EmitSourceDepth("ps = src0.x * src1.x;\n");
break;
// addsc dest, src0.a, src0.b
case AluScalarOpcode::kAddsc0:
case AluScalarOpcode::kAddsc1:
EmitSourceDepth("ps = src0.x + src1.x;\n");
break;
// subsc dest, src0.a, src0.b
case AluScalarOpcode::kSubsc0:
case AluScalarOpcode::kSubsc1:
EmitSourceDepth("ps = src0.x - src1.x;\n");
break;
// sin dest, src0.a
case AluScalarOpcode::kSin:
EmitSourceDepth("ps = sin(src0.x);\n");
break;
// cos dest, src0.a
case AluScalarOpcode::kCos:
EmitSourceDepth("ps = cos(src0.x);\n");
break;
// retain_prev dest
case AluScalarOpcode::kRetainPrev:
// ps is reused.
break;
}
EmitStoreScalarResult(instr.result);
if (conditional) {
Unindent();
EmitSourceDepth("}\n");
}
}
} // namespace gpu
} // namespace xe