1
0
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:
Thomas Harte
2025-10-10 17:59:10 -04:00
parent b0c2b55fc9
commit a9f5b17fcb
3 changed files with 67 additions and 20 deletions
+5 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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);