1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-11 08:30:55 +00:00

Resolves off-by-one error in line writing, adds diagnostic one-big-lock option.

This commit is contained in:
Thomas Harte 2020-07-29 22:45:13 -04:00
parent 72a8fef989
commit 51ad423eca
2 changed files with 22 additions and 6 deletions

View File

@ -11,6 +11,12 @@
#include <cassert>
#include <cstring>
// If enabled, this uses the producer lock to cover both production and consumption
// rather than attempting to proceed lockfree. This is primarily for diagnostic purposes;
// it allows empirical exploration of whether the logical and memory barriers that are
// meant to mediate things between the read pointers and the submit pointers are functioning.
#define ONE_BIG_LOCK
#define TextureAddressGetY(v) uint16_t((v) >> 11)
#define TextureAddressGetX(v) uint16_t((v) & 0x7ff)
#define TextureSub(a, b) (((a) - (b)) & 0x3fffff)
@ -20,11 +26,11 @@ using namespace Outputs::Display;
BufferingScanTarget::BufferingScanTarget() {
// Ensure proper initialisation of the two atomic pointer sets.
read_pointers_.store(write_pointers_);
submit_pointers_.store(write_pointers_);
read_pointers_.store(write_pointers_, std::memory_order::memory_order_relaxed);
submit_pointers_.store(write_pointers_, std::memory_order::memory_order_relaxed);
// Establish initial state for is_updating_.
is_updating_.clear();
is_updating_.clear(std::memory_order::memory_order_relaxed);
}
// MARK: - Producer; pixel data.
@ -191,7 +197,6 @@ void BufferingScanTarget::announce(Event event, bool is_visible, const Outputs::
allocation_has_failed_ = true;
active_line_ = nullptr;
} else {
write_pointers_.line = next_line;
active_line_ = &line_buffer_[size_t(write_pointers_.line)];
}
provided_scans_ = 0;
@ -219,6 +224,9 @@ void BufferingScanTarget::announce(Event event, bool is_visible, const Outputs::
active_line_->end_points[1].cycles_since_end_of_horizontal_retrace = location.cycles_since_end_of_horizontal_retrace;
active_line_->end_points[1].composite_angle = location.composite_angle;
// Advance the line pointer.
write_pointers_.line = uint16_t((write_pointers_.line + 1) % line_buffer_size_);
// Update the submit pointers with all lines, scans and data written during this line.
submit_pointers_.store(write_pointers_, std::memory_order::memory_order_release);
} else {
@ -247,7 +255,9 @@ const Outputs::Display::Metrics &BufferingScanTarget::display_metrics() {
}
void BufferingScanTarget::set_write_area(uint8_t *base) {
#ifndef ONE_BIG_LOCK
std::lock_guard lock_guard(producer_mutex_);
#endif
write_area_ = base;
data_type_size_ = Outputs::Display::size_for_data_type(modals_.input_data_type);
write_pointers_ = submit_pointers_ = read_pointers_ = PointerSet();
@ -267,6 +277,10 @@ void BufferingScanTarget::set_modals(Modals modals) {
// MARK: - Consumer.
void BufferingScanTarget::perform(const std::function<void(const OutputArea &)> &function) {
#ifdef ONE_BIG_LOCK
std::lock_guard lock_guard(producer_mutex_);
#endif
// The area to draw is that between the read pointers, representing wherever reading
// last stopped, and the submit pointers, representing all the new data that has been
// cleared for submission.

View File

@ -168,8 +168,10 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
bool frame_is_complete_ = true;
bool previous_frame_was_complete_ = true;
// TODO: make this an implementation detail.
// ... and expose some sort of difference?
// By convention everything in the PointerSet points to the next instance
// of whatever it is that will be used. So a client should start with whatever
// is pointed to by the read pointers and carry until it gets to a value that
// is equal to whatever is in the submit pointers.
struct PointerSet {
// This constructor is here to appease GCC's interpretation of
// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377