mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 18:30:21 +00:00
Merge pull request #142 from TomHarte/BufferOverflow
Corrects a source of potential desynchronisation between the texture and geometry builders
This commit is contained in:
commit
6d941b0c1f
@ -259,7 +259,7 @@ template <class T> class MOS6560 {
|
||||
if(this_state_ != output_state_) {
|
||||
switch(output_state_) {
|
||||
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break;
|
||||
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0, 0); break;
|
||||
case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break;
|
||||
case State::Border: output_border(cycles_in_state_ * 4); break;
|
||||
case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break;
|
||||
}
|
||||
|
@ -8,9 +8,10 @@
|
||||
|
||||
#include "CRT.hpp"
|
||||
#include "CRTOpenGL.hpp"
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <cstdarg>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
using namespace Outputs::CRT;
|
||||
|
||||
@ -144,6 +145,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
||||
|
||||
if(next_run) {
|
||||
// output_y and texture locations will be written later; we won't necessarily know what it is outside of the locked region
|
||||
openGL_output_builder_.texture_builder.retain_latest();
|
||||
source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||
source_phase() = colour_burst_phase_;
|
||||
source_amplitude() = colour_burst_amplitude_;
|
||||
@ -192,9 +194,10 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
|
||||
output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position();
|
||||
}
|
||||
openGL_output_builder_.array_builder.flush(
|
||||
[output_y, this] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) {
|
||||
[=] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) {
|
||||
openGL_output_builder_.texture_builder.flush(
|
||||
[output_y, input_buffer] (const std::vector<TextureBuilder::WriteArea> &write_areas, size_t number_of_write_areas) {
|
||||
[=] (const std::vector<TextureBuilder::WriteArea> &write_areas, size_t number_of_write_areas) {
|
||||
assert(number_of_write_areas * SourceVertexSize == input_size);
|
||||
for(size_t run = 0; run < number_of_write_areas; run++) {
|
||||
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 0] = write_areas[run].x;
|
||||
*(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 2] = write_areas[run].y;
|
||||
@ -311,6 +314,7 @@ void CRT::output_blank(unsigned int number_of_cycles) {
|
||||
}
|
||||
|
||||
void CRT::output_level(unsigned int number_of_cycles) {
|
||||
openGL_output_builder_.texture_builder.reduce_previous_allocation_to(1);
|
||||
Scan scan {
|
||||
.type = Scan::Type::Level,
|
||||
.number_of_cycles = number_of_cycles,
|
||||
@ -329,13 +333,7 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint
|
||||
}
|
||||
|
||||
void CRT::output_default_colour_burst(unsigned int number_of_cycles) {
|
||||
Scan scan {
|
||||
.type = Scan::Type::ColourBurst,
|
||||
.number_of_cycles = number_of_cycles,
|
||||
.phase = (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)),
|
||||
.amplitude = 32
|
||||
};
|
||||
output_scan(&scan);
|
||||
output_colour_burst(number_of_cycles, (uint8_t)((phase_numerator_ * 256) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)));
|
||||
}
|
||||
|
||||
void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) {
|
||||
|
@ -187,7 +187,7 @@ class CRT {
|
||||
@param amplitude The amplitude of the colour burst in 1/256ths of the amplitude of the
|
||||
positive portion of the wave.
|
||||
*/
|
||||
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude);
|
||||
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude = 102);
|
||||
|
||||
/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
|
||||
|
||||
|
@ -39,7 +39,6 @@ TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) :
|
||||
write_areas_start_y_(0),
|
||||
is_full_(false),
|
||||
did_submit_(false),
|
||||
has_write_area_(false),
|
||||
number_of_write_areas_(0) {
|
||||
image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
|
||||
glGenTextures(1, &texture_name_);
|
||||
@ -74,7 +73,6 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) {
|
||||
starting_y = write_areas_[number_of_write_areas_ - 1].y;
|
||||
}
|
||||
|
||||
WriteArea next_write_area;
|
||||
if(starting_x + required_length + 2 > InputBufferBuilderWidth) {
|
||||
starting_x = 0;
|
||||
starting_y++;
|
||||
@ -85,33 +83,34 @@ uint8_t *TextureBuilder::allocate_write_area(size_t required_length) {
|
||||
}
|
||||
}
|
||||
|
||||
next_write_area.x = starting_x + 1;
|
||||
next_write_area.y = starting_y;
|
||||
next_write_area.length = (uint16_t)required_length;
|
||||
if(number_of_write_areas_ < write_areas_.size())
|
||||
write_areas_[number_of_write_areas_] = next_write_area;
|
||||
else
|
||||
write_areas_.push_back(next_write_area);
|
||||
number_of_write_areas_++;
|
||||
has_write_area_ = true;
|
||||
write_area_.x = starting_x + 1;
|
||||
write_area_.y = starting_y;
|
||||
write_area_.length = (uint16_t)required_length;
|
||||
|
||||
return pointer_to_location(next_write_area.x, next_write_area.y);
|
||||
return pointer_to_location(write_area_.x, write_area_.y);
|
||||
}
|
||||
|
||||
bool TextureBuilder::is_full() {
|
||||
return is_full_;
|
||||
}
|
||||
|
||||
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) {
|
||||
if(is_full_ || !has_write_area_) return;
|
||||
void TextureBuilder::retain_latest() {
|
||||
if(is_full_) return;
|
||||
if(number_of_write_areas_ < write_areas_.size())
|
||||
write_areas_[number_of_write_areas_] = write_area_;
|
||||
else
|
||||
write_areas_.push_back(write_area_);
|
||||
number_of_write_areas_++;
|
||||
}
|
||||
|
||||
has_write_area_ = false;
|
||||
WriteArea &write_area = write_areas_[number_of_write_areas_-1];
|
||||
write_area.length = (uint16_t)actual_length;
|
||||
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) {
|
||||
if(is_full_) return;
|
||||
|
||||
write_area_.length = (uint16_t)actual_length;
|
||||
|
||||
// book end the allocation with duplicates of the first and last pixel, to protect
|
||||
// against rounding errors when this run is drawn
|
||||
uint8_t *start_pointer = pointer_to_location(write_area.x, write_area.y);
|
||||
uint8_t *start_pointer = pointer_to_location(write_area_.x, write_area_.y);
|
||||
memcpy( &start_pointer[-bytes_per_pixel_],
|
||||
start_pointer,
|
||||
bytes_per_pixel_);
|
||||
@ -172,6 +171,5 @@ void TextureBuilder::flush(const std::function<void(const std::vector<WriteArea>
|
||||
}
|
||||
|
||||
did_submit_ = false;
|
||||
has_write_area_ = false;
|
||||
number_of_write_areas_ = 0;
|
||||
}
|
||||
|
@ -24,6 +24,32 @@ namespace CRT {
|
||||
Owns an OpenGL texture resource and provides mechanisms to fill it from bottom left to top right
|
||||
with runs of data, ensuring each run is neighboured immediately to the left and right by copies of its
|
||||
first and last pixels.
|
||||
|
||||
Intended usage:
|
||||
|
||||
(i) allocate a write area with allocate_write_area, supplying a maximum size.
|
||||
(ii) call reduce_previous_allocation_to to announce the actual size written.
|
||||
|
||||
This will cause you to have added source data to the target texture. You can then either use that data
|
||||
or allow it to expire.
|
||||
|
||||
(iii) call retain_latest to add the most recently written write area to the flush queue.
|
||||
|
||||
The flush queue contains provisional data, that can sit in the CPU's memory space indefinitely. This facility
|
||||
is provided because it is expected that a texture will be built alontside some other collection of data —
|
||||
that data in the flush queue is expected to become useful in coordination with something else but should
|
||||
be retained at least until then.
|
||||
|
||||
(iv) call flush to move data to the submit queue.
|
||||
|
||||
When you flush, you'll receive a record of the bounds of all newly-flushed areas of source data. That gives
|
||||
an opportunity to correlate the data with whatever else it is being tied to. It will continue to sit in
|
||||
the CPU's memory space but has now passed beyond any further modification or reporting.
|
||||
|
||||
(v) call submit to move data to the GPU and free up its CPU-side resources.
|
||||
|
||||
The data is now on the GPU, for whatever use the caller desires.
|
||||
|
||||
*/
|
||||
class TextureBuilder {
|
||||
public:
|
||||
@ -41,6 +67,9 @@ class TextureBuilder {
|
||||
/// and indicates that its actual final size was @c actual_length.
|
||||
void reduce_previous_allocation_to(size_t actual_length);
|
||||
|
||||
/// Allocated runs are provisional; they will not appear in the next flush queue unless retained.
|
||||
void retain_latest();
|
||||
|
||||
/// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture
|
||||
/// being full; @c false if calls may succeed.
|
||||
bool is_full();
|
||||
@ -64,12 +93,14 @@ class TextureBuilder {
|
||||
std::vector<uint8_t> image_;
|
||||
GLuint texture_name_;
|
||||
|
||||
// the current list of write areas
|
||||
// the current write area
|
||||
WriteArea write_area_;
|
||||
|
||||
// the list of write areas that have ascended to the flush queue
|
||||
std::vector<WriteArea> write_areas_;
|
||||
size_t number_of_write_areas_;
|
||||
bool is_full_;
|
||||
bool did_submit_;
|
||||
bool has_write_area_;
|
||||
inline uint8_t *pointer_to_location(uint16_t x, uint16_t y);
|
||||
|
||||
// Usually: the start position for the current batch of write areas.
|
||||
|
Loading…
Reference in New Issue
Block a user