memory_util.cpp 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <boost/container_hash/hash.hpp>
  7. #include "common/common_types.h"
  8. #include "core/core.h"
  9. #include "video_core/engines/maxwell_3d.h"
  10. #include "video_core/memory_manager.h"
  11. #include "video_core/shader/memory_util.h"
  12. #include "video_core/shader/shader_ir.h"
  13. namespace VideoCommon::Shader {
  14. GPUVAddr GetShaderAddress(Core::System& system,
  15. Tegra::Engines::Maxwell3D::Regs::ShaderProgram program) {
  16. const auto& gpu{system.GPU().Maxwell3D()};
  17. const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
  18. return gpu.regs.code_address.CodeAddress() + shader_config.offset;
  19. }
  20. bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
  21. // Sched instructions appear once every 4 instructions.
  22. constexpr std::size_t SchedPeriod = 4;
  23. const std::size_t absolute_offset = offset - main_offset;
  24. return (absolute_offset % SchedPeriod) == 0;
  25. }
  26. std::size_t CalculateProgramSize(const ProgramCode& program, bool is_compute) {
  27. // This is the encoded version of BRA that jumps to itself. All Nvidia
  28. // shaders end with one.
  29. static constexpr u64 SELF_JUMPING_BRANCH = 0xE2400FFFFF07000FULL;
  30. static constexpr u64 MASK = 0xFFFFFFFFFF7FFFFFULL;
  31. const std::size_t start_offset = is_compute ? KERNEL_MAIN_OFFSET : STAGE_MAIN_OFFSET;
  32. std::size_t offset = start_offset;
  33. while (offset < program.size()) {
  34. const u64 instruction = program[offset];
  35. if (!IsSchedInstruction(offset, start_offset)) {
  36. if ((instruction & MASK) == SELF_JUMPING_BRANCH) {
  37. // End on Maxwell's "nop" instruction
  38. break;
  39. }
  40. if (instruction == 0) {
  41. break;
  42. }
  43. }
  44. ++offset;
  45. }
  46. // The last instruction is included in the program size
  47. return std::min(offset + 1, program.size());
  48. }
  49. ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, GPUVAddr gpu_addr,
  50. const u8* host_ptr, bool is_compute) {
  51. ProgramCode code(VideoCommon::Shader::MAX_PROGRAM_LENGTH);
  52. ASSERT_OR_EXECUTE(host_ptr != nullptr, { return code; });
  53. memory_manager.ReadBlockUnsafe(gpu_addr, code.data(), code.size() * sizeof(u64));
  54. code.resize(CalculateProgramSize(code, is_compute));
  55. return code;
  56. }
  57. u64 GetUniqueIdentifier(Tegra::Engines::ShaderType shader_type, bool is_a, const ProgramCode& code,
  58. const ProgramCode& code_b) {
  59. u64 unique_identifier = boost::hash_value(code);
  60. if (is_a) {
  61. // VertexA programs include two programs
  62. boost::hash_combine(unique_identifier, boost::hash_value(code_b));
  63. }
  64. return unique_identifier;
  65. }
  66. } // namespace VideoCommon::Shader