diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/handle_table.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/handle_table.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.h | 9 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 29 | ||||
| -rw-r--r-- | src/video_core/command_processor.cpp | 142 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 436 |
8 files changed, 220 insertions, 412 deletions
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 1bf79b692..c8acde5b1 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -42,9 +42,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { | |||
| 42 | u16 generation = next_generation++; | 42 | u16 generation = next_generation++; |
| 43 | 43 | ||
| 44 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. | 44 | // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. |
| 45 | // CTR-OS doesn't use generation 0, so skip straight to 1. | 45 | // Horizon OS uses zero to represent an invalid handle, so skip to 1. |
| 46 | if (next_generation >= (1 << 15)) | 46 | if (next_generation >= (1 << 15)) { |
| 47 | next_generation = 1; | 47 | next_generation = 1; |
| 48 | } | ||
| 48 | 49 | ||
| 49 | generations[slot] = generation; | 50 | generations[slot] = generation; |
| 50 | objects[slot] = std::move(obj); | 51 | objects[slot] = std::move(obj); |
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index e3f3e3fb8..6b7927fd8 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | 14 | ||
| 15 | enum KernelHandle : Handle { | 15 | enum KernelHandle : Handle { |
| 16 | InvalidHandle = 0, | ||
| 16 | CurrentThread = 0xFFFF8000, | 17 | CurrentThread = 0xFFFF8000, |
| 17 | CurrentProcess = 0xFFFF8001, | 18 | CurrentProcess = 0xFFFF8001, |
| 18 | }; | 19 | }; |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 7ca538401..4ecb8c926 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -44,6 +44,10 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { | |||
| 44 | return process; | 44 | return process; |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | SharedPtr<ResourceLimit> Process::GetResourceLimit() const { | ||
| 48 | return resource_limit; | ||
| 49 | } | ||
| 50 | |||
| 47 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | 51 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { |
| 48 | program_id = metadata.GetTitleID(); | 52 | program_id = metadata.GetTitleID(); |
| 49 | is_64bit_process = metadata.Is64BitProgram(); | 53 | is_64bit_process = metadata.Is64BitProgram(); |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index ada845c7f..49345aa66 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -171,14 +171,7 @@ public: | |||
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | /// Gets the resource limit descriptor for this process | 173 | /// Gets the resource limit descriptor for this process |
| 174 | ResourceLimit& GetResourceLimit() { | 174 | SharedPtr<ResourceLimit> GetResourceLimit() const; |
| 175 | return *resource_limit; | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Gets the resource limit descriptor for this process | ||
| 179 | const ResourceLimit& GetResourceLimit() const { | ||
| 180 | return *resource_limit; | ||
| 181 | } | ||
| 182 | 175 | ||
| 183 | /// Gets the default CPU ID for this process | 176 | /// Gets the default CPU ID for this process |
| 184 | u8 GetDefaultProcessorID() const { | 177 | u8 GetDefaultProcessorID() const { |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index d2d893992..812b32005 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -663,7 +663,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 663 | TotalMemoryUsage = 6, | 663 | TotalMemoryUsage = 6, |
| 664 | TotalHeapUsage = 7, | 664 | TotalHeapUsage = 7, |
| 665 | IsCurrentProcessBeingDebugged = 8, | 665 | IsCurrentProcessBeingDebugged = 8, |
| 666 | ResourceHandleLimit = 9, | 666 | RegisterResourceLimit = 9, |
| 667 | IdleTickCount = 10, | 667 | IdleTickCount = 10, |
| 668 | RandomEntropy = 11, | 668 | RandomEntropy = 11, |
| 669 | PerformanceCounter = 0xF0000002, | 669 | PerformanceCounter = 0xF0000002, |
| @@ -787,6 +787,33 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 787 | *result = 0; | 787 | *result = 0; |
| 788 | return RESULT_SUCCESS; | 788 | return RESULT_SUCCESS; |
| 789 | 789 | ||
| 790 | case GetInfoType::RegisterResourceLimit: { | ||
| 791 | if (handle != 0) { | ||
| 792 | return ERR_INVALID_HANDLE; | ||
| 793 | } | ||
| 794 | |||
| 795 | if (info_sub_id != 0) { | ||
| 796 | return ERR_INVALID_COMBINATION; | ||
| 797 | } | ||
| 798 | |||
| 799 | Process* const current_process = Core::CurrentProcess(); | ||
| 800 | HandleTable& handle_table = current_process->GetHandleTable(); | ||
| 801 | const auto resource_limit = current_process->GetResourceLimit(); | ||
| 802 | if (!resource_limit) { | ||
| 803 | *result = KernelHandle::InvalidHandle; | ||
| 804 | // Yes, the kernel considers this a successful operation. | ||
| 805 | return RESULT_SUCCESS; | ||
| 806 | } | ||
| 807 | |||
| 808 | const auto table_result = handle_table.Create(resource_limit); | ||
| 809 | if (table_result.Failed()) { | ||
| 810 | return table_result.Code(); | ||
| 811 | } | ||
| 812 | |||
| 813 | *result = *table_result; | ||
| 814 | return RESULT_SUCCESS; | ||
| 815 | } | ||
| 816 | |||
| 790 | case GetInfoType::RandomEntropy: | 817 | case GetInfoType::RandomEntropy: |
| 791 | if (handle != 0) { | 818 | if (handle != 0) { |
| 792 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", | 819 | LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp deleted file mode 100644 index 8b9c548cc..000000000 --- a/src/video_core/command_processor.cpp +++ /dev/null | |||
| @@ -1,142 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstddef> | ||
| 7 | #include <memory> | ||
| 8 | #include <utility> | ||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/logging/log.h" | ||
| 11 | #include "common/microprofile.h" | ||
| 12 | #include "common/vector_math.h" | ||
| 13 | #include "core/memory.h" | ||
| 14 | #include "core/tracer/recorder.h" | ||
| 15 | #include "video_core/command_processor.h" | ||
| 16 | #include "video_core/engines/fermi_2d.h" | ||
| 17 | #include "video_core/engines/kepler_memory.h" | ||
| 18 | #include "video_core/engines/maxwell_3d.h" | ||
| 19 | #include "video_core/engines/maxwell_compute.h" | ||
| 20 | #include "video_core/engines/maxwell_dma.h" | ||
| 21 | #include "video_core/gpu.h" | ||
| 22 | #include "video_core/renderer_base.h" | ||
| 23 | #include "video_core/video_core.h" | ||
| 24 | |||
| 25 | namespace Tegra { | ||
| 26 | |||
| 27 | enum class BufferMethods { | ||
| 28 | BindObject = 0, | ||
| 29 | CountBufferMethods = 0x40, | ||
| 30 | }; | ||
| 31 | |||
| 32 | MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB(128, 128, 192)); | ||
| 33 | |||
| 34 | void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { | ||
| 35 | MICROPROFILE_SCOPE(ProcessCommandLists); | ||
| 36 | |||
| 37 | // On entering GPU code, assume all memory may be touched by the ARM core. | ||
| 38 | maxwell_3d->dirty_flags.OnMemoryWrite(); | ||
| 39 | |||
| 40 | auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) { | ||
| 41 | LOG_TRACE(HW_GPU, | ||
| 42 | "Processing method {:08X} on subchannel {} value " | ||
| 43 | "{:08X} remaining params {}", | ||
| 44 | method, subchannel, value, remaining_params); | ||
| 45 | |||
| 46 | ASSERT(subchannel < bound_engines.size()); | ||
| 47 | |||
| 48 | if (method == static_cast<u32>(BufferMethods::BindObject)) { | ||
| 49 | // Bind the current subchannel to the desired engine id. | ||
| 50 | LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value); | ||
| 51 | bound_engines[subchannel] = static_cast<EngineID>(value); | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | |||
| 55 | if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) { | ||
| 56 | // TODO(Subv): Research and implement these methods. | ||
| 57 | LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | const EngineID engine = bound_engines[subchannel]; | ||
| 62 | |||
| 63 | switch (engine) { | ||
| 64 | case EngineID::FERMI_TWOD_A: | ||
| 65 | fermi_2d->WriteReg(method, value); | ||
| 66 | break; | ||
| 67 | case EngineID::MAXWELL_B: | ||
| 68 | maxwell_3d->WriteReg(method, value, remaining_params); | ||
| 69 | break; | ||
| 70 | case EngineID::MAXWELL_COMPUTE_B: | ||
| 71 | maxwell_compute->WriteReg(method, value); | ||
| 72 | break; | ||
| 73 | case EngineID::MAXWELL_DMA_COPY_A: | ||
| 74 | maxwell_dma->WriteReg(method, value); | ||
| 75 | break; | ||
| 76 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | ||
| 77 | kepler_memory->WriteReg(method, value); | ||
| 78 | break; | ||
| 79 | default: | ||
| 80 | UNIMPLEMENTED_MSG("Unimplemented engine"); | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | |||
| 84 | for (auto entry : commands) { | ||
| 85 | Tegra::GPUVAddr address = entry.Address(); | ||
| 86 | u32 size = entry.sz; | ||
| 87 | const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); | ||
| 88 | VAddr current_addr = *head_address; | ||
| 89 | while (current_addr < *head_address + size * sizeof(CommandHeader)) { | ||
| 90 | const CommandHeader header = {Memory::Read32(current_addr)}; | ||
| 91 | current_addr += sizeof(u32); | ||
| 92 | |||
| 93 | switch (header.mode.Value()) { | ||
| 94 | case SubmissionMode::IncreasingOld: | ||
| 95 | case SubmissionMode::Increasing: { | ||
| 96 | // Increase the method value with each argument. | ||
| 97 | for (unsigned i = 0; i < header.arg_count; ++i) { | ||
| 98 | WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr), | ||
| 99 | header.arg_count - i - 1); | ||
| 100 | current_addr += sizeof(u32); | ||
| 101 | } | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | case SubmissionMode::NonIncreasingOld: | ||
| 105 | case SubmissionMode::NonIncreasing: { | ||
| 106 | // Use the same method value for all arguments. | ||
| 107 | for (unsigned i = 0; i < header.arg_count; ++i) { | ||
| 108 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | ||
| 109 | header.arg_count - i - 1); | ||
| 110 | current_addr += sizeof(u32); | ||
| 111 | } | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | case SubmissionMode::IncreaseOnce: { | ||
| 115 | ASSERT(header.arg_count.Value() >= 1); | ||
| 116 | |||
| 117 | // Use the original method for the first argument and then the next method for all | ||
| 118 | // other arguments. | ||
| 119 | WriteReg(header.method, header.subchannel, Memory::Read32(current_addr), | ||
| 120 | header.arg_count - 1); | ||
| 121 | current_addr += sizeof(u32); | ||
| 122 | |||
| 123 | for (unsigned i = 1; i < header.arg_count; ++i) { | ||
| 124 | WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr), | ||
| 125 | header.arg_count - i - 1); | ||
| 126 | current_addr += sizeof(u32); | ||
| 127 | } | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | case SubmissionMode::Inline: { | ||
| 131 | // The register value is stored in the bits 16-28 as an immediate | ||
| 132 | WriteReg(header.method, header.subchannel, header.inline_data, 0); | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | default: | ||
| 136 | UNIMPLEMENTED(); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | } // namespace Tegra | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index fd1242333..88c45a423 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -141,6 +141,12 @@ void GPU::CallMethod(const MethodCall& method_call) { | |||
| 141 | return; | 141 | return; |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | if (method_call.method < static_cast<u32>(BufferMethods::CountBufferMethods)) { | ||
| 145 | // TODO(Subv): Research and implement these methods. | ||
| 146 | LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented"); | ||
| 147 | return; | ||
| 148 | } | ||
| 149 | |||
| 144 | const EngineID engine = bound_engines[method_call.subchannel]; | 150 | const EngineID engine = bound_engines[method_call.subchannel]; |
| 145 | 151 | ||
| 146 | switch (engine) { | 152 | switch (engine) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index d235bfcd4..8d68156bf 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -1515,6 +1515,161 @@ private: | |||
| 1515 | } | 1515 | } |
| 1516 | } | 1516 | } |
| 1517 | 1517 | ||
| 1518 | std::pair<size_t, std::string> ValidateAndGetCoordinateElement( | ||
| 1519 | const Tegra::Shader::TextureType texture_type, const bool depth_compare, | ||
| 1520 | const bool is_array, const bool lod_bias_enabled, size_t max_coords, size_t max_inputs) { | ||
| 1521 | const size_t coord_count = TextureCoordinates(texture_type); | ||
| 1522 | |||
| 1523 | size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0); | ||
| 1524 | const size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0); | ||
| 1525 | if (total_coord_count > max_coords || total_reg_count > max_inputs) { | ||
| 1526 | UNIMPLEMENTED_MSG("Unsupported Texture operation"); | ||
| 1527 | total_coord_count = std::min(total_coord_count, max_coords); | ||
| 1528 | } | ||
| 1529 | // 1D.DC opengl is using a vec3 but 2nd component is ignored later. | ||
| 1530 | total_coord_count += | ||
| 1531 | (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) | ||
| 1532 | ? 1 | ||
| 1533 | : 0; | ||
| 1534 | |||
| 1535 | constexpr std::array<const char*, 5> coord_container{ | ||
| 1536 | {"", "float coord = (", "vec2 coord = vec2(", "vec3 coord = vec3(", | ||
| 1537 | "vec4 coord = vec4("}}; | ||
| 1538 | |||
| 1539 | return std::pair<size_t, std::string>(coord_count, coord_container[total_coord_count]); | ||
| 1540 | } | ||
| 1541 | |||
| 1542 | std::string GetTextureCode(const Tegra::Shader::Instruction& instr, | ||
| 1543 | const Tegra::Shader::TextureType texture_type, | ||
| 1544 | const Tegra::Shader::TextureProcessMode process_mode, | ||
| 1545 | const bool depth_compare, const bool is_array, | ||
| 1546 | const size_t bias_offset) { | ||
| 1547 | |||
| 1548 | if ((texture_type == Tegra::Shader::TextureType::Texture3D && | ||
| 1549 | (is_array || depth_compare)) || | ||
| 1550 | (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && | ||
| 1551 | depth_compare)) { | ||
| 1552 | UNIMPLEMENTED_MSG("This method is not supported."); | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | const std::string sampler = | ||
| 1556 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); | ||
| 1557 | |||
| 1558 | const bool lod_needed = process_mode == Tegra::Shader::TextureProcessMode::LZ || | ||
| 1559 | process_mode == Tegra::Shader::TextureProcessMode::LL || | ||
| 1560 | process_mode == Tegra::Shader::TextureProcessMode::LLA; | ||
| 1561 | |||
| 1562 | const bool gl_lod_supported = !( | ||
| 1563 | (texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) || | ||
| 1564 | (texture_type == Tegra::Shader::TextureType::TextureCube && !is_array && | ||
| 1565 | depth_compare)); | ||
| 1566 | |||
| 1567 | const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture("; | ||
| 1568 | std::string texture = read_method + sampler + ", coord"; | ||
| 1569 | |||
| 1570 | if (process_mode != Tegra::Shader::TextureProcessMode::None) { | ||
| 1571 | if (process_mode == Tegra::Shader::TextureProcessMode::LZ) { | ||
| 1572 | if (gl_lod_supported) { | ||
| 1573 | texture += ", 0"; | ||
| 1574 | } else { | ||
| 1575 | // Lod 0 is emulated by a big negative bias | ||
| 1576 | // in scenarios that are not supported by glsl | ||
| 1577 | texture += ", -1000"; | ||
| 1578 | } | ||
| 1579 | } else { | ||
| 1580 | // If present, lod or bias are always stored in the register indexed by the | ||
| 1581 | // gpr20 | ||
| 1582 | // field with an offset depending on the usage of the other registers | ||
| 1583 | texture += ',' + regs.GetRegisterAsFloat(instr.gpr20.Value() + bias_offset); | ||
| 1584 | } | ||
| 1585 | } | ||
| 1586 | texture += ")"; | ||
| 1587 | return texture; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | std::pair<std::string, std::string> GetTEXCode( | ||
| 1591 | const Instruction& instr, const Tegra::Shader::TextureType texture_type, | ||
| 1592 | const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, | ||
| 1593 | const bool is_array) { | ||
| 1594 | const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && | ||
| 1595 | process_mode != Tegra::Shader::TextureProcessMode::LZ); | ||
| 1596 | |||
| 1597 | const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( | ||
| 1598 | texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); | ||
| 1599 | // If enabled arrays index is always stored in the gpr8 field | ||
| 1600 | const u64 array_register = instr.gpr8.Value(); | ||
| 1601 | // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used | ||
| 1602 | const u64 coord_register = array_register + (is_array ? 1 : 0); | ||
| 1603 | |||
| 1604 | std::string coord = coord_dcl; | ||
| 1605 | for (size_t i = 0; i < coord_count;) { | ||
| 1606 | coord += regs.GetRegisterAsFloat(coord_register + i); | ||
| 1607 | ++i; | ||
| 1608 | if (i != coord_count) { | ||
| 1609 | coord += ','; | ||
| 1610 | } | ||
| 1611 | } | ||
| 1612 | // 1D.DC in opengl the 2nd component is ignored. | ||
| 1613 | if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) { | ||
| 1614 | coord += ",0.0"; | ||
| 1615 | } | ||
| 1616 | if (depth_compare) { | ||
| 1617 | // Depth is always stored in the register signaled by gpr20 | ||
| 1618 | // or in the next register if lod or bias are used | ||
| 1619 | const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); | ||
| 1620 | coord += ',' + regs.GetRegisterAsFloat(depth_register); | ||
| 1621 | } | ||
| 1622 | if (is_array) { | ||
| 1623 | coord += ',' + regs.GetRegisterAsInteger(array_register); | ||
| 1624 | } | ||
| 1625 | coord += ");"; | ||
| 1626 | return std::make_pair( | ||
| 1627 | coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0)); | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | std::pair<std::string, std::string> GetTEXSCode( | ||
| 1631 | const Instruction& instr, const Tegra::Shader::TextureType texture_type, | ||
| 1632 | const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare, | ||
| 1633 | const bool is_array) { | ||
| 1634 | const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None && | ||
| 1635 | process_mode != Tegra::Shader::TextureProcessMode::LZ); | ||
| 1636 | |||
| 1637 | const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement( | ||
| 1638 | texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4); | ||
| 1639 | // If enabled arrays index is always stored in the gpr8 field | ||
| 1640 | const u64 array_register = instr.gpr8.Value(); | ||
| 1641 | // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used | ||
| 1642 | const u64 coord_register = array_register + (is_array ? 1 : 0); | ||
| 1643 | const u64 last_coord_register = | ||
| 1644 | (is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2)) | ||
| 1645 | ? static_cast<u64>(instr.gpr20.Value()) | ||
| 1646 | : coord_register + 1; | ||
| 1647 | |||
| 1648 | std::string coord = coord_dcl; | ||
| 1649 | for (size_t i = 0; i < coord_count; ++i) { | ||
| 1650 | const bool last = (i == (coord_count - 1)) && (coord_count > 1); | ||
| 1651 | coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); | ||
| 1652 | if (!last) { | ||
| 1653 | coord += ','; | ||
| 1654 | } | ||
| 1655 | } | ||
| 1656 | |||
| 1657 | if (depth_compare) { | ||
| 1658 | // Depth is always stored in the register signaled by gpr20 | ||
| 1659 | // or in the next register if lod or bias are used | ||
| 1660 | const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0); | ||
| 1661 | coord += ',' + regs.GetRegisterAsFloat(depth_register); | ||
| 1662 | } | ||
| 1663 | if (is_array) { | ||
| 1664 | coord += ',' + regs.GetRegisterAsInteger(array_register); | ||
| 1665 | } | ||
| 1666 | coord += ");"; | ||
| 1667 | |||
| 1668 | return std::make_pair(coord, | ||
| 1669 | GetTextureCode(instr, texture_type, process_mode, depth_compare, | ||
| 1670 | is_array, (coord_count > 2 ? 1 : 0))); | ||
| 1671 | } | ||
| 1672 | |||
| 1518 | /** | 1673 | /** |
| 1519 | * Compiles a single instruction from Tegra to GLSL. | 1674 | * Compiles a single instruction from Tegra to GLSL. |
| 1520 | * @param offset the offset of the Tegra shader instruction. | 1675 | * @param offset the offset of the Tegra shader instruction. |
| @@ -2574,168 +2729,32 @@ private: | |||
| 2574 | case OpCode::Id::TEX: { | 2729 | case OpCode::Id::TEX: { |
| 2575 | Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; | 2730 | Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; |
| 2576 | const bool is_array = instr.tex.array != 0; | 2731 | const bool is_array = instr.tex.array != 0; |
| 2577 | 2732 | const bool depth_compare = | |
| 2733 | instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | ||
| 2734 | const auto process_mode = instr.tex.GetTextureProcessMode(); | ||
| 2578 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2735 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2579 | "NODEP is not implemented"); | 2736 | "NODEP is not implemented"); |
| 2580 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2737 | UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), |
| 2581 | "AOFFI is not implemented"); | 2738 | "AOFFI is not implemented"); |
| 2582 | 2739 | ||
| 2583 | const bool depth_compare = | 2740 | const auto [coord, texture] = |
| 2584 | instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2741 | GetTEXCode(instr, texture_type, process_mode, depth_compare, is_array); |
| 2585 | u32 num_coordinates = TextureCoordinates(texture_type); | ||
| 2586 | u32 start_index = 0; | ||
| 2587 | std::string array_elem; | ||
| 2588 | if (is_array) { | ||
| 2589 | array_elem = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2590 | start_index = 1; | ||
| 2591 | } | ||
| 2592 | const auto process_mode = instr.tex.GetTextureProcessMode(); | ||
| 2593 | u32 start_index_b = 0; | ||
| 2594 | std::string lod_value; | ||
| 2595 | if (process_mode != Tegra::Shader::TextureProcessMode::LZ && | ||
| 2596 | process_mode != Tegra::Shader::TextureProcessMode::None) { | ||
| 2597 | start_index_b = 1; | ||
| 2598 | lod_value = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2599 | } | ||
| 2600 | |||
| 2601 | std::string depth_value; | ||
| 2602 | if (depth_compare) { | ||
| 2603 | depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b); | ||
| 2604 | } | ||
| 2605 | |||
| 2606 | bool depth_compare_extra = false; | ||
| 2607 | 2742 | ||
| 2608 | const auto scope = shader.Scope(); | 2743 | const auto scope = shader.Scope(); |
| 2609 | 2744 | shader.AddLine(coord); | |
| 2610 | switch (num_coordinates) { | ||
| 2611 | case 1: { | ||
| 2612 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); | ||
| 2613 | if (is_array) { | ||
| 2614 | if (depth_compare) { | ||
| 2615 | shader.AddLine("vec3 coords = vec3(" + x + ", " + depth_value + ", " + | ||
| 2616 | array_elem + ");"); | ||
| 2617 | } else { | ||
| 2618 | shader.AddLine("vec2 coords = vec2(" + x + ", " + array_elem + ");"); | ||
| 2619 | } | ||
| 2620 | } else { | ||
| 2621 | if (depth_compare) { | ||
| 2622 | shader.AddLine("vec2 coords = vec2(" + x + ", " + depth_value + ");"); | ||
| 2623 | } else { | ||
| 2624 | shader.AddLine("float coords = " + x + ';'); | ||
| 2625 | } | ||
| 2626 | } | ||
| 2627 | break; | ||
| 2628 | } | ||
| 2629 | case 2: { | ||
| 2630 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); | ||
| 2631 | const std::string y = | ||
| 2632 | regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); | ||
| 2633 | if (is_array) { | ||
| 2634 | if (depth_compare) { | ||
| 2635 | shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + | ||
| 2636 | depth_value + ", " + array_elem + ");"); | ||
| 2637 | } else { | ||
| 2638 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + | ||
| 2639 | array_elem + ");"); | ||
| 2640 | } | ||
| 2641 | } else { | ||
| 2642 | if (depth_compare) { | ||
| 2643 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + | ||
| 2644 | depth_value + ");"); | ||
| 2645 | } else { | ||
| 2646 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2647 | } | ||
| 2648 | } | ||
| 2649 | break; | ||
| 2650 | } | ||
| 2651 | case 3: { | ||
| 2652 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index); | ||
| 2653 | const std::string y = | ||
| 2654 | regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1); | ||
| 2655 | const std::string z = | ||
| 2656 | regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2); | ||
| 2657 | if (is_array) { | ||
| 2658 | depth_compare_extra = depth_compare; | ||
| 2659 | shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + | ||
| 2660 | array_elem + ");"); | ||
| 2661 | } else { | ||
| 2662 | if (depth_compare) { | ||
| 2663 | shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + | ||
| 2664 | depth_value + ");"); | ||
| 2665 | } else { | ||
| 2666 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); | ||
| 2667 | } | ||
| 2668 | } | ||
| 2669 | break; | ||
| 2670 | } | ||
| 2671 | default: | ||
| 2672 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", | ||
| 2673 | static_cast<u32>(num_coordinates)); | ||
| 2674 | |||
| 2675 | // Fallback to interpreting as a 2D texture for now | ||
| 2676 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2677 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2678 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2679 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 2680 | } | ||
| 2681 | |||
| 2682 | const std::string sampler = | ||
| 2683 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); | ||
| 2684 | // Add an extra scope and declare the texture coords inside to prevent | ||
| 2685 | // overwriting them in case they are used as outputs of the texs instruction. | ||
| 2686 | |||
| 2687 | const std::string texture = [&]() { | ||
| 2688 | switch (instr.tex.GetTextureProcessMode()) { | ||
| 2689 | case Tegra::Shader::TextureProcessMode::None: | ||
| 2690 | if (depth_compare_extra) { | ||
| 2691 | return "texture(" + sampler + ", coords, " + depth_value + ')'; | ||
| 2692 | } | ||
| 2693 | return "texture(" + sampler + ", coords)"; | ||
| 2694 | case Tegra::Shader::TextureProcessMode::LZ: | ||
| 2695 | if (depth_compare_extra) { | ||
| 2696 | return "texture(" + sampler + ", coords, " + depth_value + ')'; | ||
| 2697 | } | ||
| 2698 | return "textureLod(" + sampler + ", coords, 0.0)"; | ||
| 2699 | case Tegra::Shader::TextureProcessMode::LB: | ||
| 2700 | case Tegra::Shader::TextureProcessMode::LBA: | ||
| 2701 | // TODO: Figure if A suffix changes the equation at all. | ||
| 2702 | if (depth_compare_extra) { | ||
| 2703 | LOG_WARNING( | ||
| 2704 | HW_GPU, | ||
| 2705 | "OpenGL Limitation: can't set bias value along depth compare"); | ||
| 2706 | return "texture(" + sampler + ", coords, " + depth_value + ')'; | ||
| 2707 | } | ||
| 2708 | return "texture(" + sampler + ", coords, " + lod_value + ')'; | ||
| 2709 | case Tegra::Shader::TextureProcessMode::LL: | ||
| 2710 | case Tegra::Shader::TextureProcessMode::LLA: | ||
| 2711 | // TODO: Figure if A suffix changes the equation at all. | ||
| 2712 | if (depth_compare_extra) { | ||
| 2713 | LOG_WARNING( | ||
| 2714 | HW_GPU, | ||
| 2715 | "OpenGL Limitation: can't set lod value along depth compare"); | ||
| 2716 | return "texture(" + sampler + ", coords, " + depth_value + ')'; | ||
| 2717 | } | ||
| 2718 | return "textureLod(" + sampler + ", coords, " + lod_value + ')'; | ||
| 2719 | default: | ||
| 2720 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", | ||
| 2721 | static_cast<u32>(instr.tex.GetTextureProcessMode())); | ||
| 2722 | if (depth_compare_extra) { | ||
| 2723 | return "texture(" + sampler + ", coords, " + depth_value + ')'; | ||
| 2724 | } | ||
| 2725 | return "texture(" + sampler + ", coords)"; | ||
| 2726 | } | ||
| 2727 | }(); | ||
| 2728 | 2745 | ||
| 2729 | if (depth_compare) { | 2746 | if (depth_compare) { |
| 2730 | regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); | 2747 | regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); |
| 2731 | } else { | 2748 | } else { |
| 2749 | shader.AddLine("vec4 texture_tmp = " + texture + ';'); | ||
| 2732 | std::size_t dest_elem{}; | 2750 | std::size_t dest_elem{}; |
| 2733 | for (std::size_t elem = 0; elem < 4; ++elem) { | 2751 | for (std::size_t elem = 0; elem < 4; ++elem) { |
| 2734 | if (!instr.tex.IsComponentEnabled(elem)) { | 2752 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 2735 | // Skip disabled components | 2753 | // Skip disabled components |
| 2736 | continue; | 2754 | continue; |
| 2737 | } | 2755 | } |
| 2738 | regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); | 2756 | regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, |
| 2757 | dest_elem); | ||
| 2739 | ++dest_elem; | 2758 | ++dest_elem; |
| 2740 | } | 2759 | } |
| 2741 | } | 2760 | } |
| @@ -2743,129 +2762,28 @@ private: | |||
| 2743 | } | 2762 | } |
| 2744 | case OpCode::Id::TEXS: { | 2763 | case OpCode::Id::TEXS: { |
| 2745 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; | 2764 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; |
| 2746 | bool is_array{instr.texs.IsArrayTexture()}; | 2765 | const bool is_array{instr.texs.IsArrayTexture()}; |
| 2747 | 2766 | const bool depth_compare = | |
| 2767 | instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | ||
| 2768 | const auto process_mode = instr.texs.GetTextureProcessMode(); | ||
| 2748 | UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2769 | UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2749 | "NODEP is not implemented"); | 2770 | "NODEP is not implemented"); |
| 2750 | 2771 | ||
| 2751 | const auto scope = shader.Scope(); | 2772 | const auto scope = shader.Scope(); |
| 2752 | 2773 | ||
| 2753 | const bool depth_compare = | 2774 | const auto [coord, texture] = |
| 2754 | instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2775 | GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array); |
| 2755 | u32 num_coordinates = TextureCoordinates(texture_type); | ||
| 2756 | const auto process_mode = instr.texs.GetTextureProcessMode(); | ||
| 2757 | u32 lod_offset = 0; | ||
| 2758 | if (process_mode == Tegra::Shader::TextureProcessMode::LL) { | ||
| 2759 | if (num_coordinates > 2) { | ||
| 2760 | shader.AddLine("float lod_value = " + | ||
| 2761 | regs.GetRegisterAsFloat(instr.gpr20.Value() + 1) + ';'); | ||
| 2762 | lod_offset = 2; | ||
| 2763 | } else { | ||
| 2764 | shader.AddLine("float lod_value = " + regs.GetRegisterAsFloat(instr.gpr20) + | ||
| 2765 | ';'); | ||
| 2766 | lod_offset = 1; | ||
| 2767 | } | ||
| 2768 | } | ||
| 2769 | 2776 | ||
| 2770 | switch (num_coordinates) { | 2777 | shader.AddLine(coord); |
| 2771 | case 1: { | ||
| 2772 | shader.AddLine("float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';'); | ||
| 2773 | break; | ||
| 2774 | } | ||
| 2775 | case 2: { | ||
| 2776 | if (is_array) { | ||
| 2777 | if (depth_compare) { | ||
| 2778 | const std::string index = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2779 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2780 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2781 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); | ||
| 2782 | shader.AddLine("vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + | ||
| 2783 | index + ");"); | ||
| 2784 | } else { | ||
| 2785 | const std::string index = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2786 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2787 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2788 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + index + | ||
| 2789 | ");"); | ||
| 2790 | } | ||
| 2791 | } else { | ||
| 2792 | if (lod_offset != 0) { | ||
| 2793 | if (depth_compare) { | ||
| 2794 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2795 | const std::string y = | ||
| 2796 | regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2797 | const std::string z = | ||
| 2798 | regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset); | ||
| 2799 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + | ||
| 2800 | ");"); | ||
| 2801 | } else { | ||
| 2802 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2803 | const std::string y = | ||
| 2804 | regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2805 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2806 | } | ||
| 2807 | } else { | ||
| 2808 | if (depth_compare) { | ||
| 2809 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2810 | const std::string y = | ||
| 2811 | regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2812 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2813 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + | ||
| 2814 | ");"); | ||
| 2815 | } else { | ||
| 2816 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2817 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2818 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2819 | } | ||
| 2820 | } | ||
| 2821 | } | ||
| 2822 | break; | ||
| 2823 | } | ||
| 2824 | case 3: { | ||
| 2825 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2826 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2827 | const std::string z = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2828 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); | ||
| 2829 | break; | ||
| 2830 | } | ||
| 2831 | default: | ||
| 2832 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", | ||
| 2833 | static_cast<u32>(num_coordinates)); | ||
| 2834 | 2778 | ||
| 2835 | // Fallback to interpreting as a 2D texture for now | 2779 | if (!depth_compare) { |
| 2836 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2780 | shader.AddLine("vec4 texture_tmp = " + texture + ';'); |
| 2837 | const std::string y = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2838 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2839 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 2840 | is_array = false; | ||
| 2841 | } | ||
| 2842 | const std::string sampler = | ||
| 2843 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); | ||
| 2844 | 2781 | ||
| 2845 | std::string texture = [&]() { | 2782 | } else { |
| 2846 | switch (process_mode) { | 2783 | shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");"); |
| 2847 | case Tegra::Shader::TextureProcessMode::None: | ||
| 2848 | return "texture(" + sampler + ", coords)"; | ||
| 2849 | case Tegra::Shader::TextureProcessMode::LZ: | ||
| 2850 | if (depth_compare && is_array) { | ||
| 2851 | return "texture(" + sampler + ", coords)"; | ||
| 2852 | } else { | ||
| 2853 | return "textureLod(" + sampler + ", coords, 0.0)"; | ||
| 2854 | } | ||
| 2855 | break; | ||
| 2856 | case Tegra::Shader::TextureProcessMode::LL: | ||
| 2857 | return "textureLod(" + sampler + ", coords, lod_value)"; | ||
| 2858 | default: | ||
| 2859 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", | ||
| 2860 | static_cast<u32>(instr.texs.GetTextureProcessMode())); | ||
| 2861 | return "texture(" + sampler + ", coords)"; | ||
| 2862 | } | ||
| 2863 | }(); | ||
| 2864 | if (depth_compare) { | ||
| 2865 | texture = "vec4(" + texture + ')'; | ||
| 2866 | } | 2784 | } |
| 2867 | 2785 | ||
| 2868 | WriteTexsInstruction(instr, texture); | 2786 | WriteTexsInstruction(instr, "texture_tmp"); |
| 2869 | break; | 2787 | break; |
| 2870 | } | 2788 | } |
| 2871 | case OpCode::Id::TLDS: { | 2789 | case OpCode::Id::TLDS: { |
| @@ -3934,4 +3852,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u | |||
| 3934 | return {}; | 3852 | return {}; |
| 3935 | } | 3853 | } |
| 3936 | 3854 | ||
| 3937 | } // namespace OpenGL::GLShader::Decompiler | 3855 | } // namespace OpenGL::GLShader::Decompiler \ No newline at end of file |