mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-12 15:31:09 +00:00
Merge pull request #550 from TomHarte/ElectronCorruption
Resolves potential Electron output errors
This commit is contained in:
commit
3b70dbfebe
@ -69,7 +69,7 @@ template <class BusHandler> class MOS6560 {
|
||||
speaker_(audio_generator_)
|
||||
{
|
||||
crt_->set_svideo_sampling_function(
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
|
||||
|
||||
|
@ -92,7 +92,7 @@ TMS9918::TMS9918(Personality p) {
|
||||
// Unimaginatively, this class just passes RGB through to the shader. Investigation is needed
|
||||
// into whether there's a more natural form.
|
||||
crt_->set_rgb_sampling_function(
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
|
||||
"{"
|
||||
"return texture(sampler, coordinate).rgb / vec3(255.0);"
|
||||
"}");
|
||||
|
@ -327,7 +327,7 @@ class CRTCBusHandler {
|
||||
void setup_output(float aspect_ratio) {
|
||||
crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::CRT::DisplayType::PAL50, 1));
|
||||
crt_->set_rgb_sampling_function(
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
|
||||
"{"
|
||||
"uint sample = texture(texID, coordinate).r;"
|
||||
"return vec3(float((sample >> 4) & 3u), float((sample >> 2) & 3u), float(sample & 3u)) / 2.0;"
|
||||
|
@ -18,7 +18,7 @@ VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
|
||||
// Set a composite sampling function that assumes one byte per pixel input, and
|
||||
// accepts any non-zero value as being fully on, zero being fully off.
|
||||
crt_->set_composite_sampling_function(
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)"
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"return clamp(texture(sampler, coordinate).r, 0.0, 1.0);"
|
||||
"}");
|
||||
|
@ -124,7 +124,7 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) {
|
||||
|
||||
if(output_mode == OutputMode::NTSC) {
|
||||
crt_->set_svideo_sampling_function(
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"uint c = texture(texID, coordinate).r;"
|
||||
"uint y = c & 14u;"
|
||||
@ -136,7 +136,7 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) {
|
||||
display_type = Outputs::CRT::DisplayType::NTSC60;
|
||||
} else {
|
||||
crt_->set_svideo_sampling_function(
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"uint c = texture(texID, coordinate).r;"
|
||||
"uint y = c & 14u;"
|
||||
|
@ -34,13 +34,6 @@ namespace {
|
||||
static const int real_time_clock_interrupt_2 = 56704;
|
||||
static const int display_end_interrupt_1 = (first_graphics_line + display_end_interrupt_line)*cycles_per_line;
|
||||
static const int display_end_interrupt_2 = (first_graphics_line + field_divider_line + display_end_interrupt_line)*cycles_per_line;
|
||||
|
||||
struct FourBPPBookender: public Outputs::CRT::TextureBuilder::Bookender {
|
||||
void add_bookends(uint8_t *const left_value, uint8_t *const right_value, uint8_t *left_bookend, uint8_t *right_bookend) {
|
||||
*left_bookend = static_cast<uint8_t>(((*left_value) & 0x0f) | (((*left_value) & 0x0f) << 4));
|
||||
*right_bookend = static_cast<uint8_t>(((*right_value) & 0xf0) | (((*right_value) & 0xf0) >> 4));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
||||
@ -52,15 +45,11 @@ VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory) {
|
||||
|
||||
crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1));
|
||||
crt_->set_rgb_sampling_function(
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
|
||||
"{"
|
||||
"uint texValue = texture(sampler, coordinate).r;"
|
||||
"texValue >>= 4 - (int(icoordinate.x) & 4);"
|
||||
"return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
|
||||
"}");
|
||||
crt_->set_integer_coordinate_multiplier(8.0f);
|
||||
std::unique_ptr<Outputs::CRT::TextureBuilder::Bookender> bookender(new FourBPPBookender);
|
||||
crt_->set_bookender(std::move(bookender));
|
||||
// TODO: as implied below, I've introduced a clock's latency into the graphics pipeline somehow. Investigate.
|
||||
crt_->set_visible_area(crt_->get_rect_for_area(first_graphics_line - 1, 256, (first_graphics_cycle+1) * crt_cycles_multiplier, 80 * crt_cycles_multiplier, 4.0f / 3.0f));
|
||||
}
|
||||
@ -113,9 +102,9 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
} else {
|
||||
unsigned int divider = 1;
|
||||
switch(screen_mode_) {
|
||||
case 0: case 3: divider = 2; break;
|
||||
case 1: case 4: case 6: divider = 4; break;
|
||||
case 2: case 5: divider = 8; break;
|
||||
case 0: case 3: divider = 1; break;
|
||||
case 1: case 4: case 6: divider = 2; break;
|
||||
case 2: case 5: divider = 4; break;
|
||||
}
|
||||
|
||||
if(!initial_output_target_ || divider != current_output_divider_) {
|
||||
@ -124,7 +113,7 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
crt_->output_data(data_length * current_output_divider_, data_length);
|
||||
}
|
||||
current_output_divider_ = divider;
|
||||
initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 4);
|
||||
initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 8 / divider);
|
||||
}
|
||||
|
||||
#define get_pixel() \
|
||||
@ -139,8 +128,8 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
if(initial_output_target_) {
|
||||
while(number_of_cycles--) {
|
||||
get_pixel();
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.eighty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
*reinterpret_cast<uint64_t *>(current_output_target_) = palette_tables_.eighty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 8;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
} else current_output_target_ += 4*number_of_cycles;
|
||||
@ -150,8 +139,8 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
if(initial_output_target_) {
|
||||
while(number_of_cycles--) {
|
||||
get_pixel();
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.eighty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.eighty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
} else current_output_target_ += 2*number_of_cycles;
|
||||
@ -161,8 +150,8 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
if(initial_output_target_) {
|
||||
while(number_of_cycles--) {
|
||||
get_pixel();
|
||||
*current_output_target_ = palette_tables_.eighty4bpp[last_pixel_byte_];
|
||||
current_output_target_ += 1;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.eighty4bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
} else current_output_target_ += number_of_cycles;
|
||||
@ -172,28 +161,28 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
if(initial_output_target_) {
|
||||
if(current_pixel_column_&1) {
|
||||
last_pixel_byte_ <<= 4;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
|
||||
number_of_cycles--;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
while(number_of_cycles > 1) {
|
||||
get_pixel();
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
|
||||
last_pixel_byte_ <<= 4;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
|
||||
number_of_cycles -= 2;
|
||||
current_pixel_column_+=2;
|
||||
}
|
||||
if(number_of_cycles) {
|
||||
get_pixel();
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
*reinterpret_cast<uint32_t *>(current_output_target_) = palette_tables_.forty1bpp[last_pixel_byte_];
|
||||
current_output_target_ += 4;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
} else current_output_target_ += 2 * number_of_cycles;
|
||||
@ -203,28 +192,28 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) {
|
||||
if(initial_output_target_) {
|
||||
if(current_pixel_column_&1) {
|
||||
last_pixel_byte_ <<= 2;
|
||||
*current_output_target_ = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 1;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
|
||||
number_of_cycles--;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
while(number_of_cycles > 1) {
|
||||
get_pixel();
|
||||
*current_output_target_ = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 1;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
|
||||
last_pixel_byte_ <<= 2;
|
||||
*current_output_target_ = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 1;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
|
||||
number_of_cycles -= 2;
|
||||
current_pixel_column_+=2;
|
||||
}
|
||||
if(number_of_cycles) {
|
||||
get_pixel();
|
||||
*current_output_target_ = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 1;
|
||||
*reinterpret_cast<uint16_t *>(current_output_target_) = palette_tables_.forty2bpp[last_pixel_byte_];
|
||||
current_output_target_ += 2;
|
||||
current_pixel_column_++;
|
||||
}
|
||||
} else current_output_target_ += number_of_cycles;
|
||||
@ -310,27 +299,37 @@ void VideoOutput::set_register(int address, uint8_t value) {
|
||||
}
|
||||
|
||||
// regenerate all palette tables for now
|
||||
#define pack(a, b) static_cast<uint8_t>((a << 4) | (b))
|
||||
for(int byte = 0; byte < 256; byte++) {
|
||||
uint8_t *target = reinterpret_cast<uint8_t *>(&palette_tables_.forty1bpp[byte]);
|
||||
target[0] = pack(palette_[(byte&0x80) >> 4], palette_[(byte&0x40) >> 3]);
|
||||
target[1] = pack(palette_[(byte&0x20) >> 2], palette_[(byte&0x10) >> 1]);
|
||||
target[0] = palette_[(byte&0x80) >> 4];
|
||||
target[1] = palette_[(byte&0x40) >> 3];
|
||||
target[2] = palette_[(byte&0x20) >> 2];
|
||||
target[3] = palette_[(byte&0x10) >> 1];
|
||||
|
||||
target = reinterpret_cast<uint8_t *>(&palette_tables_.eighty2bpp[byte]);
|
||||
target[0] = pack(palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)], palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)]);
|
||||
target[1] = pack(palette_[((byte&0x20) >> 2) | ((byte&0x02) >> 0)], palette_[((byte&0x10) >> 1) | ((byte&0x01) << 1)]);
|
||||
target[0] = palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)];
|
||||
target[1] = palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)];
|
||||
target[2] = palette_[((byte&0x20) >> 2) | ((byte&0x02) >> 0)];
|
||||
target[3] = palette_[((byte&0x10) >> 1) | ((byte&0x01) << 1)];
|
||||
|
||||
target = reinterpret_cast<uint8_t *>(&palette_tables_.eighty1bpp[byte]);
|
||||
target[0] = pack(palette_[(byte&0x80) >> 4], palette_[(byte&0x40) >> 3]);
|
||||
target[1] = pack(palette_[(byte&0x20) >> 2], palette_[(byte&0x10) >> 1]);
|
||||
target[2] = pack(palette_[(byte&0x08) >> 0], palette_[(byte&0x04) << 1]);
|
||||
target[3] = pack(palette_[(byte&0x02) << 2], palette_[(byte&0x01) << 3]);
|
||||
target[0] = palette_[(byte&0x80) >> 4];
|
||||
target[1] = palette_[(byte&0x40) >> 3];
|
||||
target[2] = palette_[(byte&0x20) >> 2];
|
||||
target[3] = palette_[(byte&0x10) >> 1];
|
||||
target[4] = palette_[(byte&0x08) >> 0];
|
||||
target[5] = palette_[(byte&0x04) << 1];
|
||||
target[6] = palette_[(byte&0x02) << 2];
|
||||
target[7] = palette_[(byte&0x01) << 3];
|
||||
|
||||
palette_tables_.forty2bpp[byte] = pack( palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)], palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)]);
|
||||
palette_tables_.eighty4bpp[byte] = pack( palette_[((byte&0x80) >> 4) | ((byte&0x20) >> 3) | ((byte&0x08) >> 2) | ((byte&0x02) >> 1)],
|
||||
palette_[((byte&0x40) >> 3) | ((byte&0x10) >> 2) | ((byte&0x04) >> 1) | ((byte&0x01) >> 0)]);
|
||||
target = reinterpret_cast<uint8_t *>(&palette_tables_.forty2bpp[byte]);
|
||||
target[0] = palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)];
|
||||
target[1] = palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)];
|
||||
|
||||
target = reinterpret_cast<uint8_t *>(&palette_tables_.eighty4bpp[byte]);
|
||||
target[0] = palette_[((byte&0x80) >> 4) | ((byte&0x20) >> 3) | ((byte&0x08) >> 2) | ((byte&0x02) >> 1)];
|
||||
target[1] = palette_[((byte&0x40) >> 3) | ((byte&0x10) >> 2) | ((byte&0x04) >> 1) | ((byte&0x01) >> 0)];
|
||||
}
|
||||
#undef pack
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -90,11 +90,11 @@ class VideoOutput {
|
||||
|
||||
uint8_t *ram_;
|
||||
struct {
|
||||
uint16_t forty1bpp[256];
|
||||
uint8_t forty2bpp[256];
|
||||
uint32_t eighty1bpp[256];
|
||||
uint16_t eighty2bpp[256];
|
||||
uint8_t eighty4bpp[256];
|
||||
uint32_t forty1bpp[256];
|
||||
uint16_t forty2bpp[256];
|
||||
uint64_t eighty1bpp[256];
|
||||
uint32_t eighty2bpp[256];
|
||||
uint16_t eighty4bpp[256];
|
||||
} palette_tables_;
|
||||
|
||||
// Display generation.
|
||||
|
@ -25,13 +25,13 @@ VideoOutput::VideoOutput(uint8_t *memory) :
|
||||
v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition),
|
||||
counter_period_(PAL50Period) {
|
||||
crt_->set_rgb_sampling_function(
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
|
||||
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
|
||||
"{"
|
||||
"uint texValue = texture(sampler, coordinate).r;"
|
||||
"return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
|
||||
"}");
|
||||
crt_->set_composite_sampling_function(
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)"
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"uint texValue = uint(dot(texture(sampler, coordinate).rg, uvec2(1, 256)));"
|
||||
"uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;"
|
||||
|
@ -26,7 +26,7 @@ Video::Video() :
|
||||
// Set a composite sampling function that assumes two-level input; either a byte is 0, which is black,
|
||||
// or it is non-zero, which is white.
|
||||
crt_->set_composite_sampling_function(
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)"
|
||||
"float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"return texture(sampler, coordinate).r;"
|
||||
"}");
|
||||
|
@ -280,7 +280,7 @@ class CRT {
|
||||
/*! Sets a function that will map from whatever data the machine provided to a composite signal.
|
||||
|
||||
@param shader A GLSL fragment including a function with the signature
|
||||
`float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)`
|
||||
`float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)`
|
||||
that evaluates to the composite signal level as a function of a source buffer, sampling location, colour
|
||||
carrier phase and amplitude.
|
||||
*/
|
||||
@ -290,22 +290,6 @@ class CRT {
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets a multiplier applied to iCoordinate values prior to their passing to the various sampling functions.
|
||||
This multiplier is applied outside of the interpolation loop, making for a more precise interpolation
|
||||
than if it were applied within the sampling function.
|
||||
|
||||
Idiomatically, this is likely to be the number of output pixels packed into each input sample where
|
||||
packing is in use.
|
||||
|
||||
The default value is 1.0.
|
||||
*/
|
||||
inline void set_integer_coordinate_multiplier(float multiplier) {
|
||||
enqueue_openGL_function([=] {
|
||||
openGL_output_builder_.set_integer_coordinate_multiplier(multiplier);
|
||||
});
|
||||
}
|
||||
|
||||
enum CompositeSourceType {
|
||||
/// The composite function provides continuous output.
|
||||
Continuous,
|
||||
@ -362,10 +346,6 @@ class CRT {
|
||||
});
|
||||
}
|
||||
|
||||
inline void set_bookender(std::unique_ptr<TextureBuilder::Bookender> bookender) {
|
||||
openGL_output_builder_.texture_builder.set_bookender(std::move(bookender));
|
||||
}
|
||||
|
||||
inline void set_video_signal(VideoSignal video_signal) {
|
||||
enqueue_openGL_function([video_signal, this] {
|
||||
openGL_output_builder_.set_video_signal(video_signal);
|
||||
|
@ -524,12 +524,4 @@ void OpenGLOutputBuilder::set_timing_uniforms() {
|
||||
if(rgb_input_shader_program_) {
|
||||
rgb_input_shader_program_->set_width_scalers(1.0f, 1.0f);
|
||||
}
|
||||
set_integer_coordinate_multiplier(integer_coordinate_multiplier_);
|
||||
}
|
||||
|
||||
void OpenGLOutputBuilder::set_integer_coordinate_multiplier(float multiplier) {
|
||||
integer_coordinate_multiplier_ = multiplier;
|
||||
if(composite_input_shader_program_) composite_input_shader_program_->set_integer_coordinate_multiplier(multiplier);
|
||||
if(svideo_input_shader_program_) svideo_input_shader_program_->set_integer_coordinate_multiplier(multiplier);
|
||||
if(rgb_input_shader_program_) rgb_input_shader_program_->set_integer_coordinate_multiplier(multiplier);
|
||||
}
|
||||
|
@ -105,8 +105,6 @@ class OpenGLOutputBuilder {
|
||||
float get_composite_output_width() const;
|
||||
void set_output_shader_width();
|
||||
|
||||
float integer_coordinate_multiplier_ = 1.0f;
|
||||
|
||||
// Maintain a couple of rectangles for masking off the extreme edge of the display;
|
||||
// this is a bit of a cheat: there's some tolerance in when a sync pulse will be
|
||||
// generated. So it might be slightly later than expected. Which might cause a scan
|
||||
@ -174,7 +172,6 @@ class OpenGLOutputBuilder {
|
||||
void set_rgb_sampling_function(const std::string &);
|
||||
void set_video_signal(VideoSignal);
|
||||
void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider);
|
||||
void set_integer_coordinate_multiplier(float multiplier);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -46,11 +46,9 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const std::s
|
||||
"uniform float inputVerticalOffset;"
|
||||
"uniform float outputVerticalOffset;"
|
||||
"uniform float textureHeightDivisor;"
|
||||
"uniform float iCoordinateMultiplier;"
|
||||
|
||||
"out vec3 phaseAndAmplitudeVarying;"
|
||||
"out vec2 inputPositionsVarying[11];"
|
||||
"out vec2 iInputPositionVarying;"
|
||||
"out vec2 delayLinePositionVarying;"
|
||||
|
||||
"void main(void)"
|
||||
@ -75,9 +73,8 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const std::s
|
||||
"vec2 extendedInputPosition = " << (input_is_inputPosition ? "inputPosition" : "outputPosition") << " + extensionVector;"
|
||||
"vec2 extendedOutputPosition = outputPosition + extensionVector;"
|
||||
|
||||
// keep iInputPositionVarying in whole source pixels, scale mappedInputPosition to the ordinary normalised range
|
||||
// scale mappedInputPosition to the ordinary normalised range
|
||||
"vec2 textureSize = vec2(textureSize(texID, 0));"
|
||||
"iInputPositionVarying = extendedInputPosition * iCoordinateMultiplier;"
|
||||
"vec2 mappedInputPosition = extendedInputPosition / textureSize;"
|
||||
|
||||
// setup input positions spaced as per the supplied offsets; these are for filtering where required
|
||||
@ -120,7 +117,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_composite_source_sh
|
||||
"#version 150\n"
|
||||
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"in vec2 iInputPositionVarying;"
|
||||
"in vec3 phaseAndAmplitudeVarying;"
|
||||
|
||||
"out vec4 fragColour;"
|
||||
@ -132,18 +128,18 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_composite_source_sh
|
||||
if(!svideo_shader.empty()) {
|
||||
fragment_shader <<
|
||||
svideo_shader <<
|
||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"vec2 svideoColour = svideo_sample(texID, coordinate, iCoordinate, phase, amplitude);"
|
||||
"vec2 svideoColour = svideo_sample(texID, coordinate, phase, amplitude);"
|
||||
"return mix(svideoColour.x, svideoColour.y, abs(amplitude));"
|
||||
"}";
|
||||
} else {
|
||||
fragment_shader <<
|
||||
rgb_shader <<
|
||||
"uniform mat3 rgbToLumaChroma;"
|
||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"float composite_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
|
||||
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate), vec3(0.0), vec3(1.0));"
|
||||
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
|
||||
"vec2 quadrature = vec2(cos(phase), sin(phase)) * vec2(abs(amplitude), amplitude);"
|
||||
"return dot(lumaChromaColour, vec3(1.0 - abs(amplitude), quadrature));"
|
||||
@ -154,7 +150,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_composite_source_sh
|
||||
fragment_shader <<
|
||||
"void main(void)"
|
||||
"{"
|
||||
"fragColour = vec4(composite_sample(texID, inputPositionsVarying[5], iInputPositionVarying, phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y));"
|
||||
"fragColour = vec4(composite_sample(texID, inputPositionsVarying[5], phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y));"
|
||||
"}";
|
||||
|
||||
return make_shader(fragment_shader.str(), true, true);
|
||||
@ -166,7 +162,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_svideo_source_shade
|
||||
"#version 150\n"
|
||||
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"in vec2 iInputPositionVarying;"
|
||||
"in vec3 phaseAndAmplitudeVarying;"
|
||||
|
||||
"out vec3 fragColour;"
|
||||
@ -178,9 +173,9 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_svideo_source_shade
|
||||
fragment_shader
|
||||
<< rgb_shader <<
|
||||
"uniform mat3 rgbToLumaChroma;"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, float phase, float amplitude)"
|
||||
"{"
|
||||
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
|
||||
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate), vec3(0.0), vec3(1.0));"
|
||||
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
|
||||
"vec2 quadrature = vec2(cos(phase), sin(phase)) * vec2(1.0, sign(amplitude));"
|
||||
"return vec2(lumaChromaColour.x, 0.5 + dot(quadrature, lumaChromaColour.yz) * 0.5);"
|
||||
@ -190,7 +185,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_svideo_source_shade
|
||||
fragment_shader <<
|
||||
"void main(void)"
|
||||
"{"
|
||||
"vec2 sample = svideo_sample(texID, inputPositionsVarying[5], iInputPositionVarying, phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y);"
|
||||
"vec2 sample = svideo_sample(texID, inputPositionsVarying[5], phaseAndAmplitudeVarying.x, phaseAndAmplitudeVarying.y);"
|
||||
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), sin(phaseAndAmplitudeVarying.x)) * vec2(1.0, sign(phaseAndAmplitudeVarying.y)) * 0.5 * phaseAndAmplitudeVarying.z;"
|
||||
"fragColour = vec3(sample.x, vec2(0.5) + (sample.y * quadrature));"
|
||||
"}";
|
||||
@ -204,7 +199,6 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_source_shader(c
|
||||
"#version 150\n"
|
||||
|
||||
"in vec2 inputPositionsVarying[11];"
|
||||
"in vec2 iInputPositionVarying;"
|
||||
"in vec3 phaseAndAmplitudeVarying;"
|
||||
|
||||
"out vec3 fragColour;"
|
||||
@ -215,7 +209,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_source_shader(c
|
||||
|
||||
"void main(void)"
|
||||
"{"
|
||||
"fragColour = rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);"
|
||||
"fragColour = rgb_sample(texID, inputPositionsVarying[5]);"
|
||||
"}";
|
||||
|
||||
return make_shader(fragment_shader.str(), true, true);
|
||||
@ -439,7 +433,3 @@ void IntermediateShader::set_is_double_height(bool is_double_height, float input
|
||||
set_uniform("inputVerticalOffset", input_offset);
|
||||
set_uniform("outputVerticalOffset", output_offset);
|
||||
}
|
||||
|
||||
void IntermediateShader::set_integer_coordinate_multiplier(float multiplier) {
|
||||
set_uniform("iCoordinateMultiplier", multiplier);
|
||||
}
|
||||
|
@ -135,11 +135,6 @@ public:
|
||||
*/
|
||||
void set_is_double_height(bool is_double_height, float input_offset = 0.0f, float output_offset = 0.0f);
|
||||
|
||||
/*!
|
||||
Sets the multiplier applied in the vertex shader to iCoordinates.
|
||||
*/
|
||||
void set_integer_coordinate_multiplier(float);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<IntermediateShader> make_shader(const std::string &fragment_shader, bool use_usampler, bool input_is_inputPosition);
|
||||
};
|
||||
|
@ -36,19 +36,6 @@ const GLenum formatForDepth(std::size_t depth) {
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultBookender: public TextureBuilder::Bookender {
|
||||
public:
|
||||
DefaultBookender(std::size_t bytes_per_pixel) : bytes_per_pixel_(bytes_per_pixel) {}
|
||||
|
||||
void add_bookends(uint8_t *const left_value, uint8_t *const right_value, uint8_t *left_bookend, uint8_t *right_bookend) {
|
||||
std::memcpy(left_bookend, left_value, bytes_per_pixel_);
|
||||
std::memcpy(right_bookend, right_value, bytes_per_pixel_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t bytes_per_pixel_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TextureBuilder::TextureBuilder(std::size_t bytes_per_pixel, GLenum texture_unit) :
|
||||
@ -58,8 +45,6 @@ TextureBuilder::TextureBuilder(std::size_t bytes_per_pixel, GLenum texture_unit)
|
||||
|
||||
bind();
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr);
|
||||
|
||||
set_bookender(nullptr);
|
||||
}
|
||||
|
||||
TextureBuilder::~TextureBuilder() {
|
||||
@ -119,17 +104,9 @@ void TextureBuilder::reduce_previous_allocation_to(std::size_t actual_length) {
|
||||
|
||||
// Bookend the allocation with duplicates of the first and last pixel, to protect
|
||||
// against rounding errors when this run is drawn.
|
||||
// TODO: allow somebody else to specify the rule for generating a left-padding value and
|
||||
// a right-padding value.
|
||||
uint8_t *start_pointer = pointer_to_location(write_area_.x, write_area_.y) - bytes_per_pixel_;
|
||||
bookender_->add_bookends(&start_pointer[bytes_per_pixel_], &start_pointer[actual_length * bytes_per_pixel_], start_pointer, &start_pointer[(actual_length + 1) * bytes_per_pixel_]);
|
||||
}
|
||||
|
||||
void TextureBuilder::set_bookender(std::unique_ptr<TextureBuilder::Bookender> bookender) {
|
||||
bookender_ = std::move(bookender);
|
||||
if(!bookender_) {
|
||||
bookender_.reset(new DefaultBookender(bytes_per_pixel_));
|
||||
}
|
||||
std::memcpy(start_pointer, &start_pointer[bytes_per_pixel_], bytes_per_pixel_);
|
||||
std::memcpy(&start_pointer[(actual_length + 1) * bytes_per_pixel_], &start_pointer[actual_length * bytes_per_pixel_], bytes_per_pixel_);
|
||||
}
|
||||
|
||||
bool TextureBuilder::retain_latest() {
|
||||
|
@ -98,22 +98,6 @@ class TextureBuilder {
|
||||
/// allocated, indicating their final resting locations and their lengths.
|
||||
void flush(const std::function<void(const std::vector<WriteArea> &write_areas, std::size_t count)> &);
|
||||
|
||||
/// A Bookender helps to paper over precision errors when rendering; its job is to provide single-sample
|
||||
/// extensions that duplicate the left and right edges of a written area. By default the texture builder will
|
||||
/// simply copy the appropriate number of bytes per pixel, but if the client is using a packed pixel format
|
||||
/// then that may be incorrect, e.g. if each sample is a byte but contains two pixels, each in a single nibble,
|
||||
/// then the correct duplication might be a byte composed of copies of the two top nibbles as the left bookend,
|
||||
/// and one composed of copies of the two bottom nibbles on the right.
|
||||
struct Bookender {
|
||||
/// Writes to left_bookend the sample that should appear as a continuation before the left_value;
|
||||
/// writes to right_bookend the sample that should appear as a continuation after right_value.
|
||||
virtual void add_bookends(uint8_t *const left_value, uint8_t *const right_value, uint8_t *left_bookend, uint8_t *right_bookend) = 0;
|
||||
};
|
||||
|
||||
/// Sets the current bookender. The bookender be called synchronously within the builder-writing thread.
|
||||
/// Supply nullptr to engage the default bookender.
|
||||
void set_bookender(std::unique_ptr<Bookender> bookender);
|
||||
|
||||
/// Binds this texture to the unit supplied at instantiation.
|
||||
void bind();
|
||||
|
||||
@ -140,8 +124,6 @@ class TextureBuilder {
|
||||
// Caveat: reset to the origin upon a submit. So used in comparison by flush to
|
||||
// determine whether the current batch of write areas needs to be relocated.
|
||||
uint16_t write_areas_start_x_ = 0, write_areas_start_y_ = 0;
|
||||
|
||||
std::unique_ptr<Bookender> bookender_;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user