mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-29 12:50:28 +00:00
Privatises write_pointers_mutex_
and write_pointers_
.
Also gives subclasses control over write-area texture space allocation.
This commit is contained in:
parent
74788ccf8e
commit
e260f92988
@ -79,10 +79,6 @@ ScanTarget::ScanTarget(GLuint target_framebuffer, float output_gamma) :
|
|||||||
unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_NEAREST, false),
|
unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_NEAREST, false),
|
||||||
full_display_rectangle_(-1.0f, -1.0f, 2.0f, 2.0f) {
|
full_display_rectangle_(-1.0f, -1.0f, 2.0f, 2.0f) {
|
||||||
|
|
||||||
// Ensure proper initialisation of the two atomic pointer sets.
|
|
||||||
read_pointers_.store(write_pointers_);
|
|
||||||
submit_pointers_.store(write_pointers_);
|
|
||||||
|
|
||||||
// Allocate space for the scans and lines.
|
// Allocate space for the scans and lines.
|
||||||
allocate_buffer(scan_buffer_, scan_buffer_name_, scan_vertex_array_);
|
allocate_buffer(scan_buffer_, scan_buffer_name_, scan_vertex_array_);
|
||||||
allocate_buffer(line_buffer_, line_buffer_name_, line_vertex_array_);
|
allocate_buffer(line_buffer_, line_buffer_name_, line_vertex_array_);
|
||||||
@ -96,8 +92,7 @@ ScanTarget::ScanTarget(GLuint target_framebuffer, float output_gamma) :
|
|||||||
test_gl(glBlendFunc, GL_SRC_ALPHA, GL_CONSTANT_COLOR);
|
test_gl(glBlendFunc, GL_SRC_ALPHA, GL_CONSTANT_COLOR);
|
||||||
test_gl(glBlendColor, 0.4f, 0.4f, 0.4f, 1.0f);
|
test_gl(glBlendColor, 0.4f, 0.4f, 0.4f, 1.0f);
|
||||||
|
|
||||||
// Establish initial state for the two atomic flags.
|
// Establish initial state for is_drawing_to_accumulation_buffer_.
|
||||||
is_updating_.clear();
|
|
||||||
is_drawing_to_accumulation_buffer_.clear();
|
is_drawing_to_accumulation_buffer_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,17 +114,9 @@ void ScanTarget::setup_pipeline() {
|
|||||||
|
|
||||||
// Ensure the lock guard here has a restricted scope; this is the only time that a thread
|
// Ensure the lock guard here has a restricted scope; this is the only time that a thread
|
||||||
// other than the main owner of write_pointers_ may adjust it.
|
// other than the main owner of write_pointers_ may adjust it.
|
||||||
{
|
if(data_type_size != write_area_data_size()) {
|
||||||
std::lock_guard lock_guard(write_pointers_mutex_);
|
write_area_texture_.resize(WriteAreaWidth*WriteAreaHeight*data_type_size);
|
||||||
if(data_type_size != data_type_size_) {
|
set_write_area(write_area_texture_.data());
|
||||||
// TODO: flush output.
|
|
||||||
|
|
||||||
data_type_size_ = data_type_size;
|
|
||||||
write_area_texture_.resize(WriteAreaWidth*WriteAreaHeight*data_type_size_);
|
|
||||||
|
|
||||||
write_pointers_.scan_buffer = 0;
|
|
||||||
write_pointers_.write_area = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to bind line shaders.
|
// Prepare to bind line shaders.
|
||||||
@ -252,11 +239,11 @@ void ScanTarget::update(int, int output_height) {
|
|||||||
test_gl(glTexImage2D,
|
test_gl(glTexImage2D,
|
||||||
GL_TEXTURE_2D,
|
GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
internalFormatForDepth(data_type_size_),
|
internalFormatForDepth(write_area_data_size()),
|
||||||
WriteAreaWidth,
|
WriteAreaWidth,
|
||||||
WriteAreaHeight,
|
WriteAreaHeight,
|
||||||
0,
|
0,
|
||||||
formatForDepth(data_type_size_),
|
formatForDepth(write_area_data_size()),
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
nullptr);
|
nullptr);
|
||||||
texture_exists_ = true;
|
texture_exists_ = true;
|
||||||
@ -271,9 +258,9 @@ void ScanTarget::update(int, int output_height) {
|
|||||||
0, start_y,
|
0, start_y,
|
||||||
WriteAreaWidth,
|
WriteAreaWidth,
|
||||||
1 + end_y - start_y,
|
1 + end_y - start_y,
|
||||||
formatForDepth(data_type_size_),
|
formatForDepth(write_area_data_size()),
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
|
&write_area_texture_[size_t(TextureAddress(0, start_y)) * write_area_data_size()]);
|
||||||
} else {
|
} else {
|
||||||
// The circular buffer wrapped around; submit the data from the read pointer to the end of
|
// The circular buffer wrapped around; submit the data from the read pointer to the end of
|
||||||
// the buffer and from the start of the buffer to the submit pointer.
|
// the buffer and from the start of the buffer to the submit pointer.
|
||||||
@ -282,7 +269,7 @@ void ScanTarget::update(int, int output_height) {
|
|||||||
0, 0,
|
0, 0,
|
||||||
WriteAreaWidth,
|
WriteAreaWidth,
|
||||||
1 + end_y,
|
1 + end_y,
|
||||||
formatForDepth(data_type_size_),
|
formatForDepth(write_area_data_size()),
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
&write_area_texture_[0]);
|
&write_area_texture_[0]);
|
||||||
test_gl(glTexSubImage2D,
|
test_gl(glTexSubImage2D,
|
||||||
@ -290,9 +277,9 @@ void ScanTarget::update(int, int output_height) {
|
|||||||
0, start_y,
|
0, start_y,
|
||||||
WriteAreaWidth,
|
WriteAreaWidth,
|
||||||
WriteAreaHeight - start_y,
|
WriteAreaHeight - start_y,
|
||||||
formatForDepth(data_type_size_),
|
formatForDepth(write_area_data_size()),
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
|
&write_area_texture_[size_t(TextureAddress(0, start_y)) * write_area_data_size()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +145,9 @@ class ScanTarget: public Outputs::Display::BufferingScanTarget {
|
|||||||
contrast tends to be low, such as a composite colour display.
|
contrast tends to be low, such as a composite colour display.
|
||||||
*/
|
*/
|
||||||
bool is_soft_display_type();
|
bool is_soft_display_type();
|
||||||
|
|
||||||
|
// Storage for the write area.
|
||||||
|
std::vector<uint8_t> write_area_texture_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,15 @@
|
|||||||
|
|
||||||
using namespace Outputs::Display;
|
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_);
|
||||||
|
|
||||||
|
// Establish initial state for is_updating_.
|
||||||
|
is_updating_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void BufferingScanTarget::set_modals(Modals modals) {
|
void BufferingScanTarget::set_modals(Modals modals) {
|
||||||
// Don't change the modals while drawing is ongoing; a previous set might be
|
// Don't change the modals while drawing is ongoing; a previous set might be
|
||||||
// in the process of being established.
|
// in the process of being established.
|
||||||
@ -47,7 +56,7 @@ uint8_t *BufferingScanTarget::begin_data(size_t required_length, size_t required
|
|||||||
if(allocation_has_failed_) return nullptr;
|
if(allocation_has_failed_) return nullptr;
|
||||||
|
|
||||||
std::lock_guard lock_guard(write_pointers_mutex_);
|
std::lock_guard lock_guard(write_pointers_mutex_);
|
||||||
if(write_area_texture_.empty()) {
|
if(!write_area_) {
|
||||||
allocation_has_failed_ = true;
|
allocation_has_failed_ = true;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -84,8 +93,8 @@ uint8_t *BufferingScanTarget::begin_data(size_t required_length, size_t required
|
|||||||
data_is_allocated_ = true;
|
data_is_allocated_ = true;
|
||||||
vended_write_area_pointer_ = write_pointers_.write_area = TextureAddress(aligned_start_x, output_y);
|
vended_write_area_pointer_ = write_pointers_.write_area = TextureAddress(aligned_start_x, output_y);
|
||||||
|
|
||||||
assert(write_pointers_.write_area >= 1 && ((size_t(write_pointers_.write_area) + required_length + 1) * data_type_size_) <= write_area_texture_.size());
|
assert(write_pointers_.write_area >= 1 && ((size_t(write_pointers_.write_area) + required_length + 1) * data_type_size_) <= WriteAreaWidth*WriteAreaHeight*data_type_size_);
|
||||||
return &write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_];
|
return &write_area_[size_t(write_pointers_.write_area) * data_type_size_];
|
||||||
|
|
||||||
// Note state at exit:
|
// Note state at exit:
|
||||||
// write_pointers_.write_area points to the first pixel the client is expected to draw to.
|
// write_pointers_.write_area points to the first pixel the client is expected to draw to.
|
||||||
@ -98,8 +107,8 @@ void BufferingScanTarget::end_data(size_t actual_length) {
|
|||||||
|
|
||||||
// Bookend the start of the new data, to safeguard for precision errors in sampling.
|
// Bookend the start of the new data, to safeguard for precision errors in sampling.
|
||||||
memcpy(
|
memcpy(
|
||||||
&write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_],
|
&write_area_[size_t(write_pointers_.write_area - 1) * data_type_size_],
|
||||||
&write_area_texture_[size_t(write_pointers_.write_area) * data_type_size_],
|
&write_area_[size_t(write_pointers_.write_area) * data_type_size_],
|
||||||
data_type_size_);
|
data_type_size_);
|
||||||
|
|
||||||
// Advance to the end of the current run.
|
// Advance to the end of the current run.
|
||||||
@ -107,14 +116,14 @@ void BufferingScanTarget::end_data(size_t actual_length) {
|
|||||||
|
|
||||||
// Also bookend the end.
|
// Also bookend the end.
|
||||||
memcpy(
|
memcpy(
|
||||||
&write_area_texture_[size_t(write_pointers_.write_area - 1) * data_type_size_],
|
&write_area_[size_t(write_pointers_.write_area - 1) * data_type_size_],
|
||||||
&write_area_texture_[size_t(write_pointers_.write_area - 2) * data_type_size_],
|
&write_area_[size_t(write_pointers_.write_area - 2) * data_type_size_],
|
||||||
data_type_size_);
|
data_type_size_);
|
||||||
|
|
||||||
// The write area was allocated in the knowledge that there's sufficient
|
// The write area was allocated in the knowledge that there's sufficient
|
||||||
// distance left on the current line, but there's a risk of exactly filling
|
// distance left on the current line, but there's a risk of exactly filling
|
||||||
// the final line, in which case this should wrap back to 0.
|
// the final line, in which case this should wrap back to 0.
|
||||||
write_pointers_.write_area %= (write_area_texture_.size() / data_type_size_);
|
write_pointers_.write_area %= WriteAreaWidth*WriteAreaHeight;
|
||||||
|
|
||||||
// Record that no further end_data calls are expected.
|
// Record that no further end_data calls are expected.
|
||||||
data_is_allocated_ = false;
|
data_is_allocated_ = false;
|
||||||
@ -238,3 +247,14 @@ Outputs::Display::ScanTarget::Scan *BufferingScanTarget::begin_scan() {
|
|||||||
vended_scan_ = result;
|
vended_scan_ = result;
|
||||||
return &result->scan;
|
return &result->scan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferingScanTarget::set_write_area(uint8_t *base) {
|
||||||
|
std::lock_guard lock_guard(write_pointers_mutex_);
|
||||||
|
write_area_ = base;
|
||||||
|
data_type_size_ = Outputs::Display::size_for_data_type(modals_.input_data_type);
|
||||||
|
write_pointers_ = submit_pointers_ = read_pointers_ = PointerSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t BufferingScanTarget::write_area_data_size() const {
|
||||||
|
return data_type_size_;
|
||||||
|
}
|
||||||
|
@ -29,7 +29,7 @@ namespace Display {
|
|||||||
Provides basic thread-safe (hopefully) circular queues for any scan target that:
|
Provides basic thread-safe (hopefully) circular queues for any scan target that:
|
||||||
|
|
||||||
* will store incoming Scans into a linear circular buffer and pack regions of
|
* will store incoming Scans into a linear circular buffer and pack regions of
|
||||||
incoming pixel data into a 2d texture;
|
incoming pixel data into a 2048x2048 2d texture;
|
||||||
* will compose whole lines of content by partioning the Scans based on sync
|
* will compose whole lines of content by partioning the Scans based on sync
|
||||||
placement and then pasting together their content;
|
placement and then pasting together their content;
|
||||||
* will process those lines as necessary to map from input format to whatever
|
* will process those lines as necessary to map from input format to whatever
|
||||||
@ -44,6 +44,8 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
|
|||||||
const Metrics &display_metrics();
|
const Metrics &display_metrics();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
BufferingScanTarget();
|
||||||
|
|
||||||
// Extends the definition of a Scan to include two extra fields,
|
// Extends the definition of a Scan to include two extra fields,
|
||||||
// completing this scan's source data and destination locations.
|
// completing this scan's source data and destination locations.
|
||||||
struct Scan {
|
struct Scan {
|
||||||
@ -99,11 +101,6 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
|
|||||||
// That'll allow owners to place this in shared video memory if possible.
|
// That'll allow owners to place this in shared video memory if possible.
|
||||||
std::array<Scan, 16384> scan_buffer_;
|
std::array<Scan, 16384> scan_buffer_;
|
||||||
|
|
||||||
/// A mutex for gettng access to write_pointers_; access to write_pointers_,
|
|
||||||
/// data_type_size_ or write_area_texture_ is almost never contended, so this
|
|
||||||
/// is cheap for the main use case.
|
|
||||||
std::mutex write_pointers_mutex_;
|
|
||||||
|
|
||||||
struct PointerSet {
|
struct PointerSet {
|
||||||
// This constructor is here to appease GCC's interpretation of
|
// This constructor is here to appease GCC's interpretation of
|
||||||
// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377
|
// an ambiguity in the C++ standard; cf. https://stackoverflow.com/questions/17430377
|
||||||
@ -116,9 +113,6 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
|
|||||||
uint16_t line = 0;
|
uint16_t line = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A pointer to the next thing that should be provided to the caller for data.
|
|
||||||
PointerSet write_pointers_;
|
|
||||||
|
|
||||||
/// A pointer to the final thing currently cleared for submission.
|
/// A pointer to the final thing currently cleared for submission.
|
||||||
std::atomic<PointerSet> submit_pointers_;
|
std::atomic<PointerSet> submit_pointers_;
|
||||||
|
|
||||||
@ -144,15 +138,17 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
|
|||||||
|
|
||||||
Metrics display_metrics_;
|
Metrics display_metrics_;
|
||||||
|
|
||||||
// Uses a texture to vend write areas.
|
|
||||||
std::vector<uint8_t> write_area_texture_;
|
|
||||||
size_t data_type_size_ = 0;
|
|
||||||
|
|
||||||
bool output_is_visible_ = false;
|
bool output_is_visible_ = false;
|
||||||
|
|
||||||
std::array<Line, LineBufferHeight> line_buffer_;
|
std::array<Line, LineBufferHeight> line_buffer_;
|
||||||
std::array<LineMetadata, LineBufferHeight> line_metadata_buffer_;
|
std::array<LineMetadata, LineBufferHeight> line_metadata_buffer_;
|
||||||
|
|
||||||
|
// Used by subclasses to set a new base address for the texture.
|
||||||
|
// When called this will flush all existing data and load up the
|
||||||
|
// new data size.
|
||||||
|
void set_write_area(uint8_t *base);
|
||||||
|
size_t write_area_data_size() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ScanTarget overrides.
|
// ScanTarget overrides.
|
||||||
void set_modals(Modals) final;
|
void set_modals(Modals) final;
|
||||||
@ -162,6 +158,18 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget {
|
|||||||
void end_data(size_t actual_length) final;
|
void end_data(size_t actual_length) final;
|
||||||
void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t colour_burst_amplitude) final;
|
void announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t colour_burst_amplitude) final;
|
||||||
void will_change_owner() final;
|
void will_change_owner() final;
|
||||||
|
|
||||||
|
/// A mutex for gettng access to write_pointers_; access to write_pointers_,
|
||||||
|
/// data_type_size_ or write_area_texture_ is almost never contended, so this
|
||||||
|
/// is cheap for the main use case.
|
||||||
|
std::mutex write_pointers_mutex_;
|
||||||
|
|
||||||
|
/// A pointer to the next thing that should be provided to the caller for data.
|
||||||
|
PointerSet write_pointers_;
|
||||||
|
|
||||||
|
// Uses a texture to vend write areas.
|
||||||
|
uint8_t *write_area_ = nullptr;
|
||||||
|
size_t data_type_size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user