OXYGENHUB
◆ main openmsx / src / video / PostProcessor.hh
7.3 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef GLPOSTPROCESSOR_HH
2
#define GLPOSTPROCESSOR_HH
3
4
#include "GLUtil.hh"
5
#include "RenderSettings.hh"
6
#include "VideoLayer.hh"
7
8
#include "EmuTime.hh"
9
#include "Schedulable.hh"
10
11
#include <array>
12
#include <memory>
13
#include <vector>
14
15
namespace openmsx {
16
17
class AviRecorder;
18
class CliComm;
19
class Deflicker;
20
class DeinterlacedFrame;
21
class Display;
22
class DoubledFrame;
23
class EventDistributor;
24
class FrameSource;
25
class GLScaler;
26
class MSXMotherBoard;
27
class RawFrame;
28
class RenderSettings;
29
class SuperImposedFrame;
30
31
/** A post processor builds the frame that is displayed from the MSX frame,
32
  * while applying effects such as scalers, noise etc.
33
  */
34
class PostProcessor final : public VideoLayer, private Schedulable
35
{
36
public:
37
	PostProcessor(
38
		MSXMotherBoard& motherBoard, Display& display,
39
		OutputSurface& screen, const std::string& videoSource,
40
		unsigned maxWidth, unsigned height, bool canDoInterlace);
41
	~PostProcessor() override;
42
43
	// Layer interface:
44
	void paint(OutputSurface& output) override;
45
46
	/** Sets up the "abcdFrame" variables for a new frame.
47
	  * TODO: The point of passing the finished frame in and the new workFrame
48
	  *       out is to be able to split off the scaler application as a
49
	  *       separate class.
50
	  * @param finishedFrame Frame that has just become available.
51
	  * @param time The moment in time the frame becomes available. Used to
52
	  *             calculate the framerate for recording (depends on
53
	  *             PAL/NTSC, frameskip).
54
	  * @return RawFrame object that can be used for building the next frame.
55
	  */
56
	[[nodiscard]] std::unique_ptr<RawFrame> rotateFrames(
57
		std::unique_ptr<RawFrame> finishedFrame, EmuTime time);
58
59
	/** Set the Video frame on which to superimpose the 'normal' output of
60
	  * this PostProcessor. Superimpose is done (preferably) after the
61
	  * normal output is scaled. IOW the video frame is (preferably) left
62
	  * unchanged, though exceptions are e.g. scalers that render
63
	  * scanlines, those are preferably also visible in the video frame.
64
	  */
65
	void setSuperimposeVideoFrame(const RawFrame* videoSource) {
66
		superImposeVideoFrame = videoSource;
67
	}
68
69
	/** Set the VDP frame on which to superimpose the 'normal' output of
70
	  * this PostProcessor. This is similar to the method above, except
71
	  * that now the superimposing is done before scaling. IOW both frames
72
	  * get scaled.
73
	  */
74
	void setSuperimposeVdpFrame(const FrameSource* vdpSource) {
75
		superImposeVdpFrame = vdpSource;
76
	}
77
78
	/** Start/stop recording.
79
	  * @param recorder_ Finished frames should be pushed to this
80
	  *                  AviRecorder. Can also be nullptr, meaning
81
	  *                  recording is stopped.
82
	  */
83
	void setRecorder(AviRecorder* recorder_) { recorder = recorder_; }
84
85
	/** Is recording active.
86
	  * ATM used to keep frameskip constant during recording.
87
	  */
88
	[[nodiscard]] bool isRecording() const { return recorder != nullptr; }
89
90
	/** Get the frame that would be displayed. E.g. so that it can be
91
	  * superimposed over the output of another PostProcessor, see
92
	  * setSuperimposeVdpFrame().
93
	  */
94
	[[nodiscard]] FrameSource* getPaintFrame() const { return paintFrame; }
95
96
	/** Get the last (finished) raw frame, could be nullptr.
97
	  */
98
	[[nodiscard]] RawFrame* getLastRawFrame() const {
99
		return lastFramesCount > 0 ? lastFrames[0].get() : nullptr;
100
	}
101
102
	// VideoLayer
103
	void takeRawScreenShot(std::optional<unsigned> height, const std::string& filename) override;
104
105
	[[nodiscard]] CliComm& getCliComm();
106
107
private:
108
	// Observer<Setting> interface:
109
	void update(const Setting& setting) noexcept override;
110
111
	// Schedulable
112
	void executeUntil(EmuTime time) override;
113
114
	/** Returns the maximum width for lines [y..y+step).
115
	  */
116
	[[nodiscard]] static unsigned getLineWidth(FrameSource* frame, unsigned y, unsigned step);
117
118
	void initBuffers();
119
	void createRegions();
120
	void uploadFrame();
121
	void uploadBlock(unsigned srcStartY, unsigned srcEndY,
122
	                 unsigned lineWidth);
123
124
	void preCalcNoise(float factor);
125
	void drawNoise() const;
126
	void drawGlow(int glow);
127
128
	void preCalcMonitor3D(float width);
129
	void drawMonitor3D() const;
130
131
private:
132
	Display& display;
133
	RenderSettings& renderSettings;
134
	EventDistributor& eventDistributor;
135
136
	/** The surface which is visible to the user. */
137
	OutputSurface& screen;
138
139
	/** The last 4 fully rendered (unscaled) MSX frames. */
140
	std::array<std::unique_ptr<RawFrame>, 4> lastFrames;
141
142
	/** Combined the last two frames in a deinterlaced frame. */
143
	std::unique_ptr<DeinterlacedFrame> deinterlacedFrame;
144
145
	/** Each line of the last frame twice, to get double vertical resolution. */
146
	std::unique_ptr<DoubledFrame> interlacedFrame;
147
148
	/** Combine the last 4 frames into one 'flicker-free' frame. */
149
	std::unique_ptr<Deflicker> deflicker;
150
151
	/** Result of superimposing 2 frames. */
152
	std::unique_ptr<SuperImposedFrame> superImposedFrame;
153
154
	/** Represents a frame as it should be displayed.
155
	  * This can be simply a RawFrame or two RawFrames combined in a
156
	  * DeinterlacedFrame or DoubledFrame.
157
	  */
158
	FrameSource* paintFrame = nullptr;
159
160
	/** Video recorder, nullptr when not recording. */
161
	AviRecorder* recorder = nullptr;
162
163
	/** Video frame on which to superimpose the (VDP) output.
164
	  * nullptr when not superimposing. */
165
	const RawFrame* superImposeVideoFrame = nullptr;
166
	const FrameSource* superImposeVdpFrame = nullptr;
167
168
	int interleaveCount = 0; // for interleave-black-frame
169
	int lastFramesCount = 0; // How many items in lastFrames[] are up-to-date
170
	unsigned maxWidth; // we lazily create RawFrame objects in lastFrames[]
171
	unsigned height;   // these two vars remember how big those should be
172
173
	/** Laserdisc cannot do interlace (better: the current implementation
174
	  * is not interlaced). In that case some internal stuff can be done
175
	  * with less buffers.
176
	  */
177
	const bool canDoInterlace;
178
179
	EmuTime lastRotate;
180
	/** The currently active scaler.
181
	  */
182
	std::unique_ptr<GLScaler> currScaler;
183
184
	struct StoredFrame {
185
		gl::ivec2 size; // (re)allocate when window size changes
186
		gl::Texture tex;
187
		gl::FrameBufferObject fbo;
188
	};
189
	std::array<StoredFrame, 2> renderedFrames;
190
191
	// Noise effect:
192
	gl::Texture noiseTextureA{true, true}; // interpolate + wrap
193
	gl::Texture noiseTextureB{true, true};
194
	float noiseX = 0.0f, noiseY = 0.0f;
195
196
	struct TextureData {
197
		gl::ColorTexture tex;
198
		[[nodiscard]] unsigned width() const { return tex.getWidth(); }
199
	};
200
	std::vector<TextureData> textures;
201
	gl::PixelBuffer<unsigned> pbo;
202
203
	gl::ColorTexture superImposeTex;
204
205
	struct Region {
206
		Region(unsigned srcStartY_, unsigned srcEndY_,
207
		       unsigned dstStartY_, unsigned dstEndY_,
208
		       unsigned lineWidth_)
209
			: srcStartY(srcStartY_)
210
			, srcEndY(srcEndY_)
211
			, dstStartY(dstStartY_)
212
			, dstEndY(dstEndY_)
213
			, lineWidth(lineWidth_) {}
214
		unsigned srcStartY;
215
		unsigned srcEndY;
216
		unsigned dstStartY;
217
		unsigned dstEndY;
218
		unsigned lineWidth;
219
	};
220
	std::vector<Region> regions;
221
	unsigned regionsDstHeight = 0; // 'regions' were calculated for this output height (relevant when changing scale_factor when paused)
222
223
	unsigned frameCounter = 0;
224
225
	/** Currently active scale algorithm, used to detect scaler changes.
226
	  */
227
	RenderSettings::ScaleAlgorithm scaleAlgorithm = RenderSettings::ScaleAlgorithm::NO;
228
229
	gl::ShaderProgram monitor3DProg;
230
	gl::BufferObject arrayBuffer;
231
	gl::BufferObject elementBuffer;
232
	gl::BufferObject vbo;
233
	gl::BufferObject stretchVBO;
234
235
	bool storedFrame = false;
236
};
237
238
} // namespace openmsx
239
240
#endif // GLPOSTPROCESSOR_HH