diff options
| author | 2019-05-20 20:42:40 -0400 | |
|---|---|---|
| committer | 2019-05-20 20:42:40 -0400 | |
| commit | 9a17b20896166b599b22b21cb0057706ad79542a (patch) | |
| tree | f8cf364953e9675e622c273426a5e562c6d7571d /src | |
| parent | Merge pull request #2499 from lioncash/translate (diff) | |
| parent | gl_shader_decompiler: Tidy up minor remaining cases of unnecessary std::strin... (diff) | |
| download | yuzu-9a17b20896166b599b22b21cb0057706ad79542a.tar.gz yuzu-9a17b20896166b599b22b21cb0057706ad79542a.tar.xz yuzu-9a17b20896166b599b22b21cb0057706ad79542a.zip | |
Merge pull request #2494 from lioncash/shader-text
gl_shader_decompiler: Add AddLine() overloads with single function that forwards to libfmt
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 376 |
1 files changed, 195 insertions, 181 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 4bff54a59..4c380677d 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -57,15 +57,14 @@ public: | |||
| 57 | shader_source += text; | 57 | shader_source += text; |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | void AddLine(std::string_view text) { | 60 | // Forwards all arguments directly to libfmt. |
| 61 | AddExpression(text); | 61 | // Note that all formatting requirements for fmt must be |
| 62 | AddNewLine(); | 62 | // obeyed when using this function. (e.g. {{ must be used |
| 63 | } | 63 | // printing the character '{' is desirable. Ditto for }} and '}', |
| 64 | 64 | // etc). | |
| 65 | void AddLine(char character) { | 65 | template <typename... Args> |
| 66 | DEBUG_ASSERT(scope >= 0); | 66 | void AddLine(std::string_view text, Args&&... args) { |
| 67 | AppendIndentation(); | 67 | AddExpression(fmt::format(text, std::forward<Args>(args)...)); |
| 68 | shader_source += character; | ||
| 69 | AddNewLine(); | 68 | AddNewLine(); |
| 70 | } | 69 | } |
| 71 | 70 | ||
| @@ -75,9 +74,7 @@ public: | |||
| 75 | } | 74 | } |
| 76 | 75 | ||
| 77 | std::string GenerateTemporary() { | 76 | std::string GenerateTemporary() { |
| 78 | std::string temporary = "tmp"; | 77 | return fmt::format("tmp{}", temporary_index++); |
| 79 | temporary += std::to_string(temporary_index++); | ||
| 80 | return temporary; | ||
| 81 | } | 78 | } |
| 82 | 79 | ||
| 83 | std::string GetResult() { | 80 | std::string GetResult() { |
| @@ -167,41 +164,41 @@ public: | |||
| 167 | DeclareSamplers(); | 164 | DeclareSamplers(); |
| 168 | DeclarePhysicalAttributeReader(); | 165 | DeclarePhysicalAttributeReader(); |
| 169 | 166 | ||
| 170 | code.AddLine("void execute_" + suffix + "() {"); | 167 | code.AddLine("void execute_{}() {{", suffix); |
| 171 | ++code.scope; | 168 | ++code.scope; |
| 172 | 169 | ||
| 173 | // VM's program counter | 170 | // VM's program counter |
| 174 | const auto first_address = ir.GetBasicBlocks().begin()->first; | 171 | const auto first_address = ir.GetBasicBlocks().begin()->first; |
| 175 | code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;"); | 172 | code.AddLine("uint jmp_to = {}u;", first_address); |
| 176 | 173 | ||
| 177 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems | 174 | // TODO(Subv): Figure out the actual depth of the flow stack, for now it seems |
| 178 | // unlikely that shaders will use 20 nested SSYs and PBKs. | 175 | // unlikely that shaders will use 20 nested SSYs and PBKs. |
| 179 | constexpr u32 FLOW_STACK_SIZE = 20; | 176 | constexpr u32 FLOW_STACK_SIZE = 20; |
| 180 | code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE)); | 177 | code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE); |
| 181 | code.AddLine("uint flow_stack_top = 0u;"); | 178 | code.AddLine("uint flow_stack_top = 0u;"); |
| 182 | 179 | ||
| 183 | code.AddLine("while (true) {"); | 180 | code.AddLine("while (true) {{"); |
| 184 | ++code.scope; | 181 | ++code.scope; |
| 185 | 182 | ||
| 186 | code.AddLine("switch (jmp_to) {"); | 183 | code.AddLine("switch (jmp_to) {{"); |
| 187 | 184 | ||
| 188 | for (const auto& pair : ir.GetBasicBlocks()) { | 185 | for (const auto& pair : ir.GetBasicBlocks()) { |
| 189 | const auto [address, bb] = pair; | 186 | const auto [address, bb] = pair; |
| 190 | code.AddLine(fmt::format("case 0x{:x}u: {{", address)); | 187 | code.AddLine("case 0x{:x}u: {{", address); |
| 191 | ++code.scope; | 188 | ++code.scope; |
| 192 | 189 | ||
| 193 | VisitBlock(bb); | 190 | VisitBlock(bb); |
| 194 | 191 | ||
| 195 | --code.scope; | 192 | --code.scope; |
| 196 | code.AddLine('}'); | 193 | code.AddLine("}}"); |
| 197 | } | 194 | } |
| 198 | 195 | ||
| 199 | code.AddLine("default: return;"); | 196 | code.AddLine("default: return;"); |
| 200 | code.AddLine('}'); | 197 | code.AddLine("}}"); |
| 201 | 198 | ||
| 202 | for (std::size_t i = 0; i < 2; ++i) { | 199 | for (std::size_t i = 0; i < 2; ++i) { |
| 203 | --code.scope; | 200 | --code.scope; |
| 204 | code.AddLine('}'); | 201 | code.AddLine("}}"); |
| 205 | } | 202 | } |
| 206 | } | 203 | } |
| 207 | 204 | ||
| @@ -241,12 +238,13 @@ private: | |||
| 241 | } | 238 | } |
| 242 | 239 | ||
| 243 | void DeclareGeometry() { | 240 | void DeclareGeometry() { |
| 244 | if (stage != ShaderStage::Geometry) | 241 | if (stage != ShaderStage::Geometry) { |
| 245 | return; | 242 | return; |
| 243 | } | ||
| 246 | 244 | ||
| 247 | const auto topology = GetTopologyName(header.common3.output_topology); | 245 | const auto topology = GetTopologyName(header.common3.output_topology); |
| 248 | const auto max_vertices = std::to_string(header.common4.max_output_vertices); | 246 | const auto max_vertices = header.common4.max_output_vertices.Value(); |
| 249 | code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;"); | 247 | code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices); |
| 250 | code.AddNewLine(); | 248 | code.AddNewLine(); |
| 251 | 249 | ||
| 252 | DeclareVertexRedeclarations(); | 250 | DeclareVertexRedeclarations(); |
| @@ -255,7 +253,7 @@ private: | |||
| 255 | void DeclareVertexRedeclarations() { | 253 | void DeclareVertexRedeclarations() { |
| 256 | bool clip_distances_declared = false; | 254 | bool clip_distances_declared = false; |
| 257 | 255 | ||
| 258 | code.AddLine("out gl_PerVertex {"); | 256 | code.AddLine("out gl_PerVertex {{"); |
| 259 | ++code.scope; | 257 | ++code.scope; |
| 260 | 258 | ||
| 261 | code.AddLine("vec4 gl_Position;"); | 259 | code.AddLine("vec4 gl_Position;"); |
| @@ -271,40 +269,42 @@ private: | |||
| 271 | } | 269 | } |
| 272 | 270 | ||
| 273 | --code.scope; | 271 | --code.scope; |
| 274 | code.AddLine("};"); | 272 | code.AddLine("}};"); |
| 275 | code.AddNewLine(); | 273 | code.AddNewLine(); |
| 276 | } | 274 | } |
| 277 | 275 | ||
| 278 | void DeclareRegisters() { | 276 | void DeclareRegisters() { |
| 279 | const auto& registers = ir.GetRegisters(); | 277 | const auto& registers = ir.GetRegisters(); |
| 280 | for (const u32 gpr : registers) { | 278 | for (const u32 gpr : registers) { |
| 281 | code.AddLine("float " + GetRegister(gpr) + " = 0;"); | 279 | code.AddLine("float {} = 0;", GetRegister(gpr)); |
| 282 | } | 280 | } |
| 283 | if (!registers.empty()) | 281 | if (!registers.empty()) { |
| 284 | code.AddNewLine(); | 282 | code.AddNewLine(); |
| 283 | } | ||
| 285 | } | 284 | } |
| 286 | 285 | ||
| 287 | void DeclarePredicates() { | 286 | void DeclarePredicates() { |
| 288 | const auto& predicates = ir.GetPredicates(); | 287 | const auto& predicates = ir.GetPredicates(); |
| 289 | for (const auto pred : predicates) { | 288 | for (const auto pred : predicates) { |
| 290 | code.AddLine("bool " + GetPredicate(pred) + " = false;"); | 289 | code.AddLine("bool {} = false;", GetPredicate(pred)); |
| 291 | } | 290 | } |
| 292 | if (!predicates.empty()) | 291 | if (!predicates.empty()) { |
| 293 | code.AddNewLine(); | 292 | code.AddNewLine(); |
| 293 | } | ||
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | void DeclareLocalMemory() { | 296 | void DeclareLocalMemory() { |
| 297 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { | 297 | if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) { |
| 298 | const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; | 298 | const auto element_count = Common::AlignUp(local_memory_size, 4) / 4; |
| 299 | code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];"); | 299 | code.AddLine("float {}[{}];", GetLocalMemory(), element_count); |
| 300 | code.AddNewLine(); | 300 | code.AddNewLine(); |
| 301 | } | 301 | } |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | void DeclareInternalFlags() { | 304 | void DeclareInternalFlags() { |
| 305 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { | 305 | for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) { |
| 306 | const InternalFlag flag_code = static_cast<InternalFlag>(flag); | 306 | const auto flag_code = static_cast<InternalFlag>(flag); |
| 307 | code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;"); | 307 | code.AddLine("bool {} = false;", GetInternalFlag(flag_code)); |
| 308 | } | 308 | } |
| 309 | code.AddNewLine(); | 309 | code.AddNewLine(); |
| 310 | } | 310 | } |
| @@ -343,8 +343,9 @@ private: | |||
| 343 | DeclareInputAttribute(index, false); | 343 | DeclareInputAttribute(index, false); |
| 344 | } | 344 | } |
| 345 | } | 345 | } |
| 346 | if (!attributes.empty()) | 346 | if (!attributes.empty()) { |
| 347 | code.AddNewLine(); | 347 | code.AddNewLine(); |
| 348 | } | ||
| 348 | } | 349 | } |
| 349 | 350 | ||
| 350 | void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { | 351 | void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { |
| @@ -370,8 +371,7 @@ private: | |||
| 370 | location += GENERIC_VARYING_START_LOCATION; | 371 | location += GENERIC_VARYING_START_LOCATION; |
| 371 | } | 372 | } |
| 372 | 373 | ||
| 373 | code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " + | 374 | code.AddLine("layout (location = {}) {} in vec4 {};", name, location, suffix, name); |
| 374 | name + ';'); | ||
| 375 | } | 375 | } |
| 376 | 376 | ||
| 377 | void DeclareOutputAttributes() { | 377 | void DeclareOutputAttributes() { |
| @@ -389,23 +389,23 @@ private: | |||
| 389 | DeclareOutputAttribute(index); | 389 | DeclareOutputAttribute(index); |
| 390 | } | 390 | } |
| 391 | } | 391 | } |
| 392 | if (!attributes.empty()) | 392 | if (!attributes.empty()) { |
| 393 | code.AddNewLine(); | 393 | code.AddNewLine(); |
| 394 | } | ||
| 394 | } | 395 | } |
| 395 | 396 | ||
| 396 | void DeclareOutputAttribute(Attribute::Index index) { | 397 | void DeclareOutputAttribute(Attribute::Index index) { |
| 397 | const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; | 398 | const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION}; |
| 398 | code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " + | 399 | code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index)); |
| 399 | GetOutputAttribute(index) + ';'); | ||
| 400 | } | 400 | } |
| 401 | 401 | ||
| 402 | void DeclareConstantBuffers() { | 402 | void DeclareConstantBuffers() { |
| 403 | for (const auto& entry : ir.GetConstantBuffers()) { | 403 | for (const auto& entry : ir.GetConstantBuffers()) { |
| 404 | const auto [index, size] = entry; | 404 | const auto [index, size] = entry; |
| 405 | code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) + | 405 | code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index, |
| 406 | ") uniform " + GetConstBufferBlock(index) + " {"); | 406 | GetConstBufferBlock(index)); |
| 407 | code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];"); | 407 | code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index)); |
| 408 | code.AddLine("};"); | 408 | code.AddLine("}};"); |
| 409 | code.AddNewLine(); | 409 | code.AddNewLine(); |
| 410 | } | 410 | } |
| 411 | } | 411 | } |
| @@ -417,17 +417,16 @@ private: | |||
| 417 | // Since we don't know how the shader will use the shader, hint the driver to disable as | 417 | // Since we don't know how the shader will use the shader, hint the driver to disable as |
| 418 | // much optimizations as possible | 418 | // much optimizations as possible |
| 419 | std::string qualifier = "coherent volatile"; | 419 | std::string qualifier = "coherent volatile"; |
| 420 | if (usage.is_read && !usage.is_written) | 420 | if (usage.is_read && !usage.is_written) { |
| 421 | qualifier += " readonly"; | 421 | qualifier += " readonly"; |
| 422 | else if (usage.is_written && !usage.is_read) | 422 | } else if (usage.is_written && !usage.is_read) { |
| 423 | qualifier += " writeonly"; | 423 | qualifier += " writeonly"; |
| 424 | } | ||
| 424 | 425 | ||
| 425 | const std::string binding = | 426 | code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{", |
| 426 | fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset); | 427 | base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base)); |
| 427 | code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " + | 428 | code.AddLine(" float {}[];", GetGlobalMemory(base)); |
| 428 | GetGlobalMemoryBlock(base) + " {"); | 429 | code.AddLine("}};"); |
| 429 | code.AddLine(" float " + GetGlobalMemory(base) + "[];"); | ||
| 430 | code.AddLine("};"); | ||
| 431 | code.AddNewLine(); | 430 | code.AddNewLine(); |
| 432 | } | 431 | } |
| 433 | } | 432 | } |
| @@ -435,7 +434,7 @@ private: | |||
| 435 | void DeclareSamplers() { | 434 | void DeclareSamplers() { |
| 436 | const auto& samplers = ir.GetSamplers(); | 435 | const auto& samplers = ir.GetSamplers(); |
| 437 | for (const auto& sampler : samplers) { | 436 | for (const auto& sampler : samplers) { |
| 438 | std::string sampler_type = [&]() { | 437 | std::string sampler_type = [&sampler] { |
| 439 | switch (sampler.GetType()) { | 438 | switch (sampler.GetType()) { |
| 440 | case Tegra::Shader::TextureType::Texture1D: | 439 | case Tegra::Shader::TextureType::Texture1D: |
| 441 | return "sampler1D"; | 440 | return "sampler1D"; |
| @@ -450,25 +449,28 @@ private: | |||
| 450 | return "sampler2D"; | 449 | return "sampler2D"; |
| 451 | } | 450 | } |
| 452 | }(); | 451 | }(); |
| 453 | if (sampler.IsArray()) | 452 | if (sampler.IsArray()) { |
| 454 | sampler_type += "Array"; | 453 | sampler_type += "Array"; |
| 455 | if (sampler.IsShadow()) | 454 | } |
| 455 | if (sampler.IsShadow()) { | ||
| 456 | sampler_type += "Shadow"; | 456 | sampler_type += "Shadow"; |
| 457 | } | ||
| 457 | 458 | ||
| 458 | code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) + | 459 | code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(), |
| 459 | ") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';'); | 460 | sampler_type, GetSampler(sampler)); |
| 460 | } | 461 | } |
| 461 | if (!samplers.empty()) | 462 | if (!samplers.empty()) { |
| 462 | code.AddNewLine(); | 463 | code.AddNewLine(); |
| 464 | } | ||
| 463 | } | 465 | } |
| 464 | 466 | ||
| 465 | void DeclarePhysicalAttributeReader() { | 467 | void DeclarePhysicalAttributeReader() { |
| 466 | if (!ir.HasPhysicalAttributes()) { | 468 | if (!ir.HasPhysicalAttributes()) { |
| 467 | return; | 469 | return; |
| 468 | } | 470 | } |
| 469 | code.AddLine("float readPhysicalAttribute(uint physical_address) {"); | 471 | code.AddLine("float readPhysicalAttribute(uint physical_address) {{"); |
| 470 | ++code.scope; | 472 | ++code.scope; |
| 471 | code.AddLine("switch (physical_address) {"); | 473 | code.AddLine("switch (physical_address) {{"); |
| 472 | 474 | ||
| 473 | // Just declare generic attributes for now. | 475 | // Just declare generic attributes for now. |
| 474 | const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())}; | 476 | const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())}; |
| @@ -483,15 +485,15 @@ private: | |||
| 483 | const bool declared{stage != ShaderStage::Fragment || | 485 | const bool declared{stage != ShaderStage::Fragment || |
| 484 | header.ps.GetAttributeUse(index) != AttributeUse::Unused}; | 486 | header.ps.GetAttributeUse(index) != AttributeUse::Unused}; |
| 485 | const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; | 487 | const std::string value{declared ? ReadAttribute(attribute, element) : "0"}; |
| 486 | code.AddLine(fmt::format("case 0x{:x}: return {};", address, value)); | 488 | code.AddLine("case 0x{:x}: return {};", address, value); |
| 487 | } | 489 | } |
| 488 | } | 490 | } |
| 489 | 491 | ||
| 490 | code.AddLine("default: return 0;"); | 492 | code.AddLine("default: return 0;"); |
| 491 | 493 | ||
| 492 | code.AddLine('}'); | 494 | code.AddLine("}}"); |
| 493 | --code.scope; | 495 | --code.scope; |
| 494 | code.AddLine('}'); | 496 | code.AddLine("}}"); |
| 495 | code.AddNewLine(); | 497 | code.AddNewLine(); |
| 496 | } | 498 | } |
| 497 | 499 | ||
| @@ -516,23 +518,26 @@ private: | |||
| 516 | return {}; | 518 | return {}; |
| 517 | } | 519 | } |
| 518 | return (this->*decompiler)(*operation); | 520 | return (this->*decompiler)(*operation); |
| 521 | } | ||
| 519 | 522 | ||
| 520 | } else if (const auto gpr = std::get_if<GprNode>(node)) { | 523 | if (const auto gpr = std::get_if<GprNode>(node)) { |
| 521 | const u32 index = gpr->GetIndex(); | 524 | const u32 index = gpr->GetIndex(); |
| 522 | if (index == Register::ZeroIndex) { | 525 | if (index == Register::ZeroIndex) { |
| 523 | return "0"; | 526 | return "0"; |
| 524 | } | 527 | } |
| 525 | return GetRegister(index); | 528 | return GetRegister(index); |
| 529 | } | ||
| 526 | 530 | ||
| 527 | } else if (const auto immediate = std::get_if<ImmediateNode>(node)) { | 531 | if (const auto immediate = std::get_if<ImmediateNode>(node)) { |
| 528 | const u32 value = immediate->GetValue(); | 532 | const u32 value = immediate->GetValue(); |
| 529 | if (value < 10) { | 533 | if (value < 10) { |
| 530 | // For eyecandy avoid using hex numbers on single digits | 534 | // For eyecandy avoid using hex numbers on single digits |
| 531 | return fmt::format("utof({}u)", immediate->GetValue()); | 535 | return fmt::format("utof({}u)", immediate->GetValue()); |
| 532 | } | 536 | } |
| 533 | return fmt::format("utof(0x{:x}u)", immediate->GetValue()); | 537 | return fmt::format("utof(0x{:x}u)", immediate->GetValue()); |
| 538 | } | ||
| 534 | 539 | ||
| 535 | } else if (const auto predicate = std::get_if<PredicateNode>(node)) { | 540 | if (const auto predicate = std::get_if<PredicateNode>(node)) { |
| 536 | const auto value = [&]() -> std::string { | 541 | const auto value = [&]() -> std::string { |
| 537 | switch (const auto index = predicate->GetIndex(); index) { | 542 | switch (const auto index = predicate->GetIndex(); index) { |
| 538 | case Tegra::Shader::Pred::UnusedIndex: | 543 | case Tegra::Shader::Pred::UnusedIndex: |
| @@ -544,19 +549,22 @@ private: | |||
| 544 | } | 549 | } |
| 545 | }(); | 550 | }(); |
| 546 | if (predicate->IsNegated()) { | 551 | if (predicate->IsNegated()) { |
| 547 | return "!(" + value + ')'; | 552 | return fmt::format("!({})", value); |
| 548 | } | 553 | } |
| 549 | return value; | 554 | return value; |
| 555 | } | ||
| 550 | 556 | ||
| 551 | } else if (const auto abuf = std::get_if<AbufNode>(node)) { | 557 | if (const auto abuf = std::get_if<AbufNode>(node)) { |
| 552 | UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry, | 558 | UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry, |
| 553 | "Physical attributes in geometry shaders are not implemented"); | 559 | "Physical attributes in geometry shaders are not implemented"); |
| 554 | if (abuf->IsPhysicalBuffer()) { | 560 | if (abuf->IsPhysicalBuffer()) { |
| 555 | return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))"; | 561 | return fmt::format("readPhysicalAttribute(ftou({}))", |
| 562 | Visit(abuf->GetPhysicalAddress())); | ||
| 556 | } | 563 | } |
| 557 | return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); | 564 | return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer()); |
| 565 | } | ||
| 558 | 566 | ||
| 559 | } else if (const auto cbuf = std::get_if<CbufNode>(node)) { | 567 | if (const auto cbuf = std::get_if<CbufNode>(node)) { |
| 560 | const Node offset = cbuf->GetOffset(); | 568 | const Node offset = cbuf->GetOffset(); |
| 561 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { | 569 | if (const auto immediate = std::get_if<ImmediateNode>(offset)) { |
| 562 | // Direct access | 570 | // Direct access |
| @@ -564,57 +572,63 @@ private: | |||
| 564 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); | 572 | ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access"); |
| 565 | return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), | 573 | return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()), |
| 566 | offset_imm / (4 * 4), (offset_imm / 4) % 4); | 574 | offset_imm / (4 * 4), (offset_imm / 4) % 4); |
| 575 | } | ||
| 567 | 576 | ||
| 568 | } else if (std::holds_alternative<OperationNode>(*offset)) { | 577 | if (std::holds_alternative<OperationNode>(*offset)) { |
| 569 | // Indirect access | 578 | // Indirect access |
| 570 | const std::string final_offset = code.GenerateTemporary(); | 579 | const std::string final_offset = code.GenerateTemporary(); |
| 571 | code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);"); | 580 | code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset)); |
| 572 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), | 581 | return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()), |
| 573 | final_offset, final_offset); | 582 | final_offset, final_offset); |
| 574 | |||
| 575 | } else { | ||
| 576 | UNREACHABLE_MSG("Unmanaged offset node type"); | ||
| 577 | } | 583 | } |
| 578 | 584 | ||
| 579 | } else if (const auto gmem = std::get_if<GmemNode>(node)) { | 585 | UNREACHABLE_MSG("Unmanaged offset node type"); |
| 586 | } | ||
| 587 | |||
| 588 | if (const auto gmem = std::get_if<GmemNode>(node)) { | ||
| 580 | const std::string real = Visit(gmem->GetRealAddress()); | 589 | const std::string real = Visit(gmem->GetRealAddress()); |
| 581 | const std::string base = Visit(gmem->GetBaseAddress()); | 590 | const std::string base = Visit(gmem->GetBaseAddress()); |
| 582 | const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; | 591 | const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); |
| 583 | return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); | 592 | return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); |
| 593 | } | ||
| 584 | 594 | ||
| 585 | } else if (const auto lmem = std::get_if<LmemNode>(node)) { | 595 | if (const auto lmem = std::get_if<LmemNode>(node)) { |
| 586 | return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); | 596 | return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); |
| 597 | } | ||
| 587 | 598 | ||
| 588 | } else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { | 599 | if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) { |
| 589 | return GetInternalFlag(internal_flag->GetFlag()); | 600 | return GetInternalFlag(internal_flag->GetFlag()); |
| 601 | } | ||
| 590 | 602 | ||
| 591 | } else if (const auto conditional = std::get_if<ConditionalNode>(node)) { | 603 | if (const auto conditional = std::get_if<ConditionalNode>(node)) { |
| 592 | // It's invalid to call conditional on nested nodes, use an operation instead | 604 | // It's invalid to call conditional on nested nodes, use an operation instead |
| 593 | code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {"); | 605 | code.AddLine("if ({}) {{", Visit(conditional->GetCondition())); |
| 594 | ++code.scope; | 606 | ++code.scope; |
| 595 | 607 | ||
| 596 | VisitBlock(conditional->GetCode()); | 608 | VisitBlock(conditional->GetCode()); |
| 597 | 609 | ||
| 598 | --code.scope; | 610 | --code.scope; |
| 599 | code.AddLine('}'); | 611 | code.AddLine("}}"); |
| 600 | return {}; | 612 | return {}; |
| 613 | } | ||
| 601 | 614 | ||
| 602 | } else if (const auto comment = std::get_if<CommentNode>(node)) { | 615 | if (const auto comment = std::get_if<CommentNode>(node)) { |
| 603 | return "// " + comment->GetText(); | 616 | return "// " + comment->GetText(); |
| 604 | } | 617 | } |
| 618 | |||
| 605 | UNREACHABLE(); | 619 | UNREACHABLE(); |
| 606 | return {}; | 620 | return {}; |
| 607 | } | 621 | } |
| 608 | 622 | ||
| 609 | std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) { | 623 | std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) { |
| 610 | const auto GeometryPass = [&](std::string name) { | 624 | const auto GeometryPass = [&](std::string_view name) { |
| 611 | if (stage == ShaderStage::Geometry && buffer) { | 625 | if (stage == ShaderStage::Geometry && buffer) { |
| 612 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games | 626 | // TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games |
| 613 | // set an 0x80000000 index for those and the shader fails to build. Find out why | 627 | // set an 0x80000000 index for those and the shader fails to build. Find out why |
| 614 | // this happens and what's its intent. | 628 | // this happens and what's its intent. |
| 615 | return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]"; | 629 | return fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer)); |
| 616 | } | 630 | } |
| 617 | return name; | 631 | return std::string(name); |
| 618 | }; | 632 | }; |
| 619 | 633 | ||
| 620 | switch (attribute) { | 634 | switch (attribute) { |
| @@ -677,7 +691,7 @@ private: | |||
| 677 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; | 691 | const std::string precise = stage != ShaderStage::Fragment ? "precise " : ""; |
| 678 | 692 | ||
| 679 | const std::string temporary = code.GenerateTemporary(); | 693 | const std::string temporary = code.GenerateTemporary(); |
| 680 | code.AddLine(precise + "float " + temporary + " = " + value + ';'); | 694 | code.AddLine("{}float {} = {};", precise, temporary, value); |
| 681 | return temporary; | 695 | return temporary; |
| 682 | } | 696 | } |
| 683 | 697 | ||
| @@ -691,7 +705,7 @@ private: | |||
| 691 | } | 705 | } |
| 692 | 706 | ||
| 693 | const std::string temporary = code.GenerateTemporary(); | 707 | const std::string temporary = code.GenerateTemporary(); |
| 694 | code.AddLine("float " + temporary + " = " + Visit(operand) + ';'); | 708 | code.AddLine("float {} = {};", temporary, Visit(operand)); |
| 695 | return temporary; | 709 | return temporary; |
| 696 | } | 710 | } |
| 697 | 711 | ||
| @@ -706,31 +720,32 @@ private: | |||
| 706 | case Type::Float: | 720 | case Type::Float: |
| 707 | return value; | 721 | return value; |
| 708 | case Type::Int: | 722 | case Type::Int: |
| 709 | return "ftoi(" + value + ')'; | 723 | return fmt::format("ftoi({})", value); |
| 710 | case Type::Uint: | 724 | case Type::Uint: |
| 711 | return "ftou(" + value + ')'; | 725 | return fmt::format("ftou({})", value); |
| 712 | case Type::HalfFloat: | 726 | case Type::HalfFloat: |
| 713 | return "toHalf2(" + value + ')'; | 727 | return fmt::format("toHalf2({})", value); |
| 714 | } | 728 | } |
| 715 | UNREACHABLE(); | 729 | UNREACHABLE(); |
| 716 | return value; | 730 | return value; |
| 717 | } | 731 | } |
| 718 | 732 | ||
| 719 | std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) { | 733 | std::string BitwiseCastResult(const std::string& value, Type type, |
| 734 | bool needs_parenthesis = false) { | ||
| 720 | switch (type) { | 735 | switch (type) { |
| 721 | case Type::Bool: | 736 | case Type::Bool: |
| 722 | case Type::Bool2: | 737 | case Type::Bool2: |
| 723 | case Type::Float: | 738 | case Type::Float: |
| 724 | if (needs_parenthesis) { | 739 | if (needs_parenthesis) { |
| 725 | return '(' + value + ')'; | 740 | return fmt::format("({})", value); |
| 726 | } | 741 | } |
| 727 | return value; | 742 | return value; |
| 728 | case Type::Int: | 743 | case Type::Int: |
| 729 | return "itof(" + value + ')'; | 744 | return fmt::format("itof({})", value); |
| 730 | case Type::Uint: | 745 | case Type::Uint: |
| 731 | return "utof(" + value + ')'; | 746 | return fmt::format("utof({})", value); |
| 732 | case Type::HalfFloat: | 747 | case Type::HalfFloat: |
| 733 | return "fromHalf2(" + value + ')'; | 748 | return fmt::format("fromHalf2({})", value); |
| 734 | } | 749 | } |
| 735 | UNREACHABLE(); | 750 | UNREACHABLE(); |
| 736 | return value; | 751 | return value; |
| @@ -738,27 +753,27 @@ private: | |||
| 738 | 753 | ||
| 739 | std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, | 754 | std::string GenerateUnary(Operation operation, const std::string& func, Type result_type, |
| 740 | Type type_a, bool needs_parenthesis = true) { | 755 | Type type_a, bool needs_parenthesis = true) { |
| 741 | return ApplyPrecise(operation, | 756 | const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a)); |
| 742 | BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')', | 757 | |
| 743 | result_type, needs_parenthesis)); | 758 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis)); |
| 744 | } | 759 | } |
| 745 | 760 | ||
| 746 | std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, | 761 | std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type, |
| 747 | Type type_a, Type type_b) { | 762 | Type type_a, Type type_b) { |
| 748 | const std::string op_a = VisitOperand(operation, 0, type_a); | 763 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 749 | const std::string op_b = VisitOperand(operation, 1, type_b); | 764 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 765 | const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b); | ||
| 750 | 766 | ||
| 751 | return ApplyPrecise( | 767 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 752 | operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type)); | ||
| 753 | } | 768 | } |
| 754 | 769 | ||
| 755 | std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, | 770 | std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type, |
| 756 | Type type_a, Type type_b) { | 771 | Type type_a, Type type_b) { |
| 757 | const std::string op_a = VisitOperand(operation, 0, type_a); | 772 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 758 | const std::string op_b = VisitOperand(operation, 1, type_b); | 773 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 774 | const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b); | ||
| 759 | 775 | ||
| 760 | return ApplyPrecise(operation, | 776 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 761 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type)); | ||
| 762 | } | 777 | } |
| 763 | 778 | ||
| 764 | std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, | 779 | std::string GenerateTernary(Operation operation, const std::string& func, Type result_type, |
| @@ -766,10 +781,9 @@ private: | |||
| 766 | const std::string op_a = VisitOperand(operation, 0, type_a); | 781 | const std::string op_a = VisitOperand(operation, 0, type_a); |
| 767 | const std::string op_b = VisitOperand(operation, 1, type_b); | 782 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 768 | const std::string op_c = VisitOperand(operation, 2, type_c); | 783 | const std::string op_c = VisitOperand(operation, 2, type_c); |
| 784 | const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c); | ||
| 769 | 785 | ||
| 770 | return ApplyPrecise( | 786 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 771 | operation, | ||
| 772 | BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type)); | ||
| 773 | } | 787 | } |
| 774 | 788 | ||
| 775 | std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, | 789 | std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type, |
| @@ -778,10 +792,9 @@ private: | |||
| 778 | const std::string op_b = VisitOperand(operation, 1, type_b); | 792 | const std::string op_b = VisitOperand(operation, 1, type_b); |
| 779 | const std::string op_c = VisitOperand(operation, 2, type_c); | 793 | const std::string op_c = VisitOperand(operation, 2, type_c); |
| 780 | const std::string op_d = VisitOperand(operation, 3, type_d); | 794 | const std::string op_d = VisitOperand(operation, 3, type_d); |
| 795 | const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d); | ||
| 781 | 796 | ||
| 782 | return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + | 797 | return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type)); |
| 783 | op_c + ", " + op_d + ')', | ||
| 784 | result_type)); | ||
| 785 | } | 798 | } |
| 786 | 799 | ||
| 787 | std::string GenerateTexture(Operation operation, const std::string& function_suffix, | 800 | std::string GenerateTexture(Operation operation, const std::string& function_suffix, |
| @@ -844,7 +857,7 @@ private: | |||
| 844 | // required to be constant) | 857 | // required to be constant) |
| 845 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 858 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 846 | } else { | 859 | } else { |
| 847 | expr += "ftoi(" + Visit(operand) + ')'; | 860 | expr += fmt::format("ftoi({})", Visit(operand)); |
| 848 | } | 861 | } |
| 849 | break; | 862 | break; |
| 850 | case Type::Float: | 863 | case Type::Float: |
| @@ -877,7 +890,7 @@ private: | |||
| 877 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 890 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 878 | } else if (device.HasVariableAoffi()) { | 891 | } else if (device.HasVariableAoffi()) { |
| 879 | // Avoid using variable AOFFI on unsupported devices. | 892 | // Avoid using variable AOFFI on unsupported devices. |
| 880 | expr += "ftoi(" + Visit(operand) + ')'; | 893 | expr += fmt::format("ftoi({})", Visit(operand)); |
| 881 | } else { | 894 | } else { |
| 882 | // Insert 0 on devices not supporting variable AOFFI. | 895 | // Insert 0 on devices not supporting variable AOFFI. |
| 883 | expr += '0'; | 896 | expr += '0'; |
| @@ -902,7 +915,6 @@ private: | |||
| 902 | return {}; | 915 | return {}; |
| 903 | } | 916 | } |
| 904 | target = GetRegister(gpr->GetIndex()); | 917 | target = GetRegister(gpr->GetIndex()); |
| 905 | |||
| 906 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { | 918 | } else if (const auto abuf = std::get_if<AbufNode>(dest)) { |
| 907 | UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); | 919 | UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer()); |
| 908 | 920 | ||
| @@ -913,9 +925,9 @@ private: | |||
| 913 | case Attribute::Index::PointSize: | 925 | case Attribute::Index::PointSize: |
| 914 | return "gl_PointSize"; | 926 | return "gl_PointSize"; |
| 915 | case Attribute::Index::ClipDistances0123: | 927 | case Attribute::Index::ClipDistances0123: |
| 916 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']'; | 928 | return fmt::format("gl_ClipDistance[{}]", abuf->GetElement()); |
| 917 | case Attribute::Index::ClipDistances4567: | 929 | case Attribute::Index::ClipDistances4567: |
| 918 | return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; | 930 | return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4); |
| 919 | default: | 931 | default: |
| 920 | if (IsGenericAttribute(attribute)) { | 932 | if (IsGenericAttribute(attribute)) { |
| 921 | return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); | 933 | return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); |
| @@ -925,21 +937,18 @@ private: | |||
| 925 | return "0"; | 937 | return "0"; |
| 926 | } | 938 | } |
| 927 | }(); | 939 | }(); |
| 928 | |||
| 929 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { | 940 | } else if (const auto lmem = std::get_if<LmemNode>(dest)) { |
| 930 | target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]"; | 941 | target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress())); |
| 931 | |||
| 932 | } else if (const auto gmem = std::get_if<GmemNode>(dest)) { | 942 | } else if (const auto gmem = std::get_if<GmemNode>(dest)) { |
| 933 | const std::string real = Visit(gmem->GetRealAddress()); | 943 | const std::string real = Visit(gmem->GetRealAddress()); |
| 934 | const std::string base = Visit(gmem->GetBaseAddress()); | 944 | const std::string base = Visit(gmem->GetBaseAddress()); |
| 935 | const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4"; | 945 | const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base); |
| 936 | target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); | 946 | target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset); |
| 937 | |||
| 938 | } else { | 947 | } else { |
| 939 | UNREACHABLE_MSG("Assign called without a proper target"); | 948 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 940 | } | 949 | } |
| 941 | 950 | ||
| 942 | code.AddLine(target + " = " + Visit(src) + ';'); | 951 | code.AddLine("{} = {};", target, Visit(src)); |
| 943 | return {}; | 952 | return {}; |
| 944 | } | 953 | } |
| 945 | 954 | ||
| @@ -992,8 +1001,9 @@ private: | |||
| 992 | const std::string condition = Visit(operation[0]); | 1001 | const std::string condition = Visit(operation[0]); |
| 993 | const std::string true_case = Visit(operation[1]); | 1002 | const std::string true_case = Visit(operation[1]); |
| 994 | const std::string false_case = Visit(operation[2]); | 1003 | const std::string false_case = Visit(operation[2]); |
| 995 | return ApplyPrecise(operation, | 1004 | const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case); |
| 996 | '(' + condition + " ? " + true_case + " : " + false_case + ')'); | 1005 | |
| 1006 | return ApplyPrecise(operation, op_str); | ||
| 997 | } | 1007 | } |
| 998 | 1008 | ||
| 999 | std::string FCos(Operation operation) { | 1009 | std::string FCos(Operation operation) { |
| @@ -1057,9 +1067,9 @@ private: | |||
| 1057 | std::string ILogicalShiftRight(Operation operation) { | 1067 | std::string ILogicalShiftRight(Operation operation) { |
| 1058 | const std::string op_a = VisitOperand(operation, 0, Type::Uint); | 1068 | const std::string op_a = VisitOperand(operation, 0, Type::Uint); |
| 1059 | const std::string op_b = VisitOperand(operation, 1, Type::Uint); | 1069 | const std::string op_b = VisitOperand(operation, 1, Type::Uint); |
| 1070 | const std::string op_str = fmt::format("int({} >> {})", op_a, op_b); | ||
| 1060 | 1071 | ||
| 1061 | return ApplyPrecise(operation, | 1072 | return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int)); |
| 1062 | BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int)); | ||
| 1063 | } | 1073 | } |
| 1064 | 1074 | ||
| 1065 | std::string IArithmeticShiftRight(Operation operation) { | 1075 | std::string IArithmeticShiftRight(Operation operation) { |
| @@ -1115,11 +1125,12 @@ private: | |||
| 1115 | } | 1125 | } |
| 1116 | 1126 | ||
| 1117 | std::string HNegate(Operation operation) { | 1127 | std::string HNegate(Operation operation) { |
| 1118 | const auto GetNegate = [&](std::size_t index) -> std::string { | 1128 | const auto GetNegate = [&](std::size_t index) { |
| 1119 | return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; | 1129 | return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1"; |
| 1120 | }; | 1130 | }; |
| 1121 | const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" + | 1131 | const std::string value = |
| 1122 | GetNegate(1) + ", " + GetNegate(2) + "))"; | 1132 | fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat), |
| 1133 | GetNegate(1), GetNegate(2)); | ||
| 1123 | return BitwiseCastResult(value, Type::HalfFloat); | 1134 | return BitwiseCastResult(value, Type::HalfFloat); |
| 1124 | } | 1135 | } |
| 1125 | 1136 | ||
| @@ -1127,7 +1138,8 @@ private: | |||
| 1127 | const std::string value = VisitOperand(operation, 0, Type::HalfFloat); | 1138 | const std::string value = VisitOperand(operation, 0, Type::HalfFloat); |
| 1128 | const std::string min = VisitOperand(operation, 1, Type::Float); | 1139 | const std::string min = VisitOperand(operation, 1, Type::Float); |
| 1129 | const std::string max = VisitOperand(operation, 2, Type::Float); | 1140 | const std::string max = VisitOperand(operation, 2, Type::Float); |
| 1130 | const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; | 1141 | const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max); |
| 1142 | |||
| 1131 | return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); | 1143 | return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); |
| 1132 | } | 1144 | } |
| 1133 | 1145 | ||
| @@ -1138,34 +1150,35 @@ private: | |||
| 1138 | case Tegra::Shader::HalfType::H0_H1: | 1150 | case Tegra::Shader::HalfType::H0_H1: |
| 1139 | return operand; | 1151 | return operand; |
| 1140 | case Tegra::Shader::HalfType::F32: | 1152 | case Tegra::Shader::HalfType::F32: |
| 1141 | return "vec2(fromHalf2(" + operand + "))"; | 1153 | return fmt::format("vec2(fromHalf2({}))", operand); |
| 1142 | case Tegra::Shader::HalfType::H0_H0: | 1154 | case Tegra::Shader::HalfType::H0_H0: |
| 1143 | return "vec2(" + operand + "[0])"; | 1155 | return fmt::format("vec2({}[0])", operand); |
| 1144 | case Tegra::Shader::HalfType::H1_H1: | 1156 | case Tegra::Shader::HalfType::H1_H1: |
| 1145 | return "vec2(" + operand + "[1])"; | 1157 | return fmt::format("vec2({}[1])", operand); |
| 1146 | } | 1158 | } |
| 1147 | UNREACHABLE(); | 1159 | UNREACHABLE(); |
| 1148 | return "0"; | 1160 | return "0"; |
| 1149 | }(); | 1161 | }(); |
| 1150 | return "fromHalf2(" + value + ')'; | 1162 | return fmt::format("fromHalf2({})", value); |
| 1151 | } | 1163 | } |
| 1152 | 1164 | ||
| 1153 | std::string HMergeF32(Operation operation) { | 1165 | std::string HMergeF32(Operation operation) { |
| 1154 | return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; | 1166 | return fmt::format("float(toHalf2({})[0])", Visit(operation[0])); |
| 1155 | } | 1167 | } |
| 1156 | 1168 | ||
| 1157 | std::string HMergeH0(Operation operation) { | 1169 | std::string HMergeH0(Operation operation) { |
| 1158 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + | 1170 | return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]), |
| 1159 | Visit(operation[0]) + ")[1]))"; | 1171 | Visit(operation[0])); |
| 1160 | } | 1172 | } |
| 1161 | 1173 | ||
| 1162 | std::string HMergeH1(Operation operation) { | 1174 | std::string HMergeH1(Operation operation) { |
| 1163 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" + | 1175 | return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]), |
| 1164 | Visit(operation[1]) + ")[1]))"; | 1176 | Visit(operation[1])); |
| 1165 | } | 1177 | } |
| 1166 | 1178 | ||
| 1167 | std::string HPack2(Operation operation) { | 1179 | std::string HPack2(Operation operation) { |
| 1168 | return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))"; | 1180 | return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]), |
| 1181 | Visit(operation[1])); | ||
| 1169 | } | 1182 | } |
| 1170 | 1183 | ||
| 1171 | template <Type type> | 1184 | template <Type type> |
| @@ -1223,7 +1236,7 @@ private: | |||
| 1223 | target = GetInternalFlag(flag->GetFlag()); | 1236 | target = GetInternalFlag(flag->GetFlag()); |
| 1224 | } | 1237 | } |
| 1225 | 1238 | ||
| 1226 | code.AddLine(target + " = " + Visit(src) + ';'); | 1239 | code.AddLine("{} = {};", target, Visit(src)); |
| 1227 | return {}; | 1240 | return {}; |
| 1228 | } | 1241 | } |
| 1229 | 1242 | ||
| @@ -1245,7 +1258,7 @@ private: | |||
| 1245 | 1258 | ||
| 1246 | std::string LogicalPick2(Operation operation) { | 1259 | std::string LogicalPick2(Operation operation) { |
| 1247 | const std::string pair = VisitOperand(operation, 0, Type::Bool2); | 1260 | const std::string pair = VisitOperand(operation, 0, Type::Bool2); |
| 1248 | return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']'; | 1261 | return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint)); |
| 1249 | } | 1262 | } |
| 1250 | 1263 | ||
| 1251 | std::string LogicalAll2(Operation operation) { | 1264 | std::string LogicalAll2(Operation operation) { |
| @@ -1257,15 +1270,15 @@ private: | |||
| 1257 | } | 1270 | } |
| 1258 | 1271 | ||
| 1259 | template <bool with_nan> | 1272 | template <bool with_nan> |
| 1260 | std::string GenerateHalfComparison(Operation operation, std::string compare_op) { | 1273 | std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) { |
| 1261 | std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, | 1274 | const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, |
| 1262 | Type::HalfFloat, Type::HalfFloat)}; | 1275 | Type::HalfFloat, Type::HalfFloat)}; |
| 1263 | if constexpr (!with_nan) { | 1276 | if constexpr (!with_nan) { |
| 1264 | return comparison; | 1277 | return comparison; |
| 1265 | } | 1278 | } |
| 1266 | return "halfFloatNanComparison(" + comparison + ", " + | 1279 | return fmt::format("halfFloatNanComparison({}, {}, {})", comparison, |
| 1267 | VisitOperand(operation, 0, Type::HalfFloat) + ", " + | 1280 | VisitOperand(operation, 0, Type::HalfFloat), |
| 1268 | VisitOperand(operation, 1, Type::HalfFloat) + ')'; | 1281 | VisitOperand(operation, 1, Type::HalfFloat)); |
| 1269 | } | 1282 | } |
| 1270 | 1283 | ||
| 1271 | template <bool with_nan> | 1284 | template <bool with_nan> |
| @@ -1342,12 +1355,12 @@ private: | |||
| 1342 | switch (meta->element) { | 1355 | switch (meta->element) { |
| 1343 | case 0: | 1356 | case 0: |
| 1344 | case 1: | 1357 | case 1: |
| 1345 | return "itof(int(textureSize(" + sampler + ", " + lod + ')' + | 1358 | return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod, |
| 1346 | GetSwizzle(meta->element) + "))"; | 1359 | GetSwizzle(meta->element)); |
| 1347 | case 2: | 1360 | case 2: |
| 1348 | return "0"; | 1361 | return "0"; |
| 1349 | case 3: | 1362 | case 3: |
| 1350 | return "itof(textureQueryLevels(" + sampler + "))"; | 1363 | return fmt::format("itof(textureQueryLevels({}))", sampler); |
| 1351 | } | 1364 | } |
| 1352 | UNREACHABLE(); | 1365 | UNREACHABLE(); |
| 1353 | return "0"; | 1366 | return "0"; |
| @@ -1358,8 +1371,9 @@ private: | |||
| 1358 | ASSERT(meta); | 1371 | ASSERT(meta); |
| 1359 | 1372 | ||
| 1360 | if (meta->element < 2) { | 1373 | if (meta->element < 2) { |
| 1361 | return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" + | 1374 | return fmt::format("itof(int(({} * vec2(256)){}))", |
| 1362 | GetSwizzle(meta->element) + "))"; | 1375 | GenerateTexture(operation, "QueryLod", {}), |
| 1376 | GetSwizzle(meta->element)); | ||
| 1363 | } | 1377 | } |
| 1364 | return "0"; | 1378 | return "0"; |
| 1365 | } | 1379 | } |
| @@ -1398,7 +1412,7 @@ private: | |||
| 1398 | const auto target = std::get_if<ImmediateNode>(operation[0]); | 1412 | const auto target = std::get_if<ImmediateNode>(operation[0]); |
| 1399 | UNIMPLEMENTED_IF(!target); | 1413 | UNIMPLEMENTED_IF(!target); |
| 1400 | 1414 | ||
| 1401 | code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue())); | 1415 | code.AddLine("jmp_to = 0x{:x}u;", target->GetValue()); |
| 1402 | code.AddLine("break;"); | 1416 | code.AddLine("break;"); |
| 1403 | return {}; | 1417 | return {}; |
| 1404 | } | 1418 | } |
| @@ -1407,7 +1421,7 @@ private: | |||
| 1407 | const auto target = std::get_if<ImmediateNode>(operation[0]); | 1421 | const auto target = std::get_if<ImmediateNode>(operation[0]); |
| 1408 | UNIMPLEMENTED_IF(!target); | 1422 | UNIMPLEMENTED_IF(!target); |
| 1409 | 1423 | ||
| 1410 | code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue())); | 1424 | code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue()); |
| 1411 | return {}; | 1425 | return {}; |
| 1412 | } | 1426 | } |
| 1413 | 1427 | ||
| @@ -1433,7 +1447,7 @@ private: | |||
| 1433 | 1447 | ||
| 1434 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); | 1448 | UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented"); |
| 1435 | 1449 | ||
| 1436 | code.AddLine("if (alpha_test[0] != 0) {"); | 1450 | code.AddLine("if (alpha_test[0] != 0) {{"); |
| 1437 | ++code.scope; | 1451 | ++code.scope; |
| 1438 | // We start on the register containing the alpha value in the first RT. | 1452 | // We start on the register containing the alpha value in the first RT. |
| 1439 | u32 current_reg = 3; | 1453 | u32 current_reg = 3; |
| @@ -1444,13 +1458,12 @@ private: | |||
| 1444 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || | 1458 | header.ps.IsColorComponentOutputEnabled(render_target, 1) || |
| 1445 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || | 1459 | header.ps.IsColorComponentOutputEnabled(render_target, 2) || |
| 1446 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { | 1460 | header.ps.IsColorComponentOutputEnabled(render_target, 3)) { |
| 1447 | code.AddLine( | 1461 | code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)); |
| 1448 | fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg))); | ||
| 1449 | current_reg += 4; | 1462 | current_reg += 4; |
| 1450 | } | 1463 | } |
| 1451 | } | 1464 | } |
| 1452 | --code.scope; | 1465 | --code.scope; |
| 1453 | code.AddLine('}'); | 1466 | code.AddLine("}}"); |
| 1454 | 1467 | ||
| 1455 | // Write the color outputs using the data in the shader registers, disabled | 1468 | // Write the color outputs using the data in the shader registers, disabled |
| 1456 | // rendertargets/components are skipped in the register assignment. | 1469 | // rendertargets/components are skipped in the register assignment. |
| @@ -1459,8 +1472,8 @@ private: | |||
| 1459 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1472 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| 1460 | for (u32 component = 0; component < 4; ++component) { | 1473 | for (u32 component = 0; component < 4; ++component) { |
| 1461 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { | 1474 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { |
| 1462 | code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, | 1475 | code.AddLine("FragColor{}[{}] = {};", render_target, component, |
| 1463 | SafeGetRegister(current_reg))); | 1476 | SafeGetRegister(current_reg)); |
| 1464 | ++current_reg; | 1477 | ++current_reg; |
| 1465 | } | 1478 | } |
| 1466 | } | 1479 | } |
| @@ -1469,7 +1482,7 @@ private: | |||
| 1469 | if (header.ps.omap.depth) { | 1482 | if (header.ps.omap.depth) { |
| 1470 | // The depth output is always 2 registers after the last color output, and current_reg | 1483 | // The depth output is always 2 registers after the last color output, and current_reg |
| 1471 | // already contains one past the last color register. | 1484 | // already contains one past the last color register. |
| 1472 | code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';'); | 1485 | code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1)); |
| 1473 | } | 1486 | } |
| 1474 | 1487 | ||
| 1475 | code.AddLine("return;"); | 1488 | code.AddLine("return;"); |
| @@ -1479,11 +1492,11 @@ private: | |||
| 1479 | std::string Discard(Operation operation) { | 1492 | std::string Discard(Operation operation) { |
| 1480 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain | 1493 | // Enclose "discard" in a conditional, so that GLSL compilation does not complain |
| 1481 | // about unexecuted instructions that may follow this. | 1494 | // about unexecuted instructions that may follow this. |
| 1482 | code.AddLine("if (true) {"); | 1495 | code.AddLine("if (true) {{"); |
| 1483 | ++code.scope; | 1496 | ++code.scope; |
| 1484 | code.AddLine("discard;"); | 1497 | code.AddLine("discard;"); |
| 1485 | --code.scope; | 1498 | --code.scope; |
| 1486 | code.AddLine("}"); | 1499 | code.AddLine("}}"); |
| 1487 | return {}; | 1500 | return {}; |
| 1488 | } | 1501 | } |
| 1489 | 1502 | ||
| @@ -1697,7 +1710,7 @@ private: | |||
| 1697 | const auto index = static_cast<u32>(flag); | 1710 | const auto index = static_cast<u32>(flag); |
| 1698 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); | 1711 | ASSERT(index < static_cast<u32>(InternalFlag::Amount)); |
| 1699 | 1712 | ||
| 1700 | return std::string(InternalFlagNames[index]) + '_' + suffix; | 1713 | return fmt::format("{}_{}", InternalFlagNames[index], suffix); |
| 1701 | } | 1714 | } |
| 1702 | 1715 | ||
| 1703 | std::string GetSampler(const Sampler& sampler) const { | 1716 | std::string GetSampler(const Sampler& sampler) const { |
| @@ -1705,7 +1718,7 @@ private: | |||
| 1705 | } | 1718 | } |
| 1706 | 1719 | ||
| 1707 | std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { | 1720 | std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const { |
| 1708 | return name + '_' + std::to_string(index) + '_' + suffix; | 1721 | return fmt::format("{}_{}_{}", name, index, suffix); |
| 1709 | } | 1722 | } |
| 1710 | 1723 | ||
| 1711 | u32 GetNumPhysicalInputAttributes() const { | 1724 | u32 GetNumPhysicalInputAttributes() const { |
| @@ -1733,24 +1746,25 @@ private: | |||
| 1733 | } // Anonymous namespace | 1746 | } // Anonymous namespace |
| 1734 | 1747 | ||
| 1735 | std::string GetCommonDeclarations() { | 1748 | std::string GetCommonDeclarations() { |
| 1736 | const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS); | 1749 | return fmt::format( |
| 1737 | return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" + | 1750 | "#define MAX_CONSTBUFFER_ELEMENTS {}\n" |
| 1738 | "#define ftoi floatBitsToInt\n" | 1751 | "#define ftoi floatBitsToInt\n" |
| 1739 | "#define ftou floatBitsToUint\n" | 1752 | "#define ftou floatBitsToUint\n" |
| 1740 | "#define itof intBitsToFloat\n" | 1753 | "#define itof intBitsToFloat\n" |
| 1741 | "#define utof uintBitsToFloat\n\n" | 1754 | "#define utof uintBitsToFloat\n\n" |
| 1742 | "float fromHalf2(vec2 pair) {\n" | 1755 | "float fromHalf2(vec2 pair) {{\n" |
| 1743 | " return utof(packHalf2x16(pair));\n" | 1756 | " return utof(packHalf2x16(pair));\n" |
| 1744 | "}\n\n" | 1757 | "}}\n\n" |
| 1745 | "vec2 toHalf2(float value) {\n" | 1758 | "vec2 toHalf2(float value) {{\n" |
| 1746 | " return unpackHalf2x16(ftou(value));\n" | 1759 | " return unpackHalf2x16(ftou(value));\n" |
| 1747 | "}\n\n" | 1760 | "}}\n\n" |
| 1748 | "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" | 1761 | "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n" |
| 1749 | " bvec2 is_nan1 = isnan(pair1);\n" | 1762 | " bvec2 is_nan1 = isnan(pair1);\n" |
| 1750 | " bvec2 is_nan2 = isnan(pair2);\n" | 1763 | " bvec2 is_nan2 = isnan(pair2);\n" |
| 1751 | " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " | 1764 | " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " |
| 1752 | "is_nan2.y);\n" | 1765 | "is_nan2.y);\n" |
| 1753 | "}\n"; | 1766 | "}}\n", |
| 1767 | MAX_CONSTBUFFER_ELEMENTS); | ||
| 1754 | } | 1768 | } |
| 1755 | 1769 | ||
| 1756 | ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, | 1770 | ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, |