1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +00:00

Begins attempts to keep track of display metrics.

i.e. a system that can both make smart decisions about when to use a lower resolution, and hopefully allow some sort of flywheel-esque horizontal retrace synchronisation. And possibly some raster beam chasing?
This commit is contained in:
Thomas Harte 2019-03-04 21:54:50 -05:00
parent 8915950c7d
commit 86a6b04d4a
5 changed files with 94 additions and 2 deletions

View File

@ -215,6 +215,7 @@
4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; }; 4B595FAE2086DFBA0083CAA8 /* AudioToggle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B595FAC2086DFBA0083CAA8 /* AudioToggle.cpp */; };
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; }; 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADB81DE3151600AEC565 /* FileHolder.cpp */; };
4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */; }; 4B5FADC01DE3BF2B00AEC565 /* Microdisc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */; };
4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */; };
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; }; 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; }; 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; }; 4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
@ -897,6 +898,8 @@
4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = "<group>"; }; 4B5FADB91DE3151600AEC565 /* FileHolder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FileHolder.hpp; sourceTree = "<group>"; };
4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Microdisc.cpp; path = Oric/Microdisc.cpp; sourceTree = "<group>"; }; 4B5FADBE1DE3BF2B00AEC565 /* Microdisc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Microdisc.cpp; path = Oric/Microdisc.cpp; sourceTree = "<group>"; };
4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Microdisc.hpp; path = Oric/Microdisc.hpp; sourceTree = "<group>"; }; 4B5FADBF1DE3BF2B00AEC565 /* Microdisc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Microdisc.hpp; path = Oric/Microdisc.hpp; sourceTree = "<group>"; };
4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DisplayMetrics.cpp; path = ../../Outputs/DisplayMetrics.cpp; sourceTree = "<group>"; };
4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = DisplayMetrics.hpp; path = ../../Outputs/DisplayMetrics.hpp; sourceTree = "<group>"; };
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; }; 4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; }; 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; }; 4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
@ -1780,7 +1783,9 @@
4B366DFD1B5C165F0026627B /* Outputs */ = { 4B366DFD1B5C165F0026627B /* Outputs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B622AE3222E0AD5008B59F2 /* DisplayMetrics.cpp */,
4B05401D219D1618001BF69C /* ScanTarget.cpp */, 4B05401D219D1618001BF69C /* ScanTarget.cpp */,
4B622AE4222E0AD5008B59F2 /* DisplayMetrics.hpp */,
4BD601A920D89F2A00CBCE57 /* Log.hpp */, 4BD601A920D89F2A00CBCE57 /* Log.hpp */,
4BF52672218E752E00313227 /* ScanTarget.hpp */, 4BF52672218E752E00313227 /* ScanTarget.hpp */,
4B0CCC411C62D0B3001CAC5F /* CRT */, 4B0CCC411C62D0B3001CAC5F /* CRT */,
@ -3025,8 +3030,8 @@
4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */, 4BD5D2672199148100DDF17D /* ScanTargetGLSLFragments.cpp */,
4BD191D9219113B80042E144 /* OpenGL.hpp */, 4BD191D9219113B80042E144 /* OpenGL.hpp */,
4BD191F32191180E0042E144 /* ScanTarget.hpp */, 4BD191F32191180E0042E144 /* ScanTarget.hpp */,
4BD424DC2193B5340097291A /* Primitives */,
4B961408222760E0001A7BF2 /* Screenshot.hpp */, 4B961408222760E0001A7BF2 /* Screenshot.hpp */,
4BD424DC2193B5340097291A /* Primitives */,
); );
name = OpenGL; name = OpenGL;
path = ../../Outputs/OpenGL; path = ../../Outputs/OpenGL;
@ -3895,6 +3900,7 @@
4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */, 4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */,
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */, 4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */,
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */, 4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
4B622AE5222E0AD5008B59F2 /* DisplayMetrics.cpp in Sources */,
4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */, 4B1497881EE4A1DA00CE2596 /* ZX80O81P.cpp in Sources */,
4B894520201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B894520201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */, 4B2B3A4B1F9B8FA70062DABF /* Typer.cpp in Sources */,

View File

@ -0,0 +1,20 @@
//
// DisplayMetrics.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#include "DisplayMetrics.hpp"
using namespace Outputs::Display;
void Metrics::announce_event(ScanTarget::Event event) {
}
void Metrics::announce_did_resize() {
}
void Metrics::announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration duration, bool complete) {
}

View File

@ -0,0 +1,34 @@
//
// DisplayMetrics.hpp
// Clock Signal
//
// Created by Thomas Harte on 04/03/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#ifndef DisplayMetrics_hpp
#define DisplayMetrics_hpp
#include "ScanTarget.hpp"
#include <chrono>
namespace Outputs {
namespace Display {
/*!
A class to derive various metrics about the input to a ScanTarget,
based purely on empirical observation. In particular it is intended
to allow for host-client frame synchronisation.
*/
class Metrics {
public:
void announce_event(ScanTarget::Event event);
void announce_did_resize();
void announce_draw_status(size_t lines, std::chrono::high_resolution_clock::duration duration, bool complete);
};
}
}
#endif /* DisplayMetrics_hpp */

View File

@ -254,6 +254,9 @@ void ScanTarget::submit() {
} }
void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t composite_amplitude) { void ScanTarget::announce(Event event, bool is_visible, const Outputs::Display::ScanTarget::Scan::EndPoint &location, uint8_t composite_amplitude) {
// Forward the event to the display metrics tracker.
display_metrics_.announce_event(event);
if(event == ScanTarget::Event::EndVerticalRetrace) { if(event == ScanTarget::Event::EndVerticalRetrace) {
// The previous-frame-is-complete flag is subject to a two-slot queue because // The previous-frame-is-complete flag is subject to a two-slot queue because
// measurement for *this* frame needs to begin now, meaning that the previous // measurement for *this* frame needs to begin now, meaning that the previous
@ -383,14 +386,26 @@ void ScanTarget::setup_pipeline() {
input_shader_->set_uniform("textureName", GLint(SourceDataTextureUnit - GL_TEXTURE0)); input_shader_->set_uniform("textureName", GLint(SourceDataTextureUnit - GL_TEXTURE0));
} }
Outputs::Display::Metrics &ScanTarget::display_metrics() {
return display_metrics_;
}
void ScanTarget::update(int output_width, int output_height) { void ScanTarget::update(int output_width, int output_height) {
if(fence_ != nullptr) { if(fence_ != nullptr) {
// if the GPU is still busy, don't wait; we'll catch it next time // if the GPU is still busy, don't wait; we'll catch it next time
if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, 0) == GL_TIMEOUT_EXPIRED) { if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, 0) == GL_TIMEOUT_EXPIRED) {
display_metrics_.announce_draw_status(
lines_submitted_,
std::chrono::high_resolution_clock::now() - line_submission_begin_time_,
false);
return; return;
} }
fence_ = nullptr; fence_ = nullptr;
} }
display_metrics_.announce_draw_status(
lines_submitted_,
std::chrono::high_resolution_clock::now() - line_submission_begin_time_,
true);
// Spin until the is-drawing flag is reset; the wait sync above will deal // Spin until the is-drawing flag is reset; the wait sync above will deal
// with instances where waiting is inappropriate. // with instances where waiting is inappropriate.
@ -403,10 +418,16 @@ void ScanTarget::update(int output_width, int output_height) {
modals_are_dirty_ = false; modals_are_dirty_ = false;
} }
// Determine the start time of this submission group.
line_submission_begin_time_ = std::chrono::high_resolution_clock::now();
// Grab the current read and submit pointers. // Grab the current read and submit pointers.
const auto submit_pointers = submit_pointers_.load(); const auto submit_pointers = submit_pointers_.load();
const auto read_pointers = read_pointers_.load(); const auto read_pointers = read_pointers_.load();
// Determine how many lines are about to be submitted.
lines_submitted_ = (read_pointers.line + line_buffer_.size() - submit_pointers.line) % line_buffer_.size();
// Submit scans; only the new ones need to be communicated. // Submit scans; only the new ones need to be communicated.
size_t new_scans = (submit_pointers.scan_buffer + scan_buffer_.size() - read_pointers.scan_buffer) % scan_buffer_.size(); size_t new_scans = (submit_pointers.scan_buffer + scan_buffer_.size() - read_pointers.scan_buffer) % scan_buffer_.size();
if(new_scans) { if(new_scans) {
@ -536,6 +557,7 @@ void ScanTarget::update(int output_width, int output_height) {
const int proportional_width = (framebuffer_height * 4) / 3; const int proportional_width = (framebuffer_height * 4) / 3;
const bool did_create_accumulation_texture = !accumulation_texture_ || ( /* !synchronous && */ (accumulation_texture_->get_width() != proportional_width || accumulation_texture_->get_height() != framebuffer_height)); const bool did_create_accumulation_texture = !accumulation_texture_ || ( /* !synchronous && */ (accumulation_texture_->get_width() != proportional_width || accumulation_texture_->get_height() != framebuffer_height));
if(did_create_accumulation_texture) { if(did_create_accumulation_texture) {
display_metrics_.announce_did_resize();
std::unique_ptr<OpenGL::TextureTarget> new_framebuffer( std::unique_ptr<OpenGL::TextureTarget> new_framebuffer(
new TextureTarget( new TextureTarget(
GLsizei(proportional_width), GLsizei(proportional_width),

View File

@ -9,8 +9,9 @@
#ifndef ScanTarget_hpp #ifndef ScanTarget_hpp
#define ScanTarget_hpp #define ScanTarget_hpp
#include "../ScanTarget.hpp"
#include "../Log.hpp" #include "../Log.hpp"
#include "../DisplayMetrics.hpp"
#include "../ScanTarget.hpp"
#include "OpenGL.hpp" #include "OpenGL.hpp"
#include "Primitives/TextureTarget.hpp" #include "Primitives/TextureTarget.hpp"
@ -21,6 +22,7 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <chrono>
#include <list> #include <list>
#include <memory> #include <memory>
#include <string> #include <string>
@ -47,6 +49,9 @@ class ScanTarget: public Outputs::Display::ScanTarget {
/*! Processes all the latest input, at a resolution suitable for later output to a framebuffer of the specified size. */ /*! Processes all the latest input, at a resolution suitable for later output to a framebuffer of the specified size. */
void update(int output_width, int output_height); void update(int output_width, int output_height);
/*! @returns The DisplayMetrics object that this ScanTarget has been providing with announcements and draw overages. */
Metrics &display_metrics();
private: private:
#ifndef NDEBUG #ifndef NDEBUG
struct OpenGLVersionDumper { struct OpenGLVersionDumper {
@ -78,6 +83,11 @@ class ScanTarget: public Outputs::Display::ScanTarget {
bool output_is_visible_ = false; bool output_is_visible_ = false;
Metrics display_metrics_;
size_t lines_submitted_ = 0;
std::chrono::high_resolution_clock::time_point line_submission_begin_time_;
// Extends the definition of a Scan to include two extra fields, // Extends the definition of a Scan to include two extra fields,
// relevant to the way that this scan target processes video. // relevant to the way that this scan target processes video.
struct Scan { struct Scan {