PostProcessor.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #include "PostProcessor.hh"
  2. #include "Display.hh"
  3. #include "OutputSurface.hh"
  4. #include "DeinterlacedFrame.hh"
  5. #include "DoubledFrame.hh"
  6. #include "Deflicker.hh"
  7. #include "SuperImposedFrame.hh"
  8. #include "PNG.hh"
  9. #include "RenderSettings.hh"
  10. #include "RawFrame.hh"
  11. #include "AviRecorder.hh"
  12. #include "CliComm.hh"
  13. #include "MSXMotherBoard.hh"
  14. #include "Reactor.hh"
  15. #include "EventDistributor.hh"
  16. #include "FinishFrameEvent.hh"
  17. #include "CommandException.hh"
  18. #include "MemBuffer.hh"
  19. #include "vla.hh"
  20. #include "likely.hh"
  21. #include "build-info.hh"
  22. #include <algorithm>
  23. #include <cassert>
  24. #include <cstdint>
  25. #include <memory>
  26. namespace openmsx {
  27. PostProcessor::PostProcessor(MSXMotherBoard& motherBoard_,
  28. Display& display_, OutputSurface& screen_, const std::string& videoSource,
  29. unsigned maxWidth_, unsigned height_, bool canDoInterlace_)
  30. : VideoLayer(motherBoard_, videoSource)
  31. , Schedulable(motherBoard_.getScheduler())
  32. , renderSettings(display_.getRenderSettings())
  33. , screen(screen_)
  34. , paintFrame(nullptr)
  35. , recorder(nullptr)
  36. , superImposeVideoFrame(nullptr)
  37. , superImposeVdpFrame(nullptr)
  38. , interleaveCount(0)
  39. , lastFramesCount(0)
  40. , maxWidth(maxWidth_)
  41. , height(height_)
  42. , display(display_)
  43. , canDoInterlace(canDoInterlace_)
  44. , lastRotate(motherBoard_.getCurrentTime())
  45. , eventDistributor(motherBoard_.getReactor().getEventDistributor())
  46. {
  47. if (canDoInterlace) {
  48. deinterlacedFrame = std::make_unique<DeinterlacedFrame>(
  49. screen.getPixelFormat());
  50. interlacedFrame = std::make_unique<DoubledFrame>(
  51. screen.getPixelFormat());
  52. deflicker = Deflicker::create(
  53. screen.getPixelFormat(), lastFrames);
  54. superImposedFrame = SuperImposedFrame::create(
  55. screen.getPixelFormat());
  56. } else {
  57. // Laserdisc always produces non-interlaced frames, so we don't
  58. // need lastFrames[1..3], deinterlacedFrame and
  59. // interlacedFrame. Also it produces a complete frame at a
  60. // time, so we don't need lastFrames[0] (and have a separate
  61. // work buffer, for partially rendered frames).
  62. }
  63. }
  64. PostProcessor::~PostProcessor()
  65. {
  66. if (recorder) {
  67. getCliComm().printWarning(
  68. "Videorecording stopped, because you "
  69. "changed machine or changed a video setting "
  70. "during recording.");
  71. recorder->stop();
  72. }
  73. }
  74. CliComm& PostProcessor::getCliComm()
  75. {
  76. return display.getCliComm();
  77. }
  78. unsigned PostProcessor::getLineWidth(
  79. FrameSource* frame, unsigned y, unsigned step)
  80. {
  81. unsigned result = frame->getLineWidth(y);
  82. for (unsigned i = 1; i < step; ++i) {
  83. result = std::max(result, frame->getLineWidth(y + i));
  84. }
  85. return result;
  86. }
  87. std::unique_ptr<RawFrame> PostProcessor::rotateFrames(
  88. std::unique_ptr<RawFrame> finishedFrame, EmuTime::param time)
  89. {
  90. if (renderSettings.getInterleaveBlackFrame()) {
  91. auto delta = time - lastRotate; // time between last two calls
  92. auto middle = time + delta / 2; // estimate for middle between now
  93. // and next call
  94. setSyncPoint(middle);
  95. }
  96. lastRotate = time;
  97. // Figure out how many past frames we want to use.
  98. int numRequired = 1;
  99. bool doDeinterlace = false;
  100. bool doInterlace = false;
  101. bool doDeflicker = false;
  102. auto currType = finishedFrame->getField();
  103. if (canDoInterlace) {
  104. if (currType != FrameSource::FIELD_NONINTERLACED) {
  105. if (renderSettings.getDeinterlace()) {
  106. doDeinterlace = true;
  107. numRequired = 2;
  108. } else {
  109. doInterlace = true;
  110. }
  111. } else if (renderSettings.getDeflicker()) {
  112. doDeflicker = true;
  113. numRequired = 4;
  114. }
  115. }
  116. // Which frame can be returned (recycled) to caller. Prefer to return
  117. // the youngest frame to improve cache locality.
  118. int recycleIdx = (lastFramesCount < numRequired)
  119. ? lastFramesCount++ // store one more
  120. : (numRequired - 1); // youngest that's no longer needed
  121. assert(recycleIdx < 4);
  122. auto recycleFrame = std::move(lastFrames[recycleIdx]); // might be nullptr
  123. // Insert new frame in front of lastFrames[], shift older frames
  124. std::move_backward(lastFrames, lastFrames + recycleIdx,
  125. lastFrames + recycleIdx + 1);
  126. lastFrames[0] = std::move(finishedFrame);
  127. // Are enough frames available?
  128. if (lastFramesCount >= numRequired) {
  129. // Only the last 'numRequired' are kept up to date.
  130. lastFramesCount = numRequired;
  131. } else {
  132. // Not enough past frames, fall back to 'regular' rendering.
  133. // This situation can only occur when:
  134. // - The very first frame we render needs to be deinterlaced.
  135. // In other case we have at least one valid frame from the
  136. // past plus one new frame passed via the 'finishedFrame'
  137. // parameter.
  138. // - Or when (re)enabling the deflicker setting. Typically only
  139. // 1 frame in lastFrames[] is kept up-to-date (and we're
  140. // given 1 new frame), so it can take up-to 2 frame after
  141. // enabling deflicker before it actually takes effect.
  142. doDeinterlace = false;
  143. doInterlace = false;
  144. doDeflicker = false;
  145. }
  146. // Setup the to-be-painted frame
  147. if (doDeinterlace) {
  148. if (currType == FrameSource::FIELD_ODD) {
  149. deinterlacedFrame->init(lastFrames[1].get(), lastFrames[0].get());
  150. } else {
  151. deinterlacedFrame->init(lastFrames[0].get(), lastFrames[1].get());
  152. }
  153. paintFrame = deinterlacedFrame.get();
  154. } else if (doInterlace) {
  155. interlacedFrame->init(
  156. lastFrames[0].get(),
  157. (currType == FrameSource::FIELD_ODD) ? 1 : 0);
  158. paintFrame = interlacedFrame.get();
  159. } else if (doDeflicker) {
  160. deflicker->init();
  161. paintFrame = deflicker.get();
  162. } else {
  163. paintFrame = lastFrames[0].get();
  164. }
  165. if (superImposeVdpFrame) {
  166. superImposedFrame->init(paintFrame, superImposeVdpFrame);
  167. paintFrame = superImposedFrame.get();
  168. }
  169. // Possibly record this frame
  170. if (recorder && needRecord()) {
  171. try {
  172. recorder->addImage(paintFrame, time);
  173. } catch (MSXException& e) {
  174. getCliComm().printWarning(
  175. "Recording stopped with error: " +
  176. e.getMessage());
  177. recorder->stop();
  178. assert(!recorder);
  179. }
  180. }
  181. // Return recycled frame to the caller
  182. if (canDoInterlace) {
  183. if (unlikely(!recycleFrame)) {
  184. recycleFrame = std::make_unique<RawFrame>(
  185. screen.getPixelFormat(), maxWidth, height);
  186. }
  187. return recycleFrame;
  188. } else {
  189. return std::move(lastFrames[0]);
  190. }
  191. }
  192. void PostProcessor::executeUntil(EmuTime::param /*time*/)
  193. {
  194. // insert fake end of frame event
  195. eventDistributor.distributeEvent(
  196. std::make_shared<FinishFrameEvent>(
  197. getVideoSource(), getVideoSourceSetting(), false));
  198. }
  199. using WorkBuffer = std::vector<MemBuffer<char, SSE2_ALIGNMENT>>;
  200. static void getScaledFrame(FrameSource& paintFrame, unsigned bpp,
  201. unsigned height, const void** lines,
  202. WorkBuffer& workBuffer)
  203. {
  204. unsigned width = (height == 240) ? 320 : 640;
  205. unsigned pitch = width * ((bpp == 32) ? 4 : 2);
  206. const void* line = nullptr;
  207. void* work = nullptr;
  208. for (unsigned i = 0; i < height; ++i) {
  209. if (line == work) {
  210. // If work buffer was used in previous iteration,
  211. // then allocate a new one.
  212. work = workBuffer.emplace_back(pitch).data();
  213. }
  214. #if HAVE_32BPP
  215. if (bpp == 32) {
  216. // 32bpp
  217. auto* work2 = static_cast<uint32_t*>(work);
  218. if (height == 240) {
  219. line = paintFrame.getLinePtr320_240(i, work2);
  220. } else {
  221. assert (height == 480);
  222. line = paintFrame.getLinePtr640_480(i, work2);
  223. }
  224. } else
  225. #endif
  226. {
  227. #if HAVE_16BPP
  228. // 15bpp or 16bpp
  229. auto* work2 = static_cast<uint16_t*>(work);
  230. if (height == 240) {
  231. line = paintFrame.getLinePtr320_240(i, work2);
  232. } else {
  233. assert (height == 480);
  234. line = paintFrame.getLinePtr640_480(i, work2);
  235. }
  236. #endif
  237. }
  238. lines[i] = line;
  239. }
  240. }
  241. void PostProcessor::takeRawScreenShot(unsigned height2, const std::string& filename)
  242. {
  243. if (!paintFrame) {
  244. throw CommandException("TODO");
  245. }
  246. VLA(const void*, lines, height2);
  247. WorkBuffer workBuffer;
  248. getScaledFrame(*paintFrame, getBpp(), height2, lines, workBuffer);
  249. unsigned width = (height2 == 240) ? 320 : 640;
  250. PNG::save(width, height2, lines, paintFrame->getPixelFormat(), filename);
  251. }
  252. unsigned PostProcessor::getBpp() const
  253. {
  254. return screen.getPixelFormat().getBpp();
  255. }
  256. } // namespace openmsx