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