mirror of
https://github.com/TomHarte/CLK.git
synced 2026-04-19 19:16:34 +00:00
Include interlacing information in Field.
This commit is contained in:
@@ -960,6 +960,18 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
||||
// Hence every pixel is touched every frame, regardless of the machine's output.
|
||||
//
|
||||
|
||||
const auto end_field =
|
||||
[&](
|
||||
const bool was_complete,
|
||||
[[maybe_unused]] const int field_index,
|
||||
[[maybe_unused]] const bool is_interlaced
|
||||
) {
|
||||
if(was_complete && !_dontClearFrameBuffer) {
|
||||
[self outputFrameCleanerToCommandBuffer:commandBuffer];
|
||||
}
|
||||
_dontClearFrameBuffer = NO;
|
||||
};
|
||||
|
||||
switch(_pipeline) {
|
||||
case Pipeline::DirectToDisplay:
|
||||
_scanTarget.output_scans(
|
||||
@@ -967,12 +979,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
||||
[&](const size_t begin, const size_t end) {
|
||||
[self outputFrom:begin to:end commandBuffer:commandBuffer];
|
||||
},
|
||||
[&](const bool was_complete) {
|
||||
if(was_complete && !_dontClearFrameBuffer) {
|
||||
[self outputFrameCleanerToCommandBuffer:commandBuffer];
|
||||
}
|
||||
_dontClearFrameBuffer = NO;
|
||||
}
|
||||
end_field
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -1095,12 +1102,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
||||
[&](const size_t begin, const size_t end) {
|
||||
[self outputFrom:begin to:end commandBuffer:commandBuffer];
|
||||
},
|
||||
[&](const bool was_complete) {
|
||||
if(was_complete && !_dontClearFrameBuffer) {
|
||||
[self outputFrameCleanerToCommandBuffer:commandBuffer];
|
||||
}
|
||||
_dontClearFrameBuffer = NO;
|
||||
}
|
||||
end_field
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,10 +127,10 @@ void ScanTarget::set_alphas() {
|
||||
static constexpr float OutputAlpha = 0.64f;
|
||||
|
||||
if(!scan_output_shader_.empty()) {
|
||||
scan_output_shader_.set_alpha(is_interlaced() ? std::sqrt(OutputAlpha) : OutputAlpha);
|
||||
scan_output_shader_.set_alpha(is_interlacing_ ? std::sqrt(OutputAlpha) : OutputAlpha);
|
||||
}
|
||||
if(!line_output_shader_.empty()) {
|
||||
line_output_shader_.set_alpha(is_interlaced() ? std::sqrt(OutputAlpha) : OutputAlpha);
|
||||
line_output_shader_.set_alpha(is_interlacing_ ? std::sqrt(OutputAlpha) : OutputAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,16 +547,25 @@ void ScanTarget::process_to_rgb(const OutputArea &area) {
|
||||
}
|
||||
|
||||
void ScanTarget::bind_current_output_buffer() {
|
||||
output_buffers_[output_buffer_ & (is_interlaced() ? 1 : 0)].bind_framebuffer();
|
||||
output_buffers_[field_index_].bind_framebuffer();
|
||||
}
|
||||
|
||||
void ScanTarget::toggle_output_buffer() {
|
||||
output_buffer_ ^= 1;
|
||||
bind_current_output_buffer();
|
||||
void ScanTarget::end_field(
|
||||
const bool was_complete,
|
||||
const int field_index,
|
||||
const bool is_interlaced
|
||||
) {
|
||||
if(was_complete) {
|
||||
full_display_rectangle_.draw(0.0, 0.0, 0.0);
|
||||
}
|
||||
test_gl([&]{ glClear(GL_STENCIL_BUFFER_BIT); });
|
||||
field_index_ = field_index;
|
||||
output_buffers_[field_index].bind_framebuffer();
|
||||
is_interlacing_ = is_interlaced;
|
||||
}
|
||||
|
||||
void ScanTarget::output_lines(const OutputArea &area) {
|
||||
bind_current_output_buffer();
|
||||
output_buffers_[field_index_].bind_framebuffer();
|
||||
BufferingScanTarget::output_lines(
|
||||
area,
|
||||
[&](const size_t begin, const size_t end) {
|
||||
@@ -568,12 +577,12 @@ void ScanTarget::output_lines(const OutputArea &area) {
|
||||
line_output_shader_.bind();
|
||||
test_gl([&]{ glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_lines)); });
|
||||
},
|
||||
[&](const bool was_complete) {
|
||||
if(was_complete) {
|
||||
full_display_rectangle_.draw(0.0, 0.0, 0.0);
|
||||
}
|
||||
test_gl([&]{ glClear(GL_STENCIL_BUFFER_BIT); });
|
||||
toggle_output_buffer();
|
||||
[&](
|
||||
const bool was_complete,
|
||||
const int field_index,
|
||||
const bool is_interlaced
|
||||
) {
|
||||
end_field(was_complete, field_index, is_interlaced);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -591,12 +600,12 @@ void ScanTarget::output_scans(const OutputArea &area) {
|
||||
composition_buffer_.bind_texture();
|
||||
test_gl([&]{ glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans)); });
|
||||
},
|
||||
[&](const bool was_complete) {
|
||||
if(was_complete) {
|
||||
full_display_rectangle_.draw(0.0, 0.0, 0.0);
|
||||
}
|
||||
test_gl([&]{ glClear(GL_STENCIL_BUFFER_BIT); });
|
||||
toggle_output_buffer();
|
||||
[&](
|
||||
const bool was_complete,
|
||||
const int field_index,
|
||||
const bool is_interlaced
|
||||
) {
|
||||
end_field(was_complete, field_index, is_interlaced);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -608,7 +617,7 @@ void ScanTarget::draw(const int output_width, const int output_height) {
|
||||
test_gl([&]{ glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); });
|
||||
|
||||
if(!output_buffers_[0].empty()) {
|
||||
if(is_interlaced()) {
|
||||
if(is_interlacing_) {
|
||||
if(!was_interlacing_) {
|
||||
set_alphas();
|
||||
output_buffers_[1].bind_framebuffer();
|
||||
@@ -635,7 +644,7 @@ void ScanTarget::draw(const int output_width, const int output_height) {
|
||||
output_buffers_[0].bind_texture();
|
||||
copy_shader_.perform(OutputTextureUnits[0], 1.0f);
|
||||
}
|
||||
was_interlacing_ = is_interlaced();
|
||||
was_interlacing_ = is_interlacing_;
|
||||
}
|
||||
|
||||
is_drawing_to_output_.clear(std::memory_order_release);
|
||||
|
||||
@@ -112,8 +112,8 @@ private:
|
||||
TextureTarget demodulation_buffer_;
|
||||
|
||||
std::array<TextureTarget, 2> output_buffers_;
|
||||
int output_buffer_ = 0;
|
||||
bool was_interlacing_ = false;
|
||||
bool is_interlacing_ = false;
|
||||
|
||||
Shader composition_shader_;
|
||||
Shader separation_shader_;
|
||||
@@ -128,8 +128,9 @@ private:
|
||||
void output_lines(const OutputArea &);
|
||||
void output_scans(const OutputArea &);
|
||||
|
||||
int field_index_ = 0;
|
||||
void bind_current_output_buffer();
|
||||
void toggle_output_buffer();
|
||||
void end_field(bool was_complete, int field_index, bool is_interlaced);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -223,9 +223,15 @@ void BufferingScanTarget::announce(
|
||||
|
||||
auto write = frame_write_.load(std::memory_order_relaxed);
|
||||
const auto submit_pointers = submit_pointers_.load(std::memory_order_relaxed);
|
||||
frames_[write].first_line = submit_pointers.line;
|
||||
frames_[write].first_scan = submit_pointers.scan;
|
||||
frames_[write].was_complete = previous_frame_was_complete_;
|
||||
auto &frame = frames_[write];
|
||||
|
||||
frame.first_line = submit_pointers.line;
|
||||
frame.first_scan = submit_pointers.scan;
|
||||
frame.was_complete = previous_frame_was_complete_;
|
||||
frame.is_interlaced = is_interlaced_;
|
||||
frame.field_index = field_index_ & (frame.is_interlaced ? 1 : 0);
|
||||
|
||||
field_index_ ^= 1;
|
||||
++write;
|
||||
frame_write_.store(write, std::memory_order_release);
|
||||
|
||||
@@ -254,9 +260,9 @@ void BufferingScanTarget::announce(
|
||||
return true;
|
||||
};
|
||||
if(!similar_enough(0, 1) && similar_enough(0, 2) && similar_enough(1, 2)) {
|
||||
is_interlaced_.store(true, std::memory_order_relaxed);
|
||||
is_interlaced_ = true;
|
||||
} else if(similar_enough(0, 1)) {
|
||||
is_interlaced_.store(false, std::memory_order_relaxed);
|
||||
is_interlaced_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +446,3 @@ const Outputs::Display::ScanTarget::Modals &BufferingScanTarget::modals() const
|
||||
bool BufferingScanTarget::has_new_modals() const {
|
||||
return modals_are_dirty_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool BufferingScanTarget::is_interlaced() const {
|
||||
return is_interlaced_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
@@ -164,13 +164,6 @@ public:
|
||||
/// @returns the current @c Modals.
|
||||
const Modals &modals() const;
|
||||
|
||||
/// @returns whether the current output being received appears to describe an interlaced signal.
|
||||
/// This is a hint only, potentially to provide for better deinterlacing of output, being derived locally
|
||||
/// from line positioning. Specifically: if a scan target pays no heed to this whatsoever it's likely to
|
||||
/// end up doing the equivalent of a bob. If it so desires, it might prefer to do something closer to
|
||||
/// a weave if and only if interlaced video is detected.
|
||||
bool is_interlaced() const;
|
||||
|
||||
/// @returns @c true if new modals are available; @c false otherwise.
|
||||
///
|
||||
/// Safe to call from any thread.
|
||||
@@ -179,10 +172,10 @@ public:
|
||||
template <typename OutputFuncT, typename FrameFuncT, typename CountGetT, typename FrameLimitGetT>
|
||||
void output(
|
||||
const OutputArea &area,
|
||||
const OutputFuncT &&output,
|
||||
const FrameFuncT &&end_frame,
|
||||
const CountGetT &&count,
|
||||
const FrameLimitGetT &&limit
|
||||
OutputFuncT &&output,
|
||||
FrameFuncT &&end_frame,
|
||||
CountGetT &&count,
|
||||
FrameLimitGetT &&limit
|
||||
) const {
|
||||
if(count(area.end) == count(area.begin) && area.end.frame == area.begin.frame) {
|
||||
return;
|
||||
@@ -200,8 +193,11 @@ public:
|
||||
output(output_begin, limit(frames_[frame_begin]));
|
||||
output_begin = limit(frames_[frame_begin]);
|
||||
}
|
||||
end_frame(frames_[frame_begin].was_complete);
|
||||
|
||||
end_frame(
|
||||
frames_[frame_begin].was_complete,
|
||||
frames_[frame_begin].field_index,
|
||||
frames_[frame_begin].is_interlaced
|
||||
);
|
||||
++frame_begin;
|
||||
if(frame_begin == frames_.size()) frame_begin = 0;
|
||||
} while(frame_begin != area.end.frame);
|
||||
@@ -214,13 +210,13 @@ public:
|
||||
template <typename ScanFuncT, typename FrameFuncT>
|
||||
void output_scans(
|
||||
const OutputArea &area,
|
||||
const ScanFuncT &&output_scans,
|
||||
const FrameFuncT &&end_frame
|
||||
ScanFuncT &&output_scans,
|
||||
FrameFuncT &&end_frame
|
||||
) const {
|
||||
output(
|
||||
area,
|
||||
std::move(output_scans),
|
||||
std::move(end_frame),
|
||||
output_scans,
|
||||
end_frame,
|
||||
[](const auto &endpoint) { return endpoint.scan; },
|
||||
[](const auto &frame) { return frame.first_scan; }
|
||||
);
|
||||
@@ -229,13 +225,13 @@ public:
|
||||
template <typename LineFuncT, typename FrameFuncT>
|
||||
void output_lines(
|
||||
const OutputArea &area,
|
||||
const LineFuncT &&output_lines,
|
||||
const FrameFuncT &&end_frame
|
||||
LineFuncT &&output_lines,
|
||||
FrameFuncT &&end_frame
|
||||
) const {
|
||||
output(
|
||||
area,
|
||||
std::move(output_lines),
|
||||
std::move(end_frame),
|
||||
output_lines,
|
||||
end_frame,
|
||||
[](const auto &endpoint) { return endpoint.line; },
|
||||
[](const auto &frame) { return frame.first_line; }
|
||||
);
|
||||
@@ -318,6 +314,8 @@ private:
|
||||
size_t first_scan;
|
||||
size_t first_line;
|
||||
bool was_complete;
|
||||
int field_index;
|
||||
bool is_interlaced;
|
||||
};
|
||||
static constexpr uint16_t NumFrames = 60;
|
||||
std::array<Frame, NumFrames> frames_;
|
||||
@@ -329,7 +327,8 @@ private:
|
||||
static constexpr size_t StartHistoryLength = 16;
|
||||
std::array<Outputs::Display::ScanTarget::Scan::EndPoint, StartHistoryLength> start_history_;
|
||||
Numeric::CircularCounter<size_t, StartHistoryLength> start_history_pointer_;
|
||||
std::atomic<bool> is_interlaced_ = false;
|
||||
bool is_interlaced_ = false;
|
||||
int field_index_ = 0;
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user