nvflinger.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include "common/alignment.h"
  6. #include "common/microprofile.h"
  7. #include "common/scope_exit.h"
  8. #include "core/core.h"
  9. #include "core/core_timing.h"
  10. #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
  11. #include "core/hle/service/nvdrv/nvdrv.h"
  12. #include "core/hle/service/nvflinger/buffer_queue.h"
  13. #include "core/hle/service/nvflinger/nvflinger.h"
  14. #include "video_core/renderer_base.h"
  15. #include "video_core/video_core.h"
  16. namespace Service::NVFlinger {
  17. constexpr size_t SCREEN_REFRESH_RATE = 60;
  18. constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
  19. NVFlinger::NVFlinger() {
  20. // Add the different displays to the list of displays.
  21. Display default_{0, "Default"};
  22. Display external{1, "External"};
  23. Display edid{2, "Edid"};
  24. Display internal{3, "Internal"};
  25. displays.emplace_back(default_);
  26. displays.emplace_back(external);
  27. displays.emplace_back(edid);
  28. displays.emplace_back(internal);
  29. // Schedule the screen composition events
  30. composition_event =
  31. CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
  32. Compose();
  33. CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
  34. });
  35. CoreTiming::ScheduleEvent(frame_ticks, composition_event);
  36. }
  37. NVFlinger::~NVFlinger() {
  38. CoreTiming::UnscheduleEvent(composition_event, 0);
  39. }
  40. u64 NVFlinger::OpenDisplay(const std::string& name) {
  41. LOG_WARNING(Service, "Opening display %s", name.c_str());
  42. // TODO(Subv): Currently we only support the Default display.
  43. ASSERT(name == "Default");
  44. auto itr = std::find_if(displays.begin(), displays.end(),
  45. [&](const Display& display) { return display.name == name; });
  46. ASSERT(itr != displays.end());
  47. return itr->id;
  48. }
  49. u64 NVFlinger::CreateLayer(u64 display_id) {
  50. auto& display = GetDisplay(display_id);
  51. ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
  52. u64 layer_id = next_layer_id++;
  53. u32 buffer_queue_id = next_buffer_queue_id++;
  54. auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
  55. display.layers.emplace_back(layer_id, buffer_queue);
  56. buffer_queues.emplace_back(std::move(buffer_queue));
  57. return layer_id;
  58. }
  59. u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
  60. const auto& layer = GetLayer(display_id, layer_id);
  61. return layer.buffer_queue->GetId();
  62. }
  63. Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
  64. const auto& display = GetDisplay(display_id);
  65. return display.vsync_event;
  66. }
  67. std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
  68. auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
  69. [&](const auto& queue) { return queue->GetId() == id; });
  70. ASSERT(itr != buffer_queues.end());
  71. return *itr;
  72. }
  73. Display& NVFlinger::GetDisplay(u64 display_id) {
  74. auto itr = std::find_if(displays.begin(), displays.end(),
  75. [&](const Display& display) { return display.id == display_id; });
  76. ASSERT(itr != displays.end());
  77. return *itr;
  78. }
  79. Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
  80. auto& display = GetDisplay(display_id);
  81. auto itr = std::find_if(display.layers.begin(), display.layers.end(),
  82. [&](const Layer& layer) { return layer.id == layer_id; });
  83. ASSERT(itr != display.layers.end());
  84. return *itr;
  85. }
  86. void NVFlinger::Compose() {
  87. for (auto& display : displays) {
  88. // Trigger vsync for this display at the end of drawing
  89. SCOPE_EXIT({ display.vsync_event->Signal(); });
  90. // Don't do anything for displays without layers.
  91. if (display.layers.empty())
  92. continue;
  93. // TODO(Subv): Support more than 1 layer.
  94. ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
  95. Layer& layer = display.layers[0];
  96. auto& buffer_queue = layer.buffer_queue;
  97. // Search for a queued buffer and acquire it
  98. auto buffer = buffer_queue->AcquireBuffer();
  99. MicroProfileFlip();
  100. if (buffer == boost::none) {
  101. // There was no queued buffer to draw, render previous frame
  102. Core::System::GetInstance().perf_stats.EndGameFrame();
  103. VideoCore::g_renderer->SwapBuffers({});
  104. continue;
  105. }
  106. auto& igbp_buffer = buffer->igbp_buffer;
  107. // Now send the buffer to the GPU for drawing.
  108. auto nvdrv = Nvidia::nvdrv.lock();
  109. ASSERT(nvdrv);
  110. // TODO(Subv): Support more than just disp0. The display device selection is probably based
  111. // on which display we're drawing (Default, Internal, External, etc)
  112. auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
  113. ASSERT(nvdisp);
  114. nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
  115. igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform);
  116. buffer_queue->ReleaseBuffer(buffer->slot);
  117. // TODO(Subv): Figure out when we should actually signal this event.
  118. buffer_queue->GetNativeHandle()->Signal();
  119. }
  120. }
  121. Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
  122. Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
  123. vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
  124. }
  125. } // namespace Service::NVFlinger