diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index 6913cec15..d6a99467c 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -79,6 +79,9 @@ ScanTarget::ScanTarget(GLuint target_framebuffer, float output_gamma) : unprocessed_line_texture_(LineBufferWidth, LineBufferHeight, UnprocessedLineBufferTextureUnit, GL_NEAREST, false), full_display_rectangle_(-1.0f, -1.0f, 2.0f, 2.0f) { + set_scan_buffer(scan_buffer_.data(), scan_buffer_.size()); + set_line_buffer(line_buffer_.data(), line_metadata_buffer_.data(), line_buffer_.size()); + // Allocate space for the scans and lines. allocate_buffer(scan_buffer_, scan_buffer_name_, scan_vertex_array_); allocate_buffer(line_buffer_, line_buffer_name_, line_vertex_array_); @@ -253,7 +256,7 @@ void ScanTarget::update(int, int output_height) { 1 + area.end.write_area_y - area.start.write_area_y, formatForDepth(write_area_data_size()), GL_UNSIGNED_BYTE, - &write_area_texture_[size_t(TextureAddress(0, area.start.write_area_y)) * write_area_data_size()]); + &write_area_texture_[size_t(area.start.write_area_y * WriteAreaWidth) * write_area_data_size()]); } else { // 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. @@ -264,7 +267,7 @@ void ScanTarget::update(int, int output_height) { WriteAreaHeight - area.start.write_area_y, formatForDepth(write_area_data_size()), GL_UNSIGNED_BYTE, - &write_area_texture_[size_t(TextureAddress(0, area.start.write_area_y)) * write_area_data_size()]); + &write_area_texture_[size_t(area.start.write_area_y * WriteAreaWidth) * write_area_data_size()]); test_gl(glTexSubImage2D, GL_TEXTURE_2D, 0, 0, 0, diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 6318efd11..5d8ee0437 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -51,6 +51,9 @@ class ScanTarget: public Outputs::Display::BufferingScanTarget { void update(int output_width, int output_height); private: + static constexpr int LineBufferWidth = 2048; + static constexpr int LineBufferHeight = 2048; + #ifndef NDEBUG struct OpenGLVersionDumper { OpenGLVersionDumper() { @@ -146,8 +149,11 @@ class ScanTarget: public Outputs::Display::BufferingScanTarget { */ bool is_soft_display_type(); - // Storage for the write area. + // Storage for the various buffers. std::vector write_area_texture_; + std::array scan_buffer_; + std::array line_buffer_; + std::array line_metadata_buffer_; }; } diff --git a/Outputs/ScanTargets/BufferingScanTarget.cpp b/Outputs/ScanTargets/BufferingScanTarget.cpp index 34800005e..e8344653a 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.cpp +++ b/Outputs/ScanTargets/BufferingScanTarget.cpp @@ -14,6 +14,7 @@ #define TextureAddressGetY(v) uint16_t((v) >> 11) #define TextureAddressGetX(v) uint16_t((v) & 0x7ff) #define TextureSub(a, b) (((a) - (b)) & 0x3fffff) +#define TextureAddress(x, y) (((y) << 11) | (x)) using namespace Outputs::Display; @@ -162,7 +163,7 @@ void BufferingScanTarget::announce(Event event, bool is_visible, const Outputs:: } // Attempt to allocate a new line; note allocation failure if necessary. - const auto next_line = uint16_t((write_pointers_.line + 1) % LineBufferHeight); + const auto next_line = uint16_t((write_pointers_.line + 1) % line_buffer_size_); if(next_line == read_pointers.line) { allocation_has_failed_ = true; active_line_ = nullptr; @@ -229,7 +230,7 @@ Outputs::Display::ScanTarget::Scan *BufferingScanTarget::begin_scan() { const auto read_pointers = read_pointers_.load(); // Advance the pointer. - const auto next_write_pointer = decltype(write_pointers_.scan_buffer)((write_pointers_.scan_buffer + 1) % scan_buffer_.size()); + const auto next_write_pointer = decltype(write_pointers_.scan_buffer)((write_pointers_.scan_buffer + 1) % scan_buffer_size_); // Check whether that's too many. if(next_write_pointer == read_pointers.scan_buffer) { @@ -298,3 +299,14 @@ void BufferingScanTarget::perform(const std::function &function) { function(); is_updating_.clear(std::memory_order_release); } + +void BufferingScanTarget::set_scan_buffer(Scan *buffer, size_t size) { + scan_buffer_ = buffer; + scan_buffer_size_ = size; +} + +void BufferingScanTarget::set_line_buffer(Line *line_buffer, LineMetadata *metadata_buffer, size_t size) { + line_buffer_ = line_buffer; + line_metadata_buffer_ = metadata_buffer; + line_buffer_size_ = size; +} diff --git a/Outputs/ScanTargets/BufferingScanTarget.hpp b/Outputs/ScanTargets/BufferingScanTarget.hpp index d2715af4e..d5897faf7 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.hpp +++ b/Outputs/ScanTargets/BufferingScanTarget.hpp @@ -18,8 +18,6 @@ #include #include -#define TextureAddress(x, y) (((y) << 11) | (x)) - namespace Outputs { namespace Display { @@ -45,9 +43,6 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget { static constexpr int WriteAreaWidth = 2048; static constexpr int WriteAreaHeight = 2048; - static constexpr int LineBufferWidth = 2048; - static constexpr int LineBufferHeight = 2048; - BufferingScanTarget(); // This is included because it's assumed that scan targets will want to expose one. @@ -93,22 +88,23 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget { bool previous_frame_was_complete; }; + /// Sets the area of memory to use as a scan buffer. + void set_scan_buffer(Scan *buffer, size_t size); + + /// Sets the area of memory to use as line and line metadata buffers. + void set_line_buffer(Line *line_buffer, LineMetadata *metadata_buffer, size_t size); + // These are safe to read only within a `perform` block. // TODO: can I do better than that? Modals modals_; bool modals_are_dirty_ = false; - /// Maintains a buffer of the most recent scans. - // TODO: have the owner supply buffers and sizes. - // That'll allow owners to place this in shared video memory if possible. - std::array scan_buffer_; - std::array line_buffer_; - std::array 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. + /// Sets 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); + + /// @returns The number of bytes per input sample, as per the latest modals. size_t write_area_data_size() const; /// Defines a segment of data now ready for output, consisting of start and endpoints for: @@ -130,9 +126,12 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget { Endpoint start, end; }; - void perform(const std::function &); - void perform(const std::function &); + /// Performs @c action ensuring that no other @c perform actions, or any + /// change to modals, occurs simultaneously. + void perform(const std::function &action); + /// Acts as per void(void) @c perform but also dequeues all latest available video output. + void perform(const std::function &); private: // ScanTarget overrides. @@ -205,6 +204,15 @@ class BufferingScanTarget: public Outputs::Display::ScanTarget { /// A pointer to the next thing that should be provided to the caller for data. PointerSet write_pointers_; + + // The owner-supplied scan buffer and size. + Scan *scan_buffer_ = nullptr; + size_t scan_buffer_size_ = 0; + + // The owner-supplied line buffer and size. + Line *line_buffer_ = nullptr; + LineMetadata *line_metadata_buffer_ = nullptr; + size_t line_buffer_size_ = 0; };