diff options
| author | 2018-12-26 01:57:14 -0300 | |
|---|---|---|
| committer | 2019-01-15 17:54:53 -0300 | |
| commit | af5d7e2c49d1cfe470b0b4ebd55561f9f22dc677 (patch) | |
| tree | fe95585385e2569af1f80b6e6c63918e24b64e5f /src/video_core/shader | |
| parent | shader_ir: Remove RZ and use Register::ZeroIndex instead (diff) | |
| download | yuzu-af5d7e2c49d1cfe470b0b4ebd55561f9f22dc677.tar.gz yuzu-af5d7e2c49d1cfe470b0b4ebd55561f9f22dc677.tar.xz yuzu-af5d7e2c49d1cfe470b0b4ebd55561f9f22dc677.zip | |
video_core: Rename glsl_decompiler to gl_shader_decompiler
Diffstat (limited to 'src/video_core/shader')
| -rw-r--r-- | src/video_core/shader/glsl_decompiler.cpp | 1543 | ||||
| -rw-r--r-- | src/video_core/shader/glsl_decompiler.h | 88 |
2 files changed, 0 insertions, 1631 deletions
diff --git a/src/video_core/shader/glsl_decompiler.cpp b/src/video_core/shader/glsl_decompiler.cpp deleted file mode 100644 index 27f1e0dde..000000000 --- a/src/video_core/shader/glsl_decompiler.cpp +++ /dev/null | |||
| @@ -1,1543 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | #include <string_view> | ||
| 7 | #include <variant> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/alignment.h" | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "video_core/engines/maxwell_3d.h" | ||
| 15 | #include "video_core/shader/glsl_decompiler.h" | ||
| 16 | #include "video_core/shader/shader_ir.h" | ||
| 17 | |||
| 18 | namespace OpenGL::GLShader { | ||
| 19 | |||
| 20 | using Tegra::Shader::Attribute; | ||
| 21 | using Tegra::Shader::Header; | ||
| 22 | using Tegra::Shader::IpaInterpMode; | ||
| 23 | using Tegra::Shader::IpaMode; | ||
| 24 | using Tegra::Shader::IpaSampleMode; | ||
| 25 | using Tegra::Shader::Register; | ||
| 26 | using namespace VideoCommon::Shader; | ||
| 27 | |||
| 28 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 29 | using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage; | ||
| 30 | using Operation = const OperationNode&; | ||
| 31 | |||
| 32 | enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 }; | ||
| 33 | constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 65536 / 16; // TODO(Rodrigo): Use rasterizer's value | ||
| 34 | |||
| 35 | enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat }; | ||
| 36 | |||
| 37 | class ShaderWriter { | ||
| 38 | public: | ||
| 39 | void AddExpression(std::string_view text) { | ||
| 40 | DEBUG_ASSERT(scope >= 0); | ||
| 41 | if (!text.empty()) { | ||
| 42 | AppendIndentation(); | ||
| 43 | } | ||
| 44 | shader_source += text; | ||
| 45 | } | ||
| 46 | |||
| 47 | void AddLine(std::string_view text) { | ||
| 48 | AddExpression(text); | ||
| 49 | AddNewLine(); | ||
| 50 | } | ||
| 51 | |||
| 52 | void AddLine(char character) { | ||
| 53 | DEBUG_ASSERT(scope >= 0); | ||
| 54 | AppendIndentation(); | ||
| 55 | shader_source += character; | ||
| 56 | AddNewLine(); | ||
| 57 | } | ||
| 58 | |||
| 59 | void AddNewLine() { | ||
| 60 | DEBUG_ASSERT(scope >= 0); | ||
| 61 | shader_source += '\n'; | ||
| 62 | } | ||
| 63 | |||
| 64 | std::string GenerateTemporal() { | ||
| 65 | std::string temporal = "tmp"; | ||
| 66 | temporal += std::to_string(temporal_index++); | ||
| 67 | return temporal; | ||
| 68 | } | ||
| 69 | |||
| 70 | std::string GetResult() { | ||
| 71 | return std::move(shader_source); | ||
| 72 | } | ||
| 73 | |||
| 74 | s32 scope = 0; | ||
| 75 | |||
| 76 | private: | ||
| 77 | void AppendIndentation() { | ||
| 78 | shader_source.append(static_cast<std::size_t>(scope) * 4, ' '); | ||
| 79 | } | ||
| 80 | |||
| 81 | std::string shader_source; | ||
| 82 | u32 temporal_index = 1; | ||
| 83 | }; | ||
| 84 | |||
| 85 | /// Generates code to use for a swizzle operation. | ||
| 86 | static std::string GetSwizzle(u32 elem) { | ||
| 87 | ASSERT(elem <= 3); | ||
| 88 | std::string swizzle = "."; | ||
| 89 | swizzle += "xyzw"[elem]; | ||
| 90 | return swizzle; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Translate topology | ||
| 94 | static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | ||
| 95 | switch (topology) { | ||
| 96 | case Tegra::Shader::OutputTopology::PointList: | ||
| 97 | return "points"; | ||
| 98 | case Tegra::Shader::OutputTopology::LineStrip: | ||
| 99 | return "line_strip"; | ||
| 100 | case Tegra::Shader::OutputTopology::TriangleStrip: | ||
| 101 | return "triangle_strip"; | ||
| 102 | default: | ||
| 103 | UNIMPLEMENTED_MSG("Unknown output topology: {}", static_cast<u32>(topology)); | ||
| 104 | return "points"; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Returns true if an object has to be treated as precise | ||
| 109 | static bool IsPrecise(Operation operand) { | ||
| 110 | const auto& meta = operand.GetMeta(); | ||
| 111 | |||
| 112 | if (std::holds_alternative<MetaArithmetic>(meta)) { | ||
| 113 | return std::get<MetaArithmetic>(meta).precise; | ||
| 114 | } | ||
| 115 | if (std::holds_alternative<MetaHalfArithmetic>(meta)) { | ||
| 116 | return std::get<MetaHalfArithmetic>(meta).precise; | ||
| 117 | } | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | static bool IsPrecise(Node node) { | ||
| 122 | if (!std::holds_alternative<OperationNode>(*node)) { | ||
| 123 | return false; | ||
| 124 | } | ||
| 125 | return IsPrecise(std::get<OperationNode>(*node)); | ||
| 126 | } | ||
| 127 | |||
| 128 | class GLSLDecompiler final { | ||
| 129 | public: | ||
| 130 | explicit GLSLDecompiler(const ShaderIR& ir, ShaderStage stage, std::string suffix) | ||
| 131 | : ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} | ||
| 132 | |||
| 133 | void Decompile() { | ||
| 134 | DeclareVertex(); | ||
| 135 | DeclareGeometry(); | ||
| 136 | DeclareRegisters(); | ||
| 137 | DeclarePredicates(); | ||
| 138 | DeclareLocalMemory(); | ||
| 139 | DeclareInternalFlags(); | ||
| 140 | DeclareInputAttributes(); | ||
| 141 | DeclareOutputAttributes(); | ||
| 142 | DeclareConstantBuffers(); | ||
| 143 | DeclareSamplers(); | ||
| 144 | |||
| 145 | code.AddLine("void execute_" + suffix + "() {"); | ||
| 146 | ++code.scope; | ||
| 147 | |||
| 148 | // VM's program counter | ||
| 149 | const auto first_address = ir.GetBasicBlocks().begin()->first; | ||
| 150 | code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); | ||
| 151 | |||
| 152 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems | ||
| 153 | // unlikely that shaders will use 20 nested SSYs and PBKs. | ||
| 154 | constexpr u32 FLOW_STACK_SIZE = 20; | ||
| 155 | code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); | ||
| 156 | code.AddLine("uint flow_stack_top = 0u;"); | ||
| 157 | |||
| 158 | code.AddLine("while (true) {"); | ||
| 159 | ++code.scope; | ||
| 160 | |||
| 161 | code.AddLine("switch (jmp_to) {"); | ||
| 162 | |||
| 163 | for (const auto& pair : ir.GetBasicBlocks()) { | ||
| 164 | const auto [address, bb] = pair; | ||
| 165 | code.AddLine(fmt::format("case 0x{:x}u: {{", address)); | ||
| 166 | ++code.scope; | ||
| 167 | |||
| 168 | VisitBasicBlock(bb); | ||
| 169 | |||
| 170 | --code.scope; | ||
| 171 | code.AddLine('}'); | ||
| 172 | } | ||
| 173 | |||
| 174 | code.AddLine("default: return;"); | ||
| 175 | code.AddLine('}'); | ||
| 176 | |||
| 177 | for (std::size_t i = 0; i < 2; ++i) { | ||
| 178 | --code.scope; | ||
| 179 | code.AddLine('}'); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | std::string GetResult() { | ||
| 184 | return code.GetResult(); | ||
| 185 | } | ||
| 186 | |||
| 187 | ShaderEntries GetShaderEntries() const { | ||
| 188 | ShaderEntries entries; | ||
| 189 | for (const auto& cbuf : ir.GetConstantBuffers()) { | ||
| 190 | ConstBufferEntry desc(cbuf.second, stage, GetConstBufferBlock(cbuf.first), cbuf.first); | ||
| 191 | entries.const_buffers.push_back(desc); | ||
| 192 | } | ||
| 193 | for (const auto& sampler : ir.GetSamplers()) { | ||
| 194 | SamplerEntry desc(sampler, stage, GetSampler(sampler)); | ||
| 195 | entries.samplers.push_back(desc); | ||
| 196 | } | ||
| 197 | entries.clip_distances = ir.GetClipDistances(); | ||
| 198 | entries.shader_length = ir.GetLength(); | ||
| 199 | return entries; | ||
| 200 | } | ||
| 201 | |||
| 202 | private: | ||
| 203 | using OperationDecompilerFn = std::string (GLSLDecompiler::*)(Operation); | ||
| 204 | using OperationDecompilersArray = | ||
| 205 | std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>; | ||
| 206 | |||
| 207 | void DeclareVertex() { | ||
| 208 | if (stage != ShaderStage::Vertex) | ||
| 209 | return; | ||
| 210 | |||
| 211 | DeclareVertexRedeclarations(); | ||
| 212 | } | ||
| 213 | |||
| 214 | void DeclareGeometry() { | ||
| 215 | if (stage != ShaderStage::Geometry) | ||
| 216 | return; | ||
| 217 | |||
| 218 | const auto topology = GetTopologyName(header.common3.output_topology); | ||
| 219 | const auto max_vertices = std::to_string(header.common4.max_output_vertices); | ||
| 220 | code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); | ||
| 221 | code.AddNewLine(); | ||
| 222 | |||
| 223 | DeclareVertexRedeclarations(); | ||
| 224 | } | ||
| 225 | |||
| 226 | void DeclareVertexRedeclarations() { | ||
| 227 | bool clip_distances_declared = false; | ||
| 228 | |||
| 229 | code.AddLine("out gl_PerVertex {"); | ||
| 230 | ++code.scope; | ||
| 231 | |||
| 232 | code.AddLine("vec4 gl_Position;"); | ||
| 233 | |||
| 234 | for (const auto o : ir.GetOutputAttributes()) { | ||
| 235 | if (o == Attribute::Index::PointSize) | ||
| 236 | code.AddLine("float gl_PointSize;"); | ||
| 237 | if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 || | ||
| 238 | o == Attribute::Index::ClipDistances4567)) { | ||
| 239 | code.AddLine("float gl_ClipDistance[];"); | ||
| 240 | clip_distances_declared = true; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | --code.scope; | ||
| 245 | code.AddLine("};"); | ||
| 246 | code.AddNewLine(); | ||
| 247 | } | ||
| 248 | |||
| 249 | void DeclareRegisters() { | ||
| 250 | const auto& registers = ir.GetRegisters(); | ||
| 251 | for (const u32 gpr : registers) { | ||
| 252 | code.AddLine("float " + GetRegister(gpr) + " = 0;"); | ||
| 253 | } | ||
| 254 | if (!registers.empty()) | ||
| 255 | code.AddNewLine(); | ||
| 256 | } | ||
| 257 | |||
| 258 | void DeclarePredicates() { | ||
| 259 | const auto& predicates = ir.GetPredicates(); | ||
| 260 | for (const auto pred : predicates) { | ||
| 261 | code.AddLine("bool " + GetPredicate(pred) + " = false;"); | ||
| 262 | } | ||
| 263 | if (!predicates.empty()) | ||
| 264 | code.AddNewLine(); | ||
| 265 | } | ||
| 266 | |||
| 267 | void DeclareLocalMemory() { | ||
| 268 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | ||
| 269 | const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; | ||
| 270 | code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); | ||
| 271 | code.AddNewLine(); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | void DeclareInternalFlags() { | ||
| 276 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { | ||
| 277 | const InternalFlag flag_code = static_cast<InternalFlag>(flag); | ||
| 278 | code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); | ||
| 279 | } | ||
| 280 | code.AddNewLine(); | ||
| 281 | } | ||
| 282 | |||
| 283 | std::string GetInputFlags(const IpaMode& input_mode) { | ||
| 284 | const IpaSampleMode sample_mode = input_mode.sampling_mode; | ||
| 285 | const IpaInterpMode interp_mode = input_mode.interpolation_mode; | ||
| 286 | std::string out; | ||
| 287 | |||
| 288 | switch (interp_mode) { | ||
| 289 | case IpaInterpMode::Flat: | ||
| 290 | out += "flat "; | ||
| 291 | break; | ||
| 292 | case IpaInterpMode::Linear: | ||
| 293 | out += "noperspective "; | ||
| 294 | break; | ||
| 295 | case IpaInterpMode::Perspective: | ||
| 296 | // Default, Smooth | ||
| 297 | break; | ||
| 298 | default: | ||
| 299 | UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode)); | ||
| 300 | } | ||
| 301 | switch (sample_mode) { | ||
| 302 | case IpaSampleMode::Centroid: | ||
| 303 | // It can be implemented with the "centroid " keyword in GLSL | ||
| 304 | UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid"); | ||
| 305 | break; | ||
| 306 | case IpaSampleMode::Default: | ||
| 307 | // Default, n/a | ||
| 308 | break; | ||
| 309 | default: | ||
| 310 | UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode)); | ||
| 311 | } | ||
| 312 | return out; | ||
| 313 | } | ||
| 314 | |||
| 315 | void DeclareInputAttributes() { | ||
| 316 | const auto& attributes = ir.GetInputAttributes(); | ||
| 317 | for (const auto element : attributes) { | ||
| 318 | const Attribute::Index index = element.first; | ||
| 319 | const IpaMode& input_mode = *element.second.begin(); | ||
| 320 | if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { | ||
| 321 | // Skip when it's not a generic attribute | ||
| 322 | continue; | ||
| 323 | } | ||
| 324 | |||
| 325 | ASSERT(element.second.size() > 0); | ||
| 326 | // UNIMPLEMENTED_IF_MSG(element.second.size() > 1, | ||
| 327 | // "Multiple input flag modes are not supported in GLSL"); | ||
| 328 | |||
| 329 | // TODO(bunnei): Use proper number of elements for these | ||
| 330 | u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); | ||
| 331 | if (stage != ShaderStage::Vertex) { | ||
| 332 | // If inputs are varyings, add an offset | ||
| 333 | idx += GENERIC_VARYING_START_LOCATION; | ||
| 334 | } | ||
| 335 | |||
| 336 | std::string attr = GetInputAttribute(index); | ||
| 337 | if (stage == ShaderStage::Geometry) { | ||
| 338 | attr = "gs_" + attr + "[]"; | ||
| 339 | } | ||
| 340 | code.AddLine("layout (location = " + std::to_string(idx) + ") " + | ||
| 341 | GetInputFlags(input_mode) + "in vec4 " + attr + ';'); | ||
| 342 | } | ||
| 343 | if (!attributes.empty()) | ||
| 344 | code.AddNewLine(); | ||
| 345 | } | ||
| 346 | |||
| 347 | void DeclareOutputAttributes() { | ||
| 348 | const auto& attributes = ir.GetOutputAttributes(); | ||
| 349 | for (const auto index : attributes) { | ||
| 350 | if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { | ||
| 351 | // Skip when it's not a generic attribute | ||
| 352 | continue; | ||
| 353 | } | ||
| 354 | // TODO(bunnei): Use proper number of elements for these | ||
| 355 | const auto idx = static_cast<u32>(index) - | ||
| 356 | static_cast<u32>(Attribute::Index::Attribute_0) + | ||
| 357 | GENERIC_VARYING_START_LOCATION; | ||
| 358 | code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " + | ||
| 359 | GetOutputAttribute(index) + ';'); | ||
| 360 | } | ||
| 361 | if (!attributes.empty()) | ||
| 362 | code.AddNewLine(); | ||
| 363 | } | ||
| 364 | |||
| 365 | void DeclareConstantBuffers() { | ||
| 366 | for (const auto& entry : ir.GetConstantBuffers()) { | ||
| 367 | const auto [index, size] = entry; | ||
| 368 | code.AddLine("layout (std140) uniform " + GetConstBufferBlock(index) + " {"); | ||
| 369 | code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); | ||
| 370 | code.AddLine("};"); | ||
| 371 | code.AddNewLine(); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | void DeclareSamplers() { | ||
| 376 | const auto& samplers = ir.GetSamplers(); | ||
| 377 | for (const auto& sampler : samplers) { | ||
| 378 | std::string sampler_type = [&]() { | ||
| 379 | switch (sampler.GetType()) { | ||
| 380 | case Tegra::Shader::TextureType::Texture1D: | ||
| 381 | return "sampler1D"; | ||
| 382 | case Tegra::Shader::TextureType::Texture2D: | ||
| 383 | return "sampler2D"; | ||
| 384 | case Tegra::Shader::TextureType::Texture3D: | ||
| 385 | return "sampler3D"; | ||
| 386 | case Tegra::Shader::TextureType::TextureCube: | ||
| 387 | return "samplerCube"; | ||
| 388 | default: | ||
| 389 | UNREACHABLE(); | ||
| 390 | return "sampler2D"; | ||
| 391 | } | ||
| 392 | }(); | ||
| 393 | if (sampler.IsArray()) | ||
| 394 | sampler_type += "Array"; | ||
| 395 | if (sampler.IsShadow()) | ||
| 396 | sampler_type += "Shadow"; | ||
| 397 | |||
| 398 | code.AddLine("uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); | ||
| 399 | } | ||
| 400 | if (!samplers.empty()) | ||
| 401 | code.AddNewLine(); | ||
| 402 | } | ||
| 403 | |||
| 404 | void VisitBasicBlock(const BasicBlock& bb) { | ||
| 405 | for (const Node node : bb) { | ||
| 406 | if (const std::string expr = Visit(node); !expr.empty()) { | ||
| 407 | code.AddLine(expr); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | std::string Visit(Node node) { | ||
| 413 | if (const auto operation = std::get_if<OperationNode>(node)) { | ||
| 414 | const auto operation_index = static_cast<std::size_t>(operation->GetCode()); | ||
| 415 | const auto decompiler = operation_decompilers[operation_index]; | ||
| 416 | if (decompiler == nullptr) { | ||
| 417 | UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index); | ||
| 418 | } | ||
| 419 | return (this->*decompiler)(*operation); | ||
| 420 | |||
| 421 | } else if (const auto gpr = std::get_if<GprNode>(node)) { | ||
| 422 | const u32 index = gpr->GetIndex(); | ||
| 423 | if (index == Register::ZeroIndex) { | ||
| 424 | return "0"; | ||
| 425 | } | ||
| 426 | return GetRegister(index); | ||
| 427 | |||
| 428 | } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { | ||
| 429 | const u32 value = immediate->GetValue(); | ||
| 430 | if (value < 10) { | ||
| 431 | // For eyecandy avoid using hex numbers on single digits | ||
| 432 | return fmt::format("utof({}u)", immediate->GetValue()); | ||
| 433 | } | ||
| 434 | return fmt::format("utof(0x{:x}u)", immediate->GetValue()); | ||
| 435 | |||
| 436 | } else if (const auto predicate = std::get_if<PredicateNode>(node)) { | ||
| 437 | const auto value = [&]() -> std::string { | ||
| 438 | switch (const auto index = predicate->GetIndex(); index) { | ||
| 439 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 440 | return "true"; | ||
| 441 | case Tegra::Shader::Pred::NeverExecute: | ||
| 442 | return "false"; | ||
| 443 | default: | ||
| 444 | return GetPredicate(index); | ||
| 445 | } | ||
| 446 | }(); | ||
| 447 | if (predicate->IsNegated()) { | ||
| 448 | return "!(" + value + ')'; | ||
| 449 | } | ||
| 450 | return value; | ||
| 451 | |||
| 452 | } else if (const auto abuf = std::get_if<AbufNode>(node)) { | ||
| 453 | const auto attribute = abuf->GetIndex(); | ||
| 454 | const auto element = abuf->GetElement(); | ||
| 455 | |||
| 456 | const auto GeometryPass = [&](const std::string& name) { | ||
| 457 | if (stage == ShaderStage::Geometry && abuf->GetBuffer()) { | ||
| 458 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games | ||
| 459 | // set an 0x80000000 index for those and the shader fails to build. Find out why | ||
| 460 | // this happens and what's its intent. | ||
| 461 | return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) + | ||
| 462 | ") % MAX_VERTEX_INPUT]"; | ||
| 463 | } | ||
| 464 | return name; | ||
| 465 | }; | ||
| 466 | |||
| 467 | switch (attribute) { | ||
| 468 | case Attribute::Index::Position: | ||
| 469 | if (stage != ShaderStage::Fragment) { | ||
| 470 | return GeometryPass("position") + GetSwizzle(element); | ||
| 471 | } else { | ||
| 472 | return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element); | ||
| 473 | } | ||
| 474 | case Attribute::Index::PointCoord: | ||
| 475 | switch (element) { | ||
| 476 | case 0: | ||
| 477 | return "gl_PointCoord.x"; | ||
| 478 | case 1: | ||
| 479 | return "gl_PointCoord.y"; | ||
| 480 | case 2: | ||
| 481 | case 3: | ||
| 482 | return "0"; | ||
| 483 | } | ||
| 484 | UNREACHABLE(); | ||
| 485 | return "0"; | ||
| 486 | case Attribute::Index::TessCoordInstanceIDVertexID: | ||
| 487 | // TODO(Subv): Find out what the values are for the first two elements when inside a | ||
| 488 | // vertex shader, and what's the value of the fourth element when inside a Tess Eval | ||
| 489 | // shader. | ||
| 490 | ASSERT(stage == ShaderStage::Vertex); | ||
| 491 | switch (element) { | ||
| 492 | case 2: | ||
| 493 | // Config pack's first value is instance_id. | ||
| 494 | return "uintBitsToFloat(config_pack[0])"; | ||
| 495 | case 3: | ||
| 496 | return "uintBitsToFloat(gl_VertexID)"; | ||
| 497 | } | ||
| 498 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | ||
| 499 | return "0"; | ||
| 500 | case Attribute::Index::FrontFacing: | ||
| 501 | // TODO(Subv): Find out what the values are for the other elements. | ||
| 502 | ASSERT(stage == ShaderStage::Fragment); | ||
| 503 | switch (element) { | ||
| 504 | case 3: | ||
| 505 | return "itof(gl_FrontFacing ? -1 : 0)"; | ||
| 506 | } | ||
| 507 | UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element); | ||
| 508 | return "0"; | ||
| 509 | default: | ||
| 510 | if (attribute >= Attribute::Index::Attribute_0 && | ||
| 511 | attribute <= Attribute::Index::Attribute_31) { | ||
| 512 | return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element); | ||
| 513 | } | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); | ||
| 517 | |||
| 518 | } else if (const auto cbuf = std::get_if<CbufNode>(node)) { | ||
| 519 | const Node offset = cbuf->GetOffset(); | ||
| 520 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { | ||
| 521 | // Direct access | ||
| 522 | const u32 offset_imm = immediate->GetValue(); | ||
| 523 | return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), offset_imm / 4, | ||
| 524 | offset_imm % 4); | ||
| 525 | |||
| 526 | } else if (std::holds_alternative<OperationNode>(*offset)) { | ||
| 527 | // Indirect access | ||
| 528 | const std::string final_offset = code.GenerateTemporal(); | ||
| 529 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " + | ||
| 530 | std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';'); | ||
| 531 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), | ||
| 532 | final_offset, final_offset); | ||
| 533 | |||
| 534 | } else { | ||
| 535 | UNREACHABLE_MSG("Unmanaged offset node type"); | ||
| 536 | } | ||
| 537 | |||
| 538 | } else if (const auto lmem = std::get_if<LmemNode>(node)) { | ||
| 539 | return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); | ||
| 540 | |||
| 541 | } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { | ||
| 542 | return GetInternalFlag(internal_flag->GetFlag()); | ||
| 543 | |||
| 544 | } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { | ||
| 545 | // It's invalid to call conditional on nested nodes, use an operation instead | ||
| 546 | code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); | ||
| 547 | ++code.scope; | ||
| 548 | |||
| 549 | VisitBasicBlock(conditional->GetCode()); | ||
| 550 | |||
| 551 | --code.scope; | ||
| 552 | code.AddLine('}'); | ||
| 553 | return {}; | ||
| 554 | |||
| 555 | } else if (const auto comment = std::get_if<CommentNode>(node)) { | ||
| 556 | return "// " + comment->GetText(); | ||
| 557 | } | ||
| 558 | UNREACHABLE(); | ||
| 559 | return {}; | ||
| 560 | } | ||
| 561 | |||
| 562 | std::string ApplyPrecise(Operation operation, const std::string& value) { | ||
| 563 | if (!IsPrecise(operation)) { | ||
| 564 | return value; | ||
| 565 | } | ||
| 566 | // There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders | ||
| 567 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; | ||
| 568 | |||
| 569 | const std::string temporal = code.GenerateTemporal(); | ||
| 570 | code.AddLine(precise + "float " + temporal + " = " + value + ';'); | ||
| 571 | return temporal; | ||
| 572 | } | ||
| 573 | |||
| 574 | std::string VisitOperand(Operation operation, std::size_t operand_index) { | ||
| 575 | const auto& operand = operation[operand_index]; | ||
| 576 | const bool parent_precise = IsPrecise(operation); | ||
| 577 | const bool child_precise = IsPrecise(operand); | ||
| 578 | const bool child_trivial = !std::holds_alternative<OperationNode>(*operand); | ||
| 579 | if (!parent_precise || child_precise || child_trivial) { | ||
| 580 | return Visit(operand); | ||
| 581 | } | ||
| 582 | |||
| 583 | const std::string temporal = code.GenerateTemporal(); | ||
| 584 | code.AddLine("float " + temporal + " = " + Visit(operand) + ';'); | ||
| 585 | return temporal; | ||
| 586 | } | ||
| 587 | |||
| 588 | std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { | ||
| 589 | std::string value = VisitOperand(operation, operand_index); | ||
| 590 | |||
| 591 | switch (type) { | ||
| 592 | case Type::Bool: | ||
| 593 | case Type::Bool2: | ||
| 594 | case Type::Float: | ||
| 595 | return value; | ||
| 596 | case Type::Int: | ||
| 597 | return "ftoi(" + value + ')'; | ||
| 598 | case Type::Uint: | ||
| 599 | return "ftou(" + value + ')'; | ||
| 600 | case Type::HalfFloat: | ||
| 601 | if (!std::holds_alternative<MetaHalfArithmetic>(operation.GetMeta())) { | ||
| 602 | value = "toHalf2(" + value + ')'; | ||
| 603 | } | ||
| 604 | |||
| 605 | const auto& half_meta = std::get<MetaHalfArithmetic>(operation.GetMeta()); | ||
| 606 | switch (half_meta.types.at(operand_index)) { | ||
| 607 | case Tegra::Shader::HalfType::H0_H1: | ||
| 608 | return "toHalf2(" + value + ')'; | ||
| 609 | case Tegra::Shader::HalfType::F32: | ||
| 610 | return "vec2(" + value + ')'; | ||
| 611 | case Tegra::Shader::HalfType::H0_H0: | ||
| 612 | return "vec2(toHalf2(" + value + ")[0])"; | ||
| 613 | case Tegra::Shader::HalfType::H1_H1: | ||
| 614 | return "vec2(toHalf2(" + value + ")[1])"; | ||
| 615 | } | ||
| 616 | } | ||
| 617 | UNREACHABLE(); | ||
| 618 | return value; | ||
| 619 | } | ||
| 620 | |||
| 621 | std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { | ||
| 622 | switch (type) { | ||
| 623 | case Type::Bool: | ||
| 624 | case Type::Float: | ||
| 625 | if (needs_parenthesis) { | ||
| 626 | return '(' + value + ')'; | ||
| 627 | } | ||
| 628 | return value; | ||
| 629 | case Type::Int: | ||
| 630 | return "itof(" + value + ')'; | ||
| 631 | case Type::Uint: | ||
| 632 | return "utof(" + value + ')'; | ||
| 633 | case Type::HalfFloat: | ||
| 634 | return "fromHalf2(" + value + ')'; | ||
| 635 | } | ||
| 636 | UNREACHABLE(); | ||
| 637 | return value; | ||
| 638 | } | ||
| 639 | |||
| 640 | std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, | ||
| 641 | Type type_a, bool needs_parenthesis = true) { | ||
| 642 | return ApplyPrecise(operation, | ||
| 643 | BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', | ||
| 644 | result_type, needs_parenthesis)); | ||
| 645 | } | ||
| 646 | |||
| 647 | std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, | ||
| 648 | Type type_a, Type type_b) { | ||
| 649 | const std::string op_a = VisitOperand(operation, 0, type_a); | ||
| 650 | const std::string op_b = VisitOperand(operation, 1, type_b); | ||
| 651 | |||
| 652 | return ApplyPrecise( | ||
| 653 | operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type)); | ||
| 654 | } | ||
| 655 | |||
| 656 | std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, | ||
| 657 | Type type_a, Type type_b) { | ||
| 658 | const std::string op_a = VisitOperand(operation, 0, type_a); | ||
| 659 | const std::string op_b = VisitOperand(operation, 1, type_b); | ||
| 660 | |||
| 661 | return ApplyPrecise(operation, | ||
| 662 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type)); | ||
| 663 | } | ||
| 664 | |||
| 665 | std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, | ||
| 666 | Type type_a, Type type_b, Type type_c) { | ||
| 667 | const std::string op_a = VisitOperand(operation, 0, type_a); | ||
| 668 | const std::string op_b = VisitOperand(operation, 1, type_b); | ||
| 669 | const std::string op_c = VisitOperand(operation, 2, type_c); | ||
| 670 | |||
| 671 | return ApplyPrecise( | ||
| 672 | operation, | ||
| 673 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type)); | ||
| 674 | } | ||
| 675 | |||
| 676 | std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, | ||
| 677 | Type type_a, Type type_b, Type type_c, Type type_d) { | ||
| 678 | const std::string op_a = VisitOperand(operation, 0, type_a); | ||
| 679 | const std::string op_b = VisitOperand(operation, 1, type_b); | ||
| 680 | const std::string op_c = VisitOperand(operation, 2, type_c); | ||
| 681 | const std::string op_d = VisitOperand(operation, 3, type_d); | ||
| 682 | |||
| 683 | return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + | ||
| 684 | op_c + ", " + op_d + ')', | ||
| 685 | result_type)); | ||
| 686 | } | ||
| 687 | |||
| 688 | std::string GenerateTexture(Operation operation, const std::string& func, | ||
| 689 | std::string extra_cast(std::string) = nullptr) { | ||
| 690 | constexpr std::array<const char*, 4> coord_constructors = {"float", "vec2", "vec3", "vec4"}; | ||
| 691 | |||
| 692 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 693 | const auto count = static_cast<u32>(operation.GetOperandsCount()); | ||
| 694 | |||
| 695 | std::string expr = func; | ||
| 696 | expr += '('; | ||
| 697 | expr += GetSampler(meta.sampler); | ||
| 698 | expr += ", "; | ||
| 699 | |||
| 700 | expr += coord_constructors[meta.coords_count - 1]; | ||
| 701 | expr += '('; | ||
| 702 | for (u32 i = 0; i < count; ++i) { | ||
| 703 | const bool is_extra = i >= meta.coords_count; | ||
| 704 | const bool is_array = i == meta.array_index; | ||
| 705 | |||
| 706 | std::string operand = Visit(operation[i]); | ||
| 707 | if (is_extra && extra_cast != nullptr) { | ||
| 708 | operand = extra_cast(operand); | ||
| 709 | } | ||
| 710 | if (is_array) { | ||
| 711 | ASSERT(!is_extra); | ||
| 712 | operand = "float(ftoi(" + operand + "))"; | ||
| 713 | } | ||
| 714 | expr += operand; | ||
| 715 | if (i + 1 == meta.coords_count) { | ||
| 716 | expr += ')'; | ||
| 717 | } | ||
| 718 | if (i + 1 < count) { | ||
| 719 | expr += ", "; | ||
| 720 | } | ||
| 721 | } | ||
| 722 | expr += ')'; | ||
| 723 | return expr; | ||
| 724 | } | ||
| 725 | |||
| 726 | std::string Assign(Operation operation) { | ||
| 727 | const Node dest = operation[0]; | ||
| 728 | const Node src = operation[1]; | ||
| 729 | |||
| 730 | std::string target; | ||
| 731 | if (const auto gpr = std::get_if<GprNode>(dest)) { | ||
| 732 | if (gpr->GetIndex() == Register::ZeroIndex) { | ||
| 733 | // Writing to Register::ZeroIndex is a no op | ||
| 734 | return {}; | ||
| 735 | } | ||
| 736 | target = GetRegister(gpr->GetIndex()); | ||
| 737 | |||
| 738 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { | ||
| 739 | target = [&]() -> std::string { | ||
| 740 | switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { | ||
| 741 | case Attribute::Index::Position: | ||
| 742 | return "position" + GetSwizzle(abuf->GetElement()); | ||
| 743 | case Attribute::Index::PointSize: | ||
| 744 | return "gl_PointSize"; | ||
| 745 | case Attribute::Index::ClipDistances0123: | ||
| 746 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; | ||
| 747 | case Attribute::Index::ClipDistances4567: | ||
| 748 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; | ||
| 749 | default: | ||
| 750 | if (attribute >= Attribute::Index::Attribute_0 && | ||
| 751 | attribute <= Attribute::Index::Attribute_31) { | ||
| 752 | return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); | ||
| 753 | } | ||
| 754 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", | ||
| 755 | static_cast<u32>(attribute)); | ||
| 756 | return "0"; | ||
| 757 | } | ||
| 758 | }(); | ||
| 759 | |||
| 760 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | ||
| 761 | target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; | ||
| 762 | |||
| 763 | } else { | ||
| 764 | UNREACHABLE_MSG("Assign called without a proper target"); | ||
| 765 | } | ||
| 766 | |||
| 767 | code.AddLine(target + " = " + Visit(src) + ';'); | ||
| 768 | return {}; | ||
| 769 | } | ||
| 770 | |||
| 771 | std::string AssignComposite(Operation operation) { | ||
| 772 | const auto& meta = std::get<MetaComponents>(operation.GetMeta()); | ||
| 773 | |||
| 774 | const std::string composite = code.GenerateTemporal(); | ||
| 775 | code.AddLine("vec4 " + composite + " = " + Visit(operation[0]) + ';'); | ||
| 776 | |||
| 777 | constexpr u32 composite_size = 4; | ||
| 778 | for (u32 i = 0; i < composite_size; ++i) { | ||
| 779 | const auto gpr = std::get<GprNode>(*operation[i + 1]).GetIndex(); | ||
| 780 | if (gpr == Register::ZeroIndex) { | ||
| 781 | continue; | ||
| 782 | } | ||
| 783 | code.AddLine(GetRegister(gpr) + " = " + composite + | ||
| 784 | GetSwizzle(meta.GetSourceComponent(i)) + ';'); | ||
| 785 | } | ||
| 786 | return {}; | ||
| 787 | } | ||
| 788 | |||
| 789 | std::string AssignCompositeHalf(Operation operation) { | ||
| 790 | const auto& meta = std::get<MetaComponents>(operation.GetMeta()); | ||
| 791 | |||
| 792 | const std::string composite = code.GenerateTemporal(); | ||
| 793 | code.AddLine("vec4 " + composite + " = " + Visit(operation[0]) + ';'); | ||
| 794 | |||
| 795 | const auto ReadComponent = [&](u32 component) { | ||
| 796 | if (component < meta.count) { | ||
| 797 | return composite + '[' + std::to_string(meta.GetSourceComponent(component)) + ']'; | ||
| 798 | } | ||
| 799 | return std::string("0"); | ||
| 800 | }; | ||
| 801 | |||
| 802 | const auto dst1 = std::get<GprNode>(*operation[1]).GetIndex(); | ||
| 803 | const std::string src1 = "vec2(" + ReadComponent(0) + ", " + ReadComponent(1) + ')'; | ||
| 804 | code.AddLine(GetRegister(dst1) + " = utof(packHalf2x16(" + src1 + "))"); | ||
| 805 | |||
| 806 | if (meta.count > 2) { | ||
| 807 | const auto dst2 = std::get<GprNode>(*operation[2]).GetIndex(); | ||
| 808 | const std::string src2 = "vec2(" + ReadComponent(2) + ", " + ReadComponent(3) + ')'; | ||
| 809 | code.AddLine(GetRegister(dst2) + " = utof(packHalf2x16(" + src2 + "))"); | ||
| 810 | } | ||
| 811 | return {}; | ||
| 812 | } | ||
| 813 | |||
| 814 | std::string Composite(Operation operation) { | ||
| 815 | std::string value = "vec4("; | ||
| 816 | for (std::size_t i = 0; i < 4; ++i) { | ||
| 817 | value += Visit(operation[i]); | ||
| 818 | if (i < 3) | ||
| 819 | value += ", "; | ||
| 820 | } | ||
| 821 | value += ')'; | ||
| 822 | return value; | ||
| 823 | } | ||
| 824 | |||
| 825 | template <Type type> | ||
| 826 | std::string Add(Operation operation) { | ||
| 827 | return GenerateBinaryInfix(operation, "+", type, type, type); | ||
| 828 | } | ||
| 829 | |||
| 830 | template <Type type> | ||
| 831 | std::string Mul(Operation operation) { | ||
| 832 | return GenerateBinaryInfix(operation, "*", type, type, type); | ||
| 833 | } | ||
| 834 | |||
| 835 | template <Type type> | ||
| 836 | std::string Div(Operation operation) { | ||
| 837 | return GenerateBinaryInfix(operation, "/", type, type, type); | ||
| 838 | } | ||
| 839 | |||
| 840 | template <Type type> | ||
| 841 | std::string Fma(Operation operation) { | ||
| 842 | return GenerateTernary(operation, "fma", type, type, type, type); | ||
| 843 | } | ||
| 844 | |||
| 845 | template <Type type> | ||
| 846 | std::string Negate(Operation operation) { | ||
| 847 | return GenerateUnary(operation, "-", type, type, true); | ||
| 848 | } | ||
| 849 | |||
| 850 | template <Type type> | ||
| 851 | std::string Absolute(Operation operation) { | ||
| 852 | return GenerateUnary(operation, "abs", type, type, false); | ||
| 853 | } | ||
| 854 | |||
| 855 | std::string FClamp(Operation operation) { | ||
| 856 | return GenerateTernary(operation, "clamp", Type::Float, Type::Float, Type::Float, | ||
| 857 | Type::Float); | ||
| 858 | } | ||
| 859 | |||
| 860 | template <Type type> | ||
| 861 | std::string Min(Operation operation) { | ||
| 862 | return GenerateBinaryCall(operation, "min", type, type, type); | ||
| 863 | } | ||
| 864 | |||
| 865 | template <Type type> | ||
| 866 | std::string Max(Operation operation) { | ||
| 867 | return GenerateBinaryCall(operation, "max", type, type, type); | ||
| 868 | } | ||
| 869 | |||
| 870 | std::string Select(Operation operation) { | ||
| 871 | const std::string condition = Visit(operation[0]); | ||
| 872 | const std::string true_case = Visit(operation[1]); | ||
| 873 | const std::string false_case = Visit(operation[2]); | ||
| 874 | return ApplyPrecise(operation, | ||
| 875 | '(' + condition + " ? " + true_case + " : " + false_case + ')'); | ||
| 876 | } | ||
| 877 | |||
| 878 | std::string FCos(Operation operation) { | ||
| 879 | return GenerateUnary(operation, "cos", Type::Float, Type::Float, false); | ||
| 880 | } | ||
| 881 | |||
| 882 | std::string FSin(Operation operation) { | ||
| 883 | return GenerateUnary(operation, "sin", Type::Float, Type::Float, false); | ||
| 884 | } | ||
| 885 | |||
| 886 | std::string FExp2(Operation operation) { | ||
| 887 | return GenerateUnary(operation, "exp2", Type::Float, Type::Float, false); | ||
| 888 | } | ||
| 889 | |||
| 890 | std::string FLog2(Operation operation) { | ||
| 891 | return GenerateUnary(operation, "log2", Type::Float, Type::Float, false); | ||
| 892 | } | ||
| 893 | |||
| 894 | std::string FInverseSqrt(Operation operation) { | ||
| 895 | return GenerateUnary(operation, "inversesqrt", Type::Float, Type::Float, false); | ||
| 896 | } | ||
| 897 | |||
| 898 | std::string FSqrt(Operation operation) { | ||
| 899 | return GenerateUnary(operation, "sqrt", Type::Float, Type::Float, false); | ||
| 900 | } | ||
| 901 | |||
| 902 | std::string FRoundEven(Operation operation) { | ||
| 903 | return GenerateUnary(operation, "roundEven", Type::Float, Type::Float, false); | ||
| 904 | } | ||
| 905 | |||
| 906 | std::string FFloor(Operation operation) { | ||
| 907 | return GenerateUnary(operation, "floor", Type::Float, Type::Float, false); | ||
| 908 | } | ||
| 909 | |||
| 910 | std::string FCeil(Operation operation) { | ||
| 911 | return GenerateUnary(operation, "ceil", Type::Float, Type::Float, false); | ||
| 912 | } | ||
| 913 | |||
| 914 | std::string FTrunc(Operation operation) { | ||
| 915 | return GenerateUnary(operation, "trunc", Type::Float, Type::Float, false); | ||
| 916 | } | ||
| 917 | |||
| 918 | template <Type type> | ||
| 919 | std::string FCastInteger(Operation operation) { | ||
| 920 | return GenerateUnary(operation, "float", Type::Float, type, false); | ||
| 921 | } | ||
| 922 | |||
| 923 | std::string ICastFloat(Operation operation) { | ||
| 924 | return GenerateUnary(operation, "int", Type::Int, Type::Float, false); | ||
| 925 | } | ||
| 926 | |||
| 927 | std::string ICastUnsigned(Operation operation) { | ||
| 928 | return GenerateUnary(operation, "int", Type::Int, Type::Uint, false); | ||
| 929 | } | ||
| 930 | |||
| 931 | template <Type type> | ||
| 932 | std::string LogicalShiftLeft(Operation operation) { | ||
| 933 | return GenerateBinaryInfix(operation, "<<", type, type, Type::Uint); | ||
| 934 | } | ||
| 935 | |||
| 936 | std::string ILogicalShiftRight(Operation operation) { | ||
| 937 | const std::string op_a = VisitOperand(operation, 0, Type::Uint); | ||
| 938 | const std::string op_b = VisitOperand(operation, 1, Type::Uint); | ||
| 939 | |||
| 940 | return ApplyPrecise(operation, | ||
| 941 | BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int)); | ||
| 942 | } | ||
| 943 | |||
| 944 | std::string IArithmeticShiftRight(Operation operation) { | ||
| 945 | return GenerateBinaryInfix(operation, ">>", Type::Int, Type::Int, Type::Uint); | ||
| 946 | } | ||
| 947 | |||
| 948 | template <Type type> | ||
| 949 | std::string BitwiseAnd(Operation operation) { | ||
| 950 | return GenerateBinaryInfix(operation, "&", type, type, type); | ||
| 951 | } | ||
| 952 | |||
| 953 | template <Type type> | ||
| 954 | std::string BitwiseOr(Operation operation) { | ||
| 955 | return GenerateBinaryInfix(operation, "|", type, type, type); | ||
| 956 | } | ||
| 957 | |||
| 958 | template <Type type> | ||
| 959 | std::string BitwiseXor(Operation operation) { | ||
| 960 | return GenerateBinaryInfix(operation, "^", type, type, type); | ||
| 961 | } | ||
| 962 | |||
| 963 | template <Type type> | ||
| 964 | std::string BitwiseNot(Operation operation) { | ||
| 965 | return GenerateUnary(operation, "~", type, type, false); | ||
| 966 | } | ||
| 967 | |||
| 968 | std::string UCastFloat(Operation operation) { | ||
| 969 | return GenerateUnary(operation, "uint", Type::Uint, Type::Float, false); | ||
| 970 | } | ||
| 971 | |||
| 972 | std::string UCastSigned(Operation operation) { | ||
| 973 | return GenerateUnary(operation, "uint", Type::Uint, Type::Int, false); | ||
| 974 | } | ||
| 975 | |||
| 976 | std::string UShiftRight(Operation operation) { | ||
| 977 | return GenerateBinaryInfix(operation, ">>", Type::Uint, Type::Uint, Type::Uint); | ||
| 978 | } | ||
| 979 | |||
| 980 | template <Type type> | ||
| 981 | std::string BitfieldInsert(Operation operation) { | ||
| 982 | return GenerateQuaternary(operation, "bitfieldInsert", type, type, type, Type::Int, | ||
| 983 | Type::Int); | ||
| 984 | } | ||
| 985 | |||
| 986 | template <Type type> | ||
| 987 | std::string BitCount(Operation operation) { | ||
| 988 | return GenerateUnary(operation, "bitCount", type, type, false); | ||
| 989 | } | ||
| 990 | |||
| 991 | std::string HNegate(Operation operation) { | ||
| 992 | const auto GetNegate = [&](std::size_t index) -> std::string { | ||
| 993 | return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; | ||
| 994 | }; | ||
| 995 | const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + | ||
| 996 | GetNegate(1) + ", " + GetNegate(2) + "))"; | ||
| 997 | return BitwiseCastResult(value, Type::HalfFloat); | ||
| 998 | } | ||
| 999 | |||
| 1000 | std::string HMergeF32(Operation operation) { | ||
| 1001 | return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | std::string HMergeH0(Operation operation) { | ||
| 1005 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[1], toHalf2(" + | ||
| 1006 | Visit(operation[1]) + ")[0]))"; | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | std::string HMergeH1(Operation operation) { | ||
| 1010 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + | ||
| 1011 | Visit(operation[1]) + ")[1]))"; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | template <Type type> | ||
| 1015 | std::string LogicalLessThan(Operation operation) { | ||
| 1016 | return GenerateBinaryInfix(operation, "<", Type::Bool, type, type); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | template <Type type> | ||
| 1020 | std::string LogicalEqual(Operation operation) { | ||
| 1021 | return GenerateBinaryInfix(operation, "==", Type::Bool, type, type); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | template <Type type> | ||
| 1025 | std::string LogicalLessEqual(Operation operation) { | ||
| 1026 | return GenerateBinaryInfix(operation, "<=", Type::Bool, type, type); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | template <Type type> | ||
| 1030 | std::string LogicalGreaterThan(Operation operation) { | ||
| 1031 | return GenerateBinaryInfix(operation, ">", Type::Bool, type, type); | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | template <Type type> | ||
| 1035 | std::string LogicalNotEqual(Operation operation) { | ||
| 1036 | return GenerateBinaryInfix(operation, "!=", Type::Bool, type, type); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | template <Type type> | ||
| 1040 | std::string LogicalGreaterEqual(Operation operation) { | ||
| 1041 | return GenerateBinaryInfix(operation, ">=", Type::Bool, type, type); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | std::string LogicalFIsNan(Operation operation) { | ||
| 1045 | return GenerateUnary(operation, "isnan", Type::Bool, Type::Float, false); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | std::string LogicalAssign(Operation operation) { | ||
| 1049 | const Node dest = operation[0]; | ||
| 1050 | const Node src = operation[1]; | ||
| 1051 | |||
| 1052 | std::string target; | ||
| 1053 | |||
| 1054 | if (const auto pred = std::get_if<PredicateNode>(dest)) { | ||
| 1055 | ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment"); | ||
| 1056 | |||
| 1057 | const auto index = pred->GetIndex(); | ||
| 1058 | switch (index) { | ||
| 1059 | case Tegra::Shader::Pred::NeverExecute: | ||
| 1060 | case Tegra::Shader::Pred::UnusedIndex: | ||
| 1061 | // Writing to these predicates is a no-op | ||
| 1062 | return {}; | ||
| 1063 | } | ||
| 1064 | target = GetPredicate(index); | ||
| 1065 | } else if (const auto flag = std::get_if<InternalFlagNode>(dest)) { | ||
| 1066 | target = GetInternalFlag(flag->GetFlag()); | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | code.AddLine(target + " = " + Visit(src) + ';'); | ||
| 1070 | return {}; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | std::string LogicalAnd(Operation operation) { | ||
| 1074 | return GenerateBinaryInfix(operation, "&&", Type::Bool, Type::Bool, Type::Bool); | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | std::string LogicalOr(Operation operation) { | ||
| 1078 | return GenerateBinaryInfix(operation, "||", Type::Bool, Type::Bool, Type::Bool); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | std::string LogicalXor(Operation operation) { | ||
| 1082 | return GenerateBinaryInfix(operation, "^^", Type::Bool, Type::Bool, Type::Bool); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | std::string LogicalNegate(Operation operation) { | ||
| 1086 | return GenerateUnary(operation, "!", Type::Bool, Type::Bool, false); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | std::string LogicalPick2(Operation operation) { | ||
| 1090 | const std::string pair = VisitOperand(operation, 0, Type::Bool2); | ||
| 1091 | return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | std::string LogicalAll2(Operation operation) { | ||
| 1095 | return GenerateUnary(operation, "all", Type::Bool, Type::Bool2); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | std::string LogicalAny2(Operation operation) { | ||
| 1099 | return GenerateUnary(operation, "any", Type::Bool, Type::Bool2); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | std::string Logical2HLessThan(Operation operation) { | ||
| 1103 | return GenerateBinaryCall(operation, "lessThan", Type::Bool2, Type::HalfFloat, | ||
| 1104 | Type::HalfFloat); | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | std::string Logical2HEqual(Operation operation) { | ||
| 1108 | return GenerateBinaryCall(operation, "equal", Type::Bool2, Type::HalfFloat, | ||
| 1109 | Type::HalfFloat); | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | std::string Logical2HLessEqual(Operation operation) { | ||
| 1113 | return GenerateBinaryCall(operation, "lessThanEqual", Type::Bool2, Type::HalfFloat, | ||
| 1114 | Type::HalfFloat); | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | std::string Logical2HGreaterThan(Operation operation) { | ||
| 1118 | return GenerateBinaryCall(operation, "greaterThan", Type::Bool2, Type::HalfFloat, | ||
| 1119 | Type::HalfFloat); | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | std::string Logical2HNotEqual(Operation operation) { | ||
| 1123 | return GenerateBinaryCall(operation, "notEqual", Type::Bool2, Type::HalfFloat, | ||
| 1124 | Type::HalfFloat); | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | std::string Logical2HGreaterEqual(Operation operation) { | ||
| 1128 | return GenerateBinaryCall(operation, "greaterThanEqual", Type::Bool2, Type::HalfFloat, | ||
| 1129 | Type::HalfFloat); | ||
| 1130 | } | ||
| 1131 | |||
| 1132 | std::string F4Texture(Operation operation) { | ||
| 1133 | std::string expr = GenerateTexture(operation, "texture"); | ||
| 1134 | if (std::get<MetaTexture>(operation.GetMeta()).sampler.IsShadow()) { | ||
| 1135 | expr = "vec4(" + expr + ')'; | ||
| 1136 | } | ||
| 1137 | return expr; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | std::string F4TextureLod(Operation operation) { | ||
| 1141 | std::string expr = GenerateTexture(operation, "textureLod"); | ||
| 1142 | if (std::get<MetaTexture>(operation.GetMeta()).sampler.IsShadow()) { | ||
| 1143 | expr = "vec4(" + expr + ')'; | ||
| 1144 | } | ||
| 1145 | return expr; | ||
| 1146 | } | ||
| 1147 | |||
| 1148 | std::string F4TextureGather(Operation operation) { | ||
| 1149 | const bool is_shadow = std::get<MetaTexture>(operation.GetMeta()).sampler.IsShadow(); | ||
| 1150 | if (is_shadow) { | ||
| 1151 | return GenerateTexture(operation, "textureGather", | ||
| 1152 | [](std::string ref_z) { return ref_z; }); | ||
| 1153 | } else { | ||
| 1154 | return GenerateTexture(operation, "textureGather", | ||
| 1155 | [](std::string comp) { return "ftoi(" + comp + ')'; }); | ||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | std::string F4TextureQueryDimensions(Operation operation) { | ||
| 1160 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1161 | const std::string sampler = GetSampler(meta.sampler); | ||
| 1162 | const std::string lod = VisitOperand(operation, 0, Type::Int); | ||
| 1163 | |||
| 1164 | const std::string sizes = code.GenerateTemporal(); | ||
| 1165 | code.AddLine("ivec2 " + sizes + " = textureSize(" + sampler + ", " + lod + ");"); | ||
| 1166 | |||
| 1167 | const std::string mip_level = "textureQueryLevels(" + sampler + ')'; | ||
| 1168 | |||
| 1169 | return "itof(ivec4(" + sizes + ", 0, " + mip_level + "))"; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | std::string F4TextureQueryLod(Operation operation) { | ||
| 1173 | const std::string tmp = code.GenerateTemporal(); | ||
| 1174 | code.AddLine("vec2 " + tmp + " = " + GenerateTexture(operation, "textureQueryLod") + | ||
| 1175 | " * vec2(256);"); | ||
| 1176 | |||
| 1177 | return "vec4(itof(int(" + tmp + ".y)), utof(uint(" + tmp + ".x)), 0, 0)"; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | std::string F4TexelFetch(Operation operation) { | ||
| 1181 | constexpr std::array<const char*, 4> constructors = {"int", "ivec2", "ivec3", "ivec4"}; | ||
| 1182 | const auto& meta = std::get<MetaTexture>(operation.GetMeta()); | ||
| 1183 | const auto count = static_cast<u32>(operation.GetOperandsCount()); | ||
| 1184 | |||
| 1185 | std::string expr = "texelFetch("; | ||
| 1186 | expr += GetSampler(meta.sampler); | ||
| 1187 | expr += ", "; | ||
| 1188 | |||
| 1189 | expr += constructors[meta.coords_count - 1]; | ||
| 1190 | expr += '('; | ||
| 1191 | for (u32 i = 0; i < count; ++i) { | ||
| 1192 | expr += VisitOperand(operation, i, Type::Int); | ||
| 1193 | |||
| 1194 | if (i + 1 == meta.coords_count) { | ||
| 1195 | expr += ')'; | ||
| 1196 | } | ||
| 1197 | if (i + 1 < count) { | ||
| 1198 | expr += ", "; | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | expr += ')'; | ||
| 1202 | return expr; | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | std::string Ipa(Operation operation) { | ||
| 1206 | const auto& attribute = operation[0]; | ||
| 1207 | // TODO(Rodrigo): Special IPA attribute interactions | ||
| 1208 | return Visit(attribute); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | std::string Bra(Operation operation) { | ||
| 1212 | const auto target = std::get<ImmediateNode>(*operation[0]); | ||
| 1213 | code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target.GetValue())); | ||
| 1214 | code.AddLine("break;"); | ||
| 1215 | return {}; | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | std::string PushFlowStack(Operation operation) { | ||
| 1219 | const auto target = std::get<ImmediateNode>(*operation[0]); | ||
| 1220 | code.AddLine(fmt::format("flow_stack[flow_stack_top] = 0x{:x}u;", target.GetValue())); | ||
| 1221 | code.AddLine("flow_stack_top++;"); | ||
| 1222 | return {}; | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | std::string PopFlowStack(Operation operation) { | ||
| 1226 | code.AddLine("flow_stack_top--;"); | ||
| 1227 | code.AddLine("jmp_to = flow_stack[flow_stack_top];"); | ||
| 1228 | code.AddLine("break;"); | ||
| 1229 | return {}; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | std::string Exit(Operation operation) { | ||
| 1233 | if (stage != ShaderStage::Fragment) { | ||
| 1234 | code.AddLine("return;"); | ||
| 1235 | return {}; | ||
| 1236 | } | ||
| 1237 | const auto& used_registers = ir.GetRegisters(); | ||
| 1238 | const auto SafeGetRegister = [&](u32 reg) -> std::string { | ||
| 1239 | // TODO(Rodrigo): Replace with contains once C++20 releases | ||
| 1240 | if (used_registers.find(reg) != used_registers.end()) { | ||
| 1241 | return GetRegister(reg); | ||
| 1242 | } | ||
| 1243 | return "0.0f"; | ||
| 1244 | }; | ||
| 1245 | |||
| 1246 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); | ||
| 1247 | |||
| 1248 | code.AddLine("if (alpha_test[0] != 0) {"); | ||
| 1249 | ++code.scope; | ||
| 1250 | // We start on the register containing the alpha value in the first RT. | ||
| 1251 | u32 current_reg = 3; | ||
| 1252 | for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) { | ||
| 1253 | // TODO(Blinkhawk): verify the behavior of alpha testing on hardware when | ||
| 1254 | // multiple render targets are used. | ||
| 1255 | if (header.ps.IsColorComponentOutputEnabled(render_target, 0) || | ||
| 1256 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || | ||
| 1257 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || | ||
| 1258 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { | ||
| 1259 | code.AddLine( | ||
| 1260 | fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg))); | ||
| 1261 | current_reg += 4; | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | --code.scope; | ||
| 1265 | code.AddLine('}'); | ||
| 1266 | |||
| 1267 | // Write the color outputs using the data in the shader registers, disabled | ||
| 1268 | // rendertargets/components are skipped in the register assignment. | ||
| 1269 | current_reg = 0; | ||
| 1270 | for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) { | ||
| 1271 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | ||
| 1272 | for (u32 component = 0; component < 4; ++component) { | ||
| 1273 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { | ||
| 1274 | code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, | ||
| 1275 | SafeGetRegister(current_reg))); | ||
| 1276 | ++current_reg; | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | if (header.ps.omap.depth) { | ||
| 1282 | // The depth output is always 2 registers after the last color output, and current_reg | ||
| 1283 | // already contains one past the last color register. | ||
| 1284 | code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | code.AddLine("return;"); | ||
| 1288 | return {}; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | std::string Kil(Operation operation) { | ||
| 1292 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain | ||
| 1293 | // about unexecuted instructions that may follow this. | ||
| 1294 | code.AddLine("if (true) {"); | ||
| 1295 | ++code.scope; | ||
| 1296 | code.AddLine("discard;"); | ||
| 1297 | --code.scope; | ||
| 1298 | code.AddLine("}"); | ||
| 1299 | return {}; | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | std::string EmitVertex(Operation operation) { | ||
| 1303 | ASSERT_MSG(stage == ShaderStage::Geometry, | ||
| 1304 | "EmitVertex is expected to be used in a geometry shader."); | ||
| 1305 | |||
| 1306 | // If a geometry shader is attached, it will always flip (it's the last stage before | ||
| 1307 | // fragment). For more info about flipping, refer to gl_shader_gen.cpp. | ||
| 1308 | code.AddLine("position.xy *= viewport_flip.xy;"); | ||
| 1309 | code.AddLine("gl_Position = position;"); | ||
| 1310 | code.AddLine("position.w = 1.0;"); | ||
| 1311 | code.AddLine("EmitVertex();"); | ||
| 1312 | return {}; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | std::string EndPrimitive(Operation operation) { | ||
| 1316 | ASSERT_MSG(stage == ShaderStage::Geometry, | ||
| 1317 | "EndPrimitive is expected to be used in a geometry shader."); | ||
| 1318 | |||
| 1319 | code.AddLine("EndPrimitive();"); | ||
| 1320 | return {}; | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | std::string YNegate(Operation operation) { | ||
| 1324 | // Config pack's third value is Y_NEGATE's state. | ||
| 1325 | return "uintBitsToFloat(config_pack[2])"; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | static constexpr OperationDecompilersArray operation_decompilers = { | ||
| 1329 | &GLSLDecompiler::Assign, | ||
| 1330 | &GLSLDecompiler::AssignComposite, | ||
| 1331 | &GLSLDecompiler::AssignCompositeHalf, | ||
| 1332 | |||
| 1333 | &GLSLDecompiler::Composite, | ||
| 1334 | &GLSLDecompiler::Select, | ||
| 1335 | |||
| 1336 | &GLSLDecompiler::Add<Type::Float>, | ||
| 1337 | &GLSLDecompiler::Mul<Type::Float>, | ||
| 1338 | &GLSLDecompiler::Div<Type::Float>, | ||
| 1339 | &GLSLDecompiler::Fma<Type::Float>, | ||
| 1340 | &GLSLDecompiler::Negate<Type::Float>, | ||
| 1341 | &GLSLDecompiler::Absolute<Type::Float>, | ||
| 1342 | &GLSLDecompiler::FClamp, | ||
| 1343 | &GLSLDecompiler::Min<Type::Float>, | ||
| 1344 | &GLSLDecompiler::Max<Type::Float>, | ||
| 1345 | &GLSLDecompiler::FCos, | ||
| 1346 | &GLSLDecompiler::FSin, | ||
| 1347 | &GLSLDecompiler::FExp2, | ||
| 1348 | &GLSLDecompiler::FLog2, | ||
| 1349 | &GLSLDecompiler::FInverseSqrt, | ||
| 1350 | &GLSLDecompiler::FSqrt, | ||
| 1351 | &GLSLDecompiler::FRoundEven, | ||
| 1352 | &GLSLDecompiler::FFloor, | ||
| 1353 | &GLSLDecompiler::FCeil, | ||
| 1354 | &GLSLDecompiler::FTrunc, | ||
| 1355 | &GLSLDecompiler::FCastInteger<Type::Int>, | ||
| 1356 | &GLSLDecompiler::FCastInteger<Type::Uint>, | ||
| 1357 | |||
| 1358 | &GLSLDecompiler::Add<Type::Int>, | ||
| 1359 | &GLSLDecompiler::Mul<Type::Int>, | ||
| 1360 | &GLSLDecompiler::Div<Type::Int>, | ||
| 1361 | &GLSLDecompiler::Negate<Type::Int>, | ||
| 1362 | &GLSLDecompiler::Absolute<Type::Int>, | ||
| 1363 | &GLSLDecompiler::Min<Type::Int>, | ||
| 1364 | &GLSLDecompiler::Max<Type::Int>, | ||
| 1365 | |||
| 1366 | &GLSLDecompiler::ICastFloat, | ||
| 1367 | &GLSLDecompiler::ICastUnsigned, | ||
| 1368 | &GLSLDecompiler::LogicalShiftLeft<Type::Int>, | ||
| 1369 | &GLSLDecompiler::ILogicalShiftRight, | ||
| 1370 | &GLSLDecompiler::IArithmeticShiftRight, | ||
| 1371 | &GLSLDecompiler::BitwiseAnd<Type::Int>, | ||
| 1372 | &GLSLDecompiler::BitwiseOr<Type::Int>, | ||
| 1373 | &GLSLDecompiler::BitwiseXor<Type::Int>, | ||
| 1374 | &GLSLDecompiler::BitwiseNot<Type::Int>, | ||
| 1375 | &GLSLDecompiler::BitfieldInsert<Type::Int>, | ||
| 1376 | &GLSLDecompiler::BitCount<Type::Int>, | ||
| 1377 | |||
| 1378 | &GLSLDecompiler::Add<Type::Uint>, | ||
| 1379 | &GLSLDecompiler::Mul<Type::Uint>, | ||
| 1380 | &GLSLDecompiler::Div<Type::Uint>, | ||
| 1381 | &GLSLDecompiler::Min<Type::Uint>, | ||
| 1382 | &GLSLDecompiler::Max<Type::Uint>, | ||
| 1383 | &GLSLDecompiler::UCastFloat, | ||
| 1384 | &GLSLDecompiler::UCastSigned, | ||
| 1385 | &GLSLDecompiler::LogicalShiftLeft<Type::Uint>, | ||
| 1386 | &GLSLDecompiler::UShiftRight, | ||
| 1387 | &GLSLDecompiler::UShiftRight, | ||
| 1388 | &GLSLDecompiler::BitwiseAnd<Type::Uint>, | ||
| 1389 | &GLSLDecompiler::BitwiseOr<Type::Uint>, | ||
| 1390 | &GLSLDecompiler::BitwiseXor<Type::Uint>, | ||
| 1391 | &GLSLDecompiler::BitwiseNot<Type::Uint>, | ||
| 1392 | &GLSLDecompiler::BitfieldInsert<Type::Uint>, | ||
| 1393 | &GLSLDecompiler::BitCount<Type::Uint>, | ||
| 1394 | |||
| 1395 | &GLSLDecompiler::Add<Type::HalfFloat>, | ||
| 1396 | &GLSLDecompiler::Mul<Type::HalfFloat>, | ||
| 1397 | &GLSLDecompiler::Fma<Type::HalfFloat>, | ||
| 1398 | &GLSLDecompiler::Absolute<Type::HalfFloat>, | ||
| 1399 | &GLSLDecompiler::HNegate, | ||
| 1400 | &GLSLDecompiler::HMergeF32, | ||
| 1401 | &GLSLDecompiler::HMergeH0, | ||
| 1402 | &GLSLDecompiler::HMergeH1, | ||
| 1403 | |||
| 1404 | &GLSLDecompiler::LogicalAssign, | ||
| 1405 | &GLSLDecompiler::LogicalAnd, | ||
| 1406 | &GLSLDecompiler::LogicalOr, | ||
| 1407 | &GLSLDecompiler::LogicalXor, | ||
| 1408 | &GLSLDecompiler::LogicalNegate, | ||
| 1409 | &GLSLDecompiler::LogicalPick2, | ||
| 1410 | &GLSLDecompiler::LogicalAll2, | ||
| 1411 | &GLSLDecompiler::LogicalAny2, | ||
| 1412 | |||
| 1413 | &GLSLDecompiler::LogicalLessThan<Type::Float>, | ||
| 1414 | &GLSLDecompiler::LogicalEqual<Type::Float>, | ||
| 1415 | &GLSLDecompiler::LogicalLessEqual<Type::Float>, | ||
| 1416 | &GLSLDecompiler::LogicalGreaterThan<Type::Float>, | ||
| 1417 | &GLSLDecompiler::LogicalNotEqual<Type::Float>, | ||
| 1418 | &GLSLDecompiler::LogicalGreaterEqual<Type::Float>, | ||
| 1419 | &GLSLDecompiler::LogicalFIsNan, | ||
| 1420 | |||
| 1421 | &GLSLDecompiler::LogicalLessThan<Type::Int>, | ||
| 1422 | &GLSLDecompiler::LogicalEqual<Type::Int>, | ||
| 1423 | &GLSLDecompiler::LogicalLessEqual<Type::Int>, | ||
| 1424 | &GLSLDecompiler::LogicalGreaterThan<Type::Int>, | ||
| 1425 | &GLSLDecompiler::LogicalNotEqual<Type::Int>, | ||
| 1426 | &GLSLDecompiler::LogicalGreaterEqual<Type::Int>, | ||
| 1427 | |||
| 1428 | &GLSLDecompiler::LogicalLessThan<Type::Uint>, | ||
| 1429 | &GLSLDecompiler::LogicalEqual<Type::Uint>, | ||
| 1430 | &GLSLDecompiler::LogicalLessEqual<Type::Uint>, | ||
| 1431 | &GLSLDecompiler::LogicalGreaterThan<Type::Uint>, | ||
| 1432 | &GLSLDecompiler::LogicalNotEqual<Type::Uint>, | ||
| 1433 | &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, | ||
| 1434 | |||
| 1435 | &GLSLDecompiler::Logical2HLessThan, | ||
| 1436 | &GLSLDecompiler::Logical2HEqual, | ||
| 1437 | &GLSLDecompiler::Logical2HLessEqual, | ||
| 1438 | &GLSLDecompiler::Logical2HGreaterThan, | ||
| 1439 | &GLSLDecompiler::Logical2HNotEqual, | ||
| 1440 | &GLSLDecompiler::Logical2HGreaterEqual, | ||
| 1441 | |||
| 1442 | &GLSLDecompiler::F4Texture, | ||
| 1443 | &GLSLDecompiler::F4TextureLod, | ||
| 1444 | &GLSLDecompiler::F4TextureGather, | ||
| 1445 | &GLSLDecompiler::F4TextureQueryDimensions, | ||
| 1446 | &GLSLDecompiler::F4TextureQueryLod, | ||
| 1447 | &GLSLDecompiler::F4TexelFetch, | ||
| 1448 | |||
| 1449 | &GLSLDecompiler::Ipa, | ||
| 1450 | |||
| 1451 | &GLSLDecompiler::Bra, | ||
| 1452 | &GLSLDecompiler::PushFlowStack, // Ssy | ||
| 1453 | &GLSLDecompiler::PushFlowStack, // Brk | ||
| 1454 | &GLSLDecompiler::PopFlowStack, // Sync | ||
| 1455 | &GLSLDecompiler::PopFlowStack, // Brk | ||
| 1456 | &GLSLDecompiler::Exit, | ||
| 1457 | &GLSLDecompiler::Kil, | ||
| 1458 | |||
| 1459 | &GLSLDecompiler::EmitVertex, | ||
| 1460 | &GLSLDecompiler::EndPrimitive, | ||
| 1461 | |||
| 1462 | &GLSLDecompiler::YNegate, | ||
| 1463 | }; | ||
| 1464 | |||
| 1465 | std::string GetRegister(u32 index) const { | ||
| 1466 | return GetDeclarationWithSuffix(index, "gpr"); | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | std::string GetPredicate(Tegra::Shader::Pred pred) const { | ||
| 1470 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | std::string GetInputAttribute(Attribute::Index attribute) const { | ||
| 1474 | const auto index{static_cast<u32>(attribute) - | ||
| 1475 | static_cast<u32>(Attribute::Index::Attribute_0)}; | ||
| 1476 | return GetDeclarationWithSuffix(index, "input_attr"); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | std::string GetOutputAttribute(Attribute::Index attribute) const { | ||
| 1480 | const auto index{static_cast<u32>(attribute) - | ||
| 1481 | static_cast<u32>(Attribute::Index::Attribute_0)}; | ||
| 1482 | return GetDeclarationWithSuffix(index, "output_attr"); | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | std::string GetConstBuffer(u32 index) const { | ||
| 1486 | return GetDeclarationWithSuffix(index, "cbuf"); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | std::string GetConstBufferBlock(u32 index) const { | ||
| 1490 | return GetDeclarationWithSuffix(index, "cbuf_block"); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | std::string GetLocalMemory() const { | ||
| 1494 | return "lmem_" + suffix; | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | std::string GetInternalFlag(InternalFlag flag) const { | ||
| 1498 | constexpr std::array<const char*, 4> InternalFlagNames = {"zero_flag", "sign_flag", | ||
| 1499 | "carry_flag", "overflow_flag"}; | ||
| 1500 | const auto index = static_cast<u32>(flag); | ||
| 1501 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); | ||
| 1502 | |||
| 1503 | return std::string(InternalFlagNames[index]) + '_' + suffix; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | std::string GetSampler(const Sampler& sampler) const { | ||
| 1507 | return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler"); | ||
| 1508 | } | ||
| 1509 | |||
| 1510 | std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { | ||
| 1511 | return name + '_' + std::to_string(index) + '_' + suffix; | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | const ShaderIR& ir; | ||
| 1515 | const ShaderStage stage; | ||
| 1516 | const std::string suffix; | ||
| 1517 | const Header header; | ||
| 1518 | |||
| 1519 | ShaderWriter code; | ||
| 1520 | }; | ||
| 1521 | |||
| 1522 | std::string GetCommonDeclarations() { | ||
| 1523 | return "#define MAX_CONSTBUFFER_ELEMENTS " + std::to_string(MAX_CONSTBUFFER_ELEMENTS) + | ||
| 1524 | "\n" | ||
| 1525 | "#define ftoi floatBitsToInt\n" | ||
| 1526 | "#define ftou floatBitsToUint\n" | ||
| 1527 | "#define itof intBitsToFloat\n" | ||
| 1528 | "#define utof uintBitsToFloat\n\n" | ||
| 1529 | "float fromHalf2(vec2 pair) {\n" | ||
| 1530 | " return utof(packHalf2x16(pair));\n" | ||
| 1531 | "}\n\n" | ||
| 1532 | "vec2 toHalf2(float value) {\n" | ||
| 1533 | " return unpackHalf2x16(ftou(value));\n" | ||
| 1534 | "}\n\n"; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | ProgramResult Decompile(const ShaderIR& ir, Maxwell::ShaderStage stage, const std::string& suffix) { | ||
| 1538 | GLSLDecompiler decompiler(ir, stage, suffix); | ||
| 1539 | decompiler.Decompile(); | ||
| 1540 | return {decompiler.GetResult(), decompiler.GetShaderEntries()}; | ||
| 1541 | } | ||
| 1542 | |||
| 1543 | } // namespace OpenGL::GLShader \ No newline at end of file | ||
diff --git a/src/video_core/shader/glsl_decompiler.h b/src/video_core/shader/glsl_decompiler.h deleted file mode 100644 index 396a560d8..000000000 --- a/src/video_core/shader/glsl_decompiler.h +++ /dev/null | |||
| @@ -1,88 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/engines/maxwell_3d.h" | ||
| 13 | #include "video_core/shader/shader_ir.h" | ||
| 14 | |||
| 15 | namespace VideoCommon::Shader { | ||
| 16 | class ShaderIR; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace OpenGL::GLShader { | ||
| 20 | |||
| 21 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 22 | |||
| 23 | class ConstBufferEntry : public VideoCommon::Shader::ConstBuffer { | ||
| 24 | public: | ||
| 25 | explicit ConstBufferEntry(const VideoCommon::Shader::ConstBuffer& entry, | ||
| 26 | Maxwell::ShaderStage stage, const std::string& name, u32 index) | ||
| 27 | : VideoCommon::Shader::ConstBuffer{entry}, stage{stage}, name{name}, index{index} {} | ||
| 28 | |||
| 29 | const std::string& GetName() const { | ||
| 30 | return name; | ||
| 31 | } | ||
| 32 | |||
| 33 | Maxwell::ShaderStage GetStage() const { | ||
| 34 | return stage; | ||
| 35 | } | ||
| 36 | |||
| 37 | u32 GetIndex() const { | ||
| 38 | return index; | ||
| 39 | } | ||
| 40 | |||
| 41 | u32 GetHash() const { | ||
| 42 | return (static_cast<u32>(stage) << 16) | index; | ||
| 43 | } | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::string name; | ||
| 47 | Maxwell::ShaderStage stage{}; | ||
| 48 | u32 index{}; | ||
| 49 | }; | ||
| 50 | |||
| 51 | class SamplerEntry : public VideoCommon::Shader::Sampler { | ||
| 52 | public: | ||
| 53 | explicit SamplerEntry(const VideoCommon::Shader::Sampler& entry, Maxwell::ShaderStage stage, | ||
| 54 | const std::string& name) | ||
| 55 | : VideoCommon::Shader::Sampler{entry}, stage{stage}, name{name} {} | ||
| 56 | |||
| 57 | const std::string& GetName() const { | ||
| 58 | return name; | ||
| 59 | } | ||
| 60 | |||
| 61 | Maxwell::ShaderStage GetStage() const { | ||
| 62 | return stage; | ||
| 63 | } | ||
| 64 | |||
| 65 | u32 GetHash() const { | ||
| 66 | return (static_cast<u32>(stage) << 16) | static_cast<u32>(GetIndex()); | ||
| 67 | } | ||
| 68 | |||
| 69 | private: | ||
| 70 | std::string name; | ||
| 71 | Maxwell::ShaderStage stage{}; | ||
| 72 | }; | ||
| 73 | |||
| 74 | struct ShaderEntries { | ||
| 75 | std::vector<ConstBufferEntry> const_buffers; | ||
| 76 | std::vector<SamplerEntry> samplers; | ||
| 77 | std::array<bool, Maxwell::NumClipDistances> clip_distances{}; | ||
| 78 | std::size_t shader_length{}; | ||
| 79 | }; | ||
| 80 | |||
| 81 | using ProgramResult = std::pair<std::string, ShaderEntries>; | ||
| 82 | |||
| 83 | std::string GetCommonDeclarations(); | ||
| 84 | |||
| 85 | ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, | ||
| 86 | const std::string& suffix); | ||
| 87 | |||
| 88 | } // namespace OpenGL::GLShader \ No newline at end of file | ||