mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-21 02:17:08 +00:00
Eliminate frame_is_complete_, add prewalming loop.
This commit is contained in:
@@ -20,13 +20,11 @@ VideoOutput::VideoOutput(const uint8_t *memory) :
|
||||
1,
|
||||
Outputs::Display::Type::PAL50,
|
||||
Outputs::Display::InputDataType::Red1Green1Blue1) {
|
||||
crt_.set_visible_area(crt_.get_rect_for_area(
|
||||
312 - vsync_end,
|
||||
256,
|
||||
h_total - hsync_start,
|
||||
80 * 8,
|
||||
4.0f / 3.0f
|
||||
));
|
||||
// Default construction values leave this out of text mode, and text
|
||||
// mode uses a subregion of pixel modes.
|
||||
crt_.set_automatic_fixed_framing([&] {
|
||||
run_for(Cycles(10'000));
|
||||
});
|
||||
}
|
||||
|
||||
void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *const scan_target) {
|
||||
|
||||
+42
-11
@@ -13,6 +13,8 @@
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
|
||||
// TODO: keep posted_rect_ et al in scaled coordinates.
|
||||
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
// MARK: - Input timing setup.
|
||||
@@ -106,6 +108,30 @@ void CRT::set_new_timing(
|
||||
scan_target_->set_modals(scan_target_modals_);
|
||||
}
|
||||
|
||||
void CRT::set_framing(const Framing framing, const Outputs::Display::Rect bounds, const float minimum_scale) {
|
||||
scan_target_modals_.visible_area = bounds;
|
||||
rect_bounds_ = posted_rect_ = Outputs::Display::Rect(
|
||||
bounds.origin.x * scan_target_modals_.output_scale.x,
|
||||
bounds.origin.y * scan_target_modals_.output_scale.y,
|
||||
bounds.size.width * scan_target_modals_.output_scale.x,
|
||||
bounds.size.height * scan_target_modals_.output_scale.y
|
||||
);
|
||||
framing_ = framing;
|
||||
minimum_scale_ = minimum_scale;
|
||||
scan_target_->set_modals(scan_target_modals_);
|
||||
}
|
||||
|
||||
void CRT::set_automatic_fixed_framing(const std::function<void()> &advance) {
|
||||
set_framing(Framing::AutomaticFixed);
|
||||
while(framing() == Framing::AutomaticFixed) {
|
||||
advance();
|
||||
}
|
||||
}
|
||||
|
||||
Framing CRT::framing() {
|
||||
return framing_;
|
||||
}
|
||||
|
||||
void CRT::set_new_display_type(const int cycles_per_line, const Outputs::Display::Type displayType) {
|
||||
switch(displayType) {
|
||||
case Outputs::Display::Type::PAL50:
|
||||
@@ -223,6 +249,9 @@ void CRT::advance_cycles(
|
||||
return this->end_point(uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles));
|
||||
};
|
||||
|
||||
using EndPoint = Outputs::Display::ScanTarget::Scan::EndPoint;
|
||||
EndPoint start_point;
|
||||
|
||||
while(number_of_cycles) {
|
||||
// Get time until next horizontal and vertical sync generator events.
|
||||
const auto vertical_event = vertical_flywheel_.next_event_in_period(vsync_requested, number_of_cycles);
|
||||
@@ -245,13 +274,14 @@ void CRT::advance_cycles(
|
||||
!horizontal_flywheel_.is_in_retrace() &&
|
||||
!vertical_flywheel_.is_in_retrace();
|
||||
Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->begin_scan() : nullptr;
|
||||
frame_is_complete_ &= !is_output_segment || bool(next_scan);
|
||||
did_output |= is_output_segment;
|
||||
|
||||
// If outputting, store the start location and scan constants.
|
||||
if(next_scan) {
|
||||
next_scan->end_points[0] = end_point();
|
||||
next_scan->composite_amplitude = colour_burst_amplitude_;
|
||||
} else if(is_output_segment) {
|
||||
start_point = end_point();
|
||||
}
|
||||
|
||||
// Advance time: that'll affect both the colour subcarrier and the number of cycles left to run.
|
||||
@@ -270,27 +300,28 @@ void CRT::advance_cycles(
|
||||
vertical_flywheel_.apply_event(next_run_length, active_vertical_event);
|
||||
|
||||
if(active_vertical_event == Flywheel::SyncEvent::StartRetrace) {
|
||||
if(frame_is_complete_ && captures_in_rect_ > 5 && vertical_flywheel_.was_stable()) {
|
||||
if(captures_in_rect_ > 5 && vertical_flywheel_.was_stable()) {
|
||||
posit(active_rect_);
|
||||
}
|
||||
active_rect_ = Display::Rect(65536.0f, 65536.0f, 0.0f, 0.0f);
|
||||
frame_is_complete_ = true;
|
||||
captures_in_rect_ = 0;
|
||||
level_changes_in_frame_ = 0;
|
||||
}
|
||||
|
||||
// End the scan if necessary.
|
||||
const auto posit_scan = [&](const EndPoint &start, const EndPoint &end) {
|
||||
if(levels_are_interesting_ || number_of_samples > 1) {
|
||||
++captures_in_rect_;
|
||||
active_rect_.expand(start.x, end.x, start.y, end.y);
|
||||
}
|
||||
};
|
||||
|
||||
if(next_scan) {
|
||||
next_scan->end_points[1] = end_point();
|
||||
if(frame_is_complete_ && (levels_are_interesting_ || number_of_samples > 1)) {
|
||||
++captures_in_rect_;
|
||||
active_rect_.expand(
|
||||
next_scan->end_points[0].x, next_scan->end_points[1].x,
|
||||
next_scan->end_points[0].y, next_scan->end_points[1].y
|
||||
);
|
||||
}
|
||||
|
||||
posit_scan(next_scan->end_points[0], next_scan->end_points[1]);
|
||||
scan_target_->end_scan();
|
||||
} else if(is_output_segment) {
|
||||
posit_scan(start_point, end_point());
|
||||
}
|
||||
|
||||
using Event = Outputs::Display::ScanTarget::Event;
|
||||
|
||||
+20
-2
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -270,7 +271,23 @@ public:
|
||||
/*! Nominates a section of the display to crop to for output. */
|
||||
void set_visible_area(Outputs::Display::Rect);
|
||||
|
||||
void set_framing(Framing, Outputs::Display::Rect seed, Outputs::Display::Rect maximum);
|
||||
/*!
|
||||
Provides hints that the CRT will use so as best to present its display to the user; in particular:
|
||||
|
||||
* Framing::AutomaticFixed implies that this machine always uses the same region of the screen for pixels, and
|
||||
the two other parameters are ignored. The CRT will automatically switch to Framing:Static once it has established
|
||||
appropriate screen bounds.
|
||||
* Framing::DynamicRange means that software is fully in control of which parts of the display may contain content; in that case
|
||||
@c bounds provides the maximal region that a programmer might consider to be visible, and @c minimum_scale provides a limit on
|
||||
the degree to which the CRT should zoom in on content within those bounds;
|
||||
* Framing::Static means that the CRT will not try to autodetect the meaningful area at all; whatever is supplied for @c bounds will be
|
||||
the permanent display area.
|
||||
|
||||
It is currently intended that this method be used at startup only.
|
||||
*/
|
||||
void set_framing(Framing, Outputs::Display::Rect bounds = {}, float minimum_scale = 0.85f);
|
||||
void set_automatic_fixed_framing(const std::function<void()> &);
|
||||
Framing framing();
|
||||
|
||||
/*! @returns The rectangle describing a subset of the display, allowing for sync periods. */
|
||||
Outputs::Display::Rect get_rect_for_area(
|
||||
@@ -382,9 +399,9 @@ private:
|
||||
// Accumulator for interesting detail from this frame.
|
||||
Outputs::Display::Rect active_rect_;
|
||||
int captures_in_rect_ = 0;
|
||||
bool frame_is_complete_ = false;
|
||||
|
||||
// Current state of cropping rectangle as communicated onwards.
|
||||
Outputs::Display::Rect rect_bounds_;
|
||||
Outputs::Display::Rect posted_rect_;
|
||||
Outputs::Display::Rect previous_posted_rect_;
|
||||
Numeric::CubicCurve animation_curve_;
|
||||
@@ -397,6 +414,7 @@ private:
|
||||
uint32_t last_level_ = 0;
|
||||
|
||||
Framing framing_ = Framing::AutomaticFixed;
|
||||
float minimum_scale_ = 0.85f;
|
||||
RectAccumulator rect_accumulator_;
|
||||
void posit(Display::Rect);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user