/** ****************************************************************************** * 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 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 defined_locations; for (auto& binding : vertex_bindings()) { for (auto& attrib : binding.attributes) { uint64_t key = (static_cast(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 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