mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-16 08:56:51 +00:00
commit
183a5379de
@ -54,16 +54,20 @@ template <class T> class MOS6560 {
|
|||||||
audio_generator_(audio_queue_),
|
audio_generator_(audio_queue_),
|
||||||
speaker_(audio_generator_)
|
speaker_(audio_generator_)
|
||||||
{
|
{
|
||||||
crt_->set_composite_sampling_function(
|
crt_->set_svideo_sampling_function(
|
||||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)"
|
||||||
"{"
|
"{"
|
||||||
"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
|
"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
|
||||||
"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
|
|
||||||
|
|
||||||
"float chroma = cos(phase + phaseOffset);"
|
"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
|
||||||
"return mix(yc.x, step(yc.y, 0.75) * chroma, amplitude);"
|
"float chroma = step(yc.y, 0.75) * cos(phase + phaseOffset);"
|
||||||
|
|
||||||
|
"return vec2(yc.x, chroma);"
|
||||||
"}");
|
"}");
|
||||||
|
|
||||||
|
// default to s-video output
|
||||||
|
crt_->set_video_signal(Outputs::CRT::VideoSignal::SVideo);
|
||||||
|
|
||||||
// default to NTSC
|
// default to NTSC
|
||||||
set_output_mode(OutputMode::NTSC);
|
set_output_mode(OutputMode::NTSC);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ TMS9918::TMS9918(Personality p) {
|
|||||||
"{"
|
"{"
|
||||||
"return texture(sampler, coordinate).rgb / vec3(255.0);"
|
"return texture(sampler, coordinate).rgb / vec3(255.0);"
|
||||||
"}");
|
"}");
|
||||||
crt_->set_output_device(Outputs::CRT::OutputDevice::Monitor);
|
crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB);
|
||||||
crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f));
|
crt_->set_visible_area(Outputs::CRT::Rect(0.055f, 0.025f, 0.9f, 0.9f));
|
||||||
crt_->set_input_gamma(2.8f);
|
crt_->set_input_gamma(2.8f);
|
||||||
|
|
||||||
|
@ -317,7 +317,7 @@ class CRTCBusHandler {
|
|||||||
"return vec3(float((sample >> 4) & 3u), float((sample >> 2) & 3u), float(sample & 3u)) / 2.0;"
|
"return vec3(float((sample >> 4) & 3u), float((sample >> 2) & 3u), float(sample & 3u)) / 2.0;"
|
||||||
"}");
|
"}");
|
||||||
crt_->set_visible_area(Outputs::CRT::Rect(0.075f, 0.05f, 0.9f, 0.9f));
|
crt_->set_visible_area(Outputs::CRT::Rect(0.075f, 0.05f, 0.9f, 0.9f));
|
||||||
crt_->set_output_device(Outputs::CRT::OutputDevice::Monitor);
|
crt_->set_video_signal(Outputs::CRT::VideoSignal::RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destructs the CRT.
|
/// Destructs the CRT.
|
||||||
|
@ -25,7 +25,7 @@ namespace {
|
|||||||
TIA::TIA(bool create_crt) {
|
TIA::TIA(bool create_crt) {
|
||||||
if(create_crt) {
|
if(create_crt) {
|
||||||
crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::CRT::DisplayType::NTSC60, 1));
|
crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::CRT::DisplayType::NTSC60, 1));
|
||||||
crt_->set_output_device(Outputs::CRT::OutputDevice::Television);
|
crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
||||||
set_output_mode(OutputMode::NTSC);
|
set_output_mode(OutputMode::NTSC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,20 +123,20 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) {
|
|||||||
Outputs::CRT::DisplayType display_type;
|
Outputs::CRT::DisplayType display_type;
|
||||||
|
|
||||||
if(output_mode == OutputMode::NTSC) {
|
if(output_mode == OutputMode::NTSC) {
|
||||||
crt_->set_composite_sampling_function(
|
crt_->set_svideo_sampling_function(
|
||||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)"
|
||||||
"{"
|
"{"
|
||||||
"uint c = texture(texID, coordinate).r;"
|
"uint c = texture(texID, coordinate).r;"
|
||||||
"uint y = c & 14u;"
|
"uint y = c & 14u;"
|
||||||
"uint iPhase = (c >> 4);"
|
"uint iPhase = (c >> 4);"
|
||||||
|
|
||||||
"float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;"
|
"float phaseOffset = 6.283185308 * float(iPhase) / 13.0 + 5.074880441076923;"
|
||||||
"return mix(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset), amplitude);"
|
"return vec2(float(y) / 14.0, step(1, iPhase) * cos(phase + phaseOffset));"
|
||||||
"}");
|
"}");
|
||||||
display_type = Outputs::CRT::DisplayType::NTSC60;
|
display_type = Outputs::CRT::DisplayType::NTSC60;
|
||||||
} else {
|
} else {
|
||||||
crt_->set_composite_sampling_function(
|
crt_->set_svideo_sampling_function(
|
||||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)"
|
||||||
"{"
|
"{"
|
||||||
"uint c = texture(texID, coordinate).r;"
|
"uint c = texture(texID, coordinate).r;"
|
||||||
"uint y = c & 14u;"
|
"uint y = c & 14u;"
|
||||||
@ -145,10 +145,12 @@ void TIA::set_output_mode(Atari2600::TIA::OutputMode output_mode) {
|
|||||||
"uint direction = iPhase & 1u;"
|
"uint direction = iPhase & 1u;"
|
||||||
"float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);"
|
"float phaseOffset = float(7u - direction) + (float(direction) - 0.5) * 2.0 * float(iPhase >> 1);"
|
||||||
"phaseOffset *= 6.283185308 / 12.0;"
|
"phaseOffset *= 6.283185308 / 12.0;"
|
||||||
"return mix(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset), amplitude);"
|
"return vec2(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset));"
|
||||||
"}");
|
"}");
|
||||||
display_type = Outputs::CRT::DisplayType::PAL50;
|
display_type = Outputs::CRT::DisplayType::PAL50;
|
||||||
}
|
}
|
||||||
|
crt_->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
||||||
|
|
||||||
// line number of cycles in a line of video is one less than twice the number of clock cycles per line; the Atari
|
// line number of cycles in a line of video is one less than twice the number of clock cycles per line; the Atari
|
||||||
// outputs 228 colour cycles of material per line when an NTSC line 227.5. Since all clock numbers will be doubled
|
// outputs 228 colour cycles of material per line when an NTSC line 227.5. Since all clock numbers will be doubled
|
||||||
// later, cycles_per_line * 2 - 1 is therefore the real length of an NTSC line, even though we're going to supply
|
// later, cycles_per_line * 2 - 1 is therefore the real length of an NTSC line, even though we're going to supply
|
||||||
|
@ -135,7 +135,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
void setup_output(float aspect_ratio) override {
|
void setup_output(float aspect_ratio) override {
|
||||||
vdp_.reset(new TI::TMS9918(TI::TMS9918::TMS9918A));
|
vdp_.reset(new TI::TMS9918(TI::TMS9918::TMS9918A));
|
||||||
get_crt()->set_output_device(Outputs::CRT::OutputDevice::Television);
|
get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_output() override {
|
void close_output() override {
|
||||||
|
@ -454,7 +454,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
Configurable::Display display;
|
Configurable::Display display;
|
||||||
if(Configurable::get_display(selections_by_option, display)) {
|
if(Configurable::get_display(selections_by_option, display)) {
|
||||||
get_crt()->set_output_device((display == Configurable::Display::RGB) ? Outputs::CRT::OutputDevice::Monitor : Outputs::CRT::OutputDevice::Television);
|
get_crt()->set_video_signal((display == Configurable::Display::RGB) ? Outputs::CRT::VideoSignal::RGB : Outputs::CRT::VideoSignal::Composite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +562,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
Configurable::Display display;
|
Configurable::Display display;
|
||||||
if(Configurable::get_display(selections_by_option, display)) {
|
if(Configurable::get_display(selections_by_option, display)) {
|
||||||
get_crt()->set_output_device((display == Configurable::Display::RGB) ? Outputs::CRT::OutputDevice::Monitor : Outputs::CRT::OutputDevice::Television);
|
get_crt()->set_video_signal((display == Configurable::Display::RGB) ? Outputs::CRT::VideoSignal::RGB : Outputs::CRT::VideoSignal::Composite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ class ConcreteMachine:
|
|||||||
use_fast_tape_hack_ = activate;
|
use_fast_tape_hack_ = activate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_output_device(Outputs::CRT::OutputDevice output_device) {
|
void set_output_device(Outputs::CRT::VideoSignal output_device) {
|
||||||
video_output_->set_output_device(output_device);
|
video_output_->set_output_device(output_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
video_output_.reset(new VideoOutput(ram_));
|
video_output_.reset(new VideoOutput(ram_));
|
||||||
if(!colour_rom_.empty()) video_output_->set_colour_rom(colour_rom_);
|
if(!colour_rom_.empty()) video_output_->set_colour_rom(colour_rom_);
|
||||||
set_output_device(Outputs::CRT::OutputDevice::Monitor);
|
set_output_device(Outputs::CRT::VideoSignal::RGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_output() override final {
|
void close_output() override final {
|
||||||
@ -465,7 +465,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
Configurable::Display display;
|
Configurable::Display display;
|
||||||
if(Configurable::get_display(selections_by_option, display)) {
|
if(Configurable::get_display(selections_by_option, display)) {
|
||||||
set_output_device((display == Configurable::Display::RGB) ? Outputs::CRT::OutputDevice::Monitor : Outputs::CRT::OutputDevice::Television);
|
set_output_device((display == Configurable::Display::RGB) ? Outputs::CRT::VideoSignal::RGB : Outputs::CRT::VideoSignal::Composite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,13 +41,13 @@ VideoOutput::VideoOutput(uint8_t *memory) :
|
|||||||
);
|
);
|
||||||
crt_->set_composite_function_type(Outputs::CRT::CRT::CompositeSourceType::DiscreteFourSamplesPerCycle, 0.0f);
|
crt_->set_composite_function_type(Outputs::CRT::CRT::CompositeSourceType::DiscreteFourSamplesPerCycle, 0.0f);
|
||||||
|
|
||||||
set_output_device(Outputs::CRT::OutputDevice::Television);
|
set_output_device(Outputs::CRT::VideoSignal::Composite);
|
||||||
crt_->set_visible_area(crt_->get_rect_for_area(53, 224, 16 * 6, 40 * 6, 4.0f / 3.0f));
|
crt_->set_visible_area(crt_->get_rect_for_area(53, 224, 16 * 6, 40 * 6, 4.0f / 3.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutput::set_output_device(Outputs::CRT::OutputDevice output_device) {
|
void VideoOutput::set_output_device(Outputs::CRT::VideoSignal output_device) {
|
||||||
output_device_ = output_device;
|
output_device_ = output_device;
|
||||||
crt_->set_output_device(output_device);
|
crt_->set_video_signal(output_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoOutput::set_colour_rom(const std::vector<uint8_t> &rom) {
|
void VideoOutput::set_colour_rom(const std::vector<uint8_t> &rom) {
|
||||||
@ -129,7 +129,7 @@ void VideoOutput::run_for(const Cycles cycles) {
|
|||||||
if(control_byte & 0x60) {
|
if(control_byte & 0x60) {
|
||||||
if(pixel_target_) {
|
if(pixel_target_) {
|
||||||
uint16_t colours[2];
|
uint16_t colours[2];
|
||||||
if(output_device_ == Outputs::CRT::OutputDevice::Monitor) {
|
if(output_device_ == Outputs::CRT::VideoSignal::RGB) {
|
||||||
colours[0] = static_cast<uint8_t>(paper_ ^ inverse_mask);
|
colours[0] = static_cast<uint8_t>(paper_ ^ inverse_mask);
|
||||||
colours[1] = static_cast<uint8_t>(ink_ ^ inverse_mask);
|
colours[1] = static_cast<uint8_t>(ink_ ^ inverse_mask);
|
||||||
} else {
|
} else {
|
||||||
@ -183,7 +183,7 @@ void VideoOutput::run_for(const Cycles cycles) {
|
|||||||
pixel_target_[0] = pixel_target_[1] =
|
pixel_target_[0] = pixel_target_[1] =
|
||||||
pixel_target_[2] = pixel_target_[3] =
|
pixel_target_[2] = pixel_target_[3] =
|
||||||
pixel_target_[4] = pixel_target_[5] =
|
pixel_target_[4] = pixel_target_[5] =
|
||||||
(output_device_ == Outputs::CRT::OutputDevice::Monitor) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask];
|
(output_device_ == Outputs::CRT::VideoSignal::RGB) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(pixel_target_) pixel_target_ += 6;
|
if(pixel_target_) pixel_target_ += 6;
|
||||||
|
@ -20,7 +20,7 @@ class VideoOutput {
|
|||||||
Outputs::CRT::CRT *get_crt();
|
Outputs::CRT::CRT *get_crt();
|
||||||
void run_for(const Cycles cycles);
|
void run_for(const Cycles cycles);
|
||||||
void set_colour_rom(const std::vector<uint8_t> &rom);
|
void set_colour_rom(const std::vector<uint8_t> &rom);
|
||||||
void set_output_device(Outputs::CRT::OutputDevice output_device);
|
void set_output_device(Outputs::CRT::VideoSignal output_device);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t *ram_;
|
uint8_t *ram_;
|
||||||
@ -33,7 +33,7 @@ class VideoOutput {
|
|||||||
// Output target and device
|
// Output target and device
|
||||||
uint16_t *pixel_target_;
|
uint16_t *pixel_target_;
|
||||||
uint16_t colour_forms_[8];
|
uint16_t colour_forms_[8];
|
||||||
Outputs::CRT::OutputDevice output_device_;
|
Outputs::CRT::VideoSignal output_device_;
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
uint8_t ink_, paper_;
|
uint8_t ink_, paper_;
|
||||||
|
@ -3060,7 +3060,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0700;
|
LastSwiftUpdateCheck = 0700;
|
||||||
LastUpgradeCheck = 0900;
|
LastUpgradeCheck = 0930;
|
||||||
ORGANIZATIONNAME = "Thomas Harte";
|
ORGANIZATIONNAME = "Thomas Harte";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
4B055A691FAE763F0060FFFF = {
|
4B055A691FAE763F0060FFFF = {
|
||||||
@ -3910,12 +3910,14 @@
|
|||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
@ -3963,12 +3965,14 @@
|
|||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0900"
|
LastUpgradeVersion = "0930"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@ -26,9 +26,8 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
language = ""
|
codeCoverageEnabled = "YES"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
codeCoverageEnabled = "YES">
|
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
@ -74,7 +73,6 @@
|
|||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
language = ""
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
@ -94,7 +94,7 @@ struct SpeakerDelegate: public Outputs::Speaker::Speaker::Delegate, public LockP
|
|||||||
|
|
||||||
[_view performWithGLContext:^{
|
[_view performWithGLContext:^{
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
_machine->crt_machine()->close_output();
|
self->_machine->crt_machine()->close_output();
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
@synchronized(_machine) {
|
@synchronized(self->_machine) {
|
||||||
_atari2600->set_switch_is_enabled(toggleSwitch, false);
|
self->_atari2600->set_switch_is_enabled(toggleSwitch, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,18 @@
|
|||||||
|
|
||||||
#import <CommonCrypto/CommonDigest.h>
|
#import <CommonCrypto/CommonDigest.h>
|
||||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||||
|
#include "../../../Analyser/Static/Atari/Target.hpp"
|
||||||
|
|
||||||
|
using PagingModel = Analyser::Static::Atari::Target::PagingModel;
|
||||||
|
|
||||||
@interface AtariROMRecord : NSObject
|
@interface AtariROMRecord : NSObject
|
||||||
@property(nonatomic, readonly) Analyser::Static::Atari2600PagingModel pagingModel;
|
@property(nonatomic, readonly) PagingModel pagingModel;
|
||||||
@property(nonatomic, readonly) BOOL usesSuperchip;
|
@property(nonatomic, readonly) BOOL usesSuperchip;
|
||||||
+ (instancetype)recordWithPagingModel:(Analyser::Static::Atari2600PagingModel)pagingModel usesSuperchip:(BOOL)usesSuperchip;
|
+ (instancetype)recordWithPagingModel:(PagingModel)pagingModel usesSuperchip:(BOOL)usesSuperchip;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation AtariROMRecord
|
@implementation AtariROMRecord
|
||||||
+ (instancetype)recordWithPagingModel:(Analyser::Static::Atari2600PagingModel)pagingModel usesSuperchip:(BOOL)usesSuperchip
|
+ (instancetype)recordWithPagingModel:(PagingModel)pagingModel usesSuperchip:(BOOL)usesSuperchip
|
||||||
{
|
{
|
||||||
AtariROMRecord *record = [[AtariROMRecord alloc] init];
|
AtariROMRecord *record = [[AtariROMRecord alloc] init];
|
||||||
record->_pagingModel = pagingModel;
|
record->_pagingModel = pagingModel;
|
||||||
@ -27,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#define Record(sha, model, uses) sha : [AtariROMRecord recordWithPagingModel:Analyser::Static::Atari2600PagingModel::model usesSuperchip:uses],
|
#define Record(sha, model, uses) sha : [AtariROMRecord recordWithPagingModel:PagingModel::model usesSuperchip:uses],
|
||||||
static NSDictionary<NSString *, AtariROMRecord *> *romRecordsBySHA1 = @{
|
static NSDictionary<NSString *, AtariROMRecord *> *romRecordsBySHA1 = @{
|
||||||
Record(@"58dbcbdffbe80be97746e94a0a75614e64458fdc", None, NO) // 4kraVCS
|
Record(@"58dbcbdffbe80be97746e94a0a75614e64458fdc", None, NO) // 4kraVCS
|
||||||
Record(@"9967a76efb68017f793188f691159f04e6bb4447", None, NO) // 'X'Mission
|
Record(@"9967a76efb68017f793188f691159f04e6bb4447", None, NO) // 'X'Mission
|
||||||
@ -598,8 +601,10 @@ static NSDictionary<NSString *, AtariROMRecord *> *romRecordsBySHA1 = @{
|
|||||||
if(!romRecord) continue;
|
if(!romRecord) continue;
|
||||||
|
|
||||||
// assert equality
|
// assert equality
|
||||||
XCTAssert(targets.front()->atari.paging_model == romRecord.pagingModel, @"%@; should be %d, is %d", testFile, romRecord.pagingModel, targets.front()->atari.paging_model);
|
Analyser::Static::Atari::Target *atari_target = dynamic_cast<Analyser::Static::Atari::Target *>(targets.front().get());
|
||||||
XCTAssert(targets.front()->atari.uses_superchip == romRecord.usesSuperchip, @"%@; should be %@", testFile, romRecord.usesSuperchip ? @"true" : @"false");
|
XCTAssert(atari_target != nullptr);
|
||||||
|
XCTAssert(atari_target->paging_model == romRecord.pagingModel, @"%@; should be %d, is %d", testFile, romRecord.pagingModel, atari_target->paging_model);
|
||||||
|
XCTAssert(atari_target->uses_superchip == romRecord.usesSuperchip, @"%@; should be %@", testFile, romRecord.usesSuperchip ? @"true" : @"false");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,10 +306,27 @@ class CRT {
|
|||||||
*/
|
*/
|
||||||
void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f);
|
void set_composite_function_type(CompositeSourceType type, float offset_of_first_sample = 0.0f);
|
||||||
|
|
||||||
|
/*! Sets a function that will map from whatever data the machine provided to an s-video signal.
|
||||||
|
|
||||||
|
If the output mode is composite then a default mapping from RGB to the display's
|
||||||
|
output mode will be applied.
|
||||||
|
|
||||||
|
@param shader A GLSL fragment including a function with the signature
|
||||||
|
`vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)`
|
||||||
|
that evaluates to the s-video signal level, luminance as the first component and chrominance
|
||||||
|
as the second, as a function of a source buffer, sampling location and colour
|
||||||
|
carrier phase.
|
||||||
|
*/
|
||||||
|
inline void set_svideo_sampling_function(const std::string &shader) {
|
||||||
|
enqueue_openGL_function([shader, this] {
|
||||||
|
openGL_output_builder_.set_svideo_sampling_function(shader);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*! Sets a function that will map from whatever data the machine provided to an RGB signal.
|
/*! Sets a function that will map from whatever data the machine provided to an RGB signal.
|
||||||
|
|
||||||
If the output mode is composite then a default mapping from RGB to the display's composite
|
If the output mode is composite or svideo then a default mapping from RGB to the display's
|
||||||
format will be applied.
|
output mode will be applied.
|
||||||
|
|
||||||
@param shader A GLSL fragent including a function with the signature
|
@param shader A GLSL fragent including a function with the signature
|
||||||
`vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)` that evaluates to an RGB colour
|
`vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)` that evaluates to an RGB colour
|
||||||
@ -329,9 +346,9 @@ class CRT {
|
|||||||
openGL_output_builder_.texture_builder.set_bookender(std::move(bookender));
|
openGL_output_builder_.texture_builder.set_bookender(std::move(bookender));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set_output_device(OutputDevice output_device) {
|
inline void set_video_signal(VideoSignal video_signal) {
|
||||||
enqueue_openGL_function([output_device, this] {
|
enqueue_openGL_function([video_signal, this] {
|
||||||
openGL_output_builder_.set_output_device(output_device);
|
openGL_output_builder_.set_video_signal(video_signal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,9 +36,10 @@ enum class ColourSpace {
|
|||||||
YUV
|
YUV
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class OutputDevice {
|
enum class VideoSignal {
|
||||||
Monitor,
|
RGB,
|
||||||
Television
|
SVideo,
|
||||||
|
Composite
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -71,10 +71,6 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() {
|
|||||||
glDeleteVertexArrays(1, &output_vertex_array_);
|
glDeleteVertexArrays(1, &output_vertex_array_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGLOutputBuilder::get_is_television_output() {
|
|
||||||
return output_device_ == OutputDevice::Television || !rgb_input_shader_program_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OpenGLOutputBuilder::set_target_framebuffer(GLint target_framebuffer) {
|
void OpenGLOutputBuilder::set_target_framebuffer(GLint target_framebuffer) {
|
||||||
target_framebuffer_ = target_framebuffer;
|
target_framebuffer_ = target_framebuffer;
|
||||||
}
|
}
|
||||||
@ -86,6 +82,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
// establish essentials
|
// establish essentials
|
||||||
if(!output_shader_program_) {
|
if(!output_shader_program_) {
|
||||||
prepare_composite_input_shaders();
|
prepare_composite_input_shaders();
|
||||||
|
prepare_svideo_input_shaders();
|
||||||
prepare_rgb_input_shaders();
|
prepare_rgb_input_shaders();
|
||||||
prepare_source_vertex_array();
|
prepare_source_vertex_array();
|
||||||
|
|
||||||
@ -153,21 +150,34 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
};
|
};
|
||||||
|
|
||||||
// for composite video, go through four steps to get to something that can be painted to the output
|
// for composite video, go through four steps to get to something that can be painted to the output
|
||||||
RenderStage composite_render_stages[] = {
|
const RenderStage composite_render_stages[] = {
|
||||||
{composite_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
|
{composite_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
|
||||||
{composite_separation_filter_program_.get(), separated_texture_.get(), {0.0, 0.5, 0.5}},
|
{composite_separation_filter_program_.get(), separated_texture_.get(), {0.0, 0.5, 0.5}},
|
||||||
{composite_chrominance_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
|
{composite_chrominance_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
// for RGB video, there's only two steps
|
// for s-video, there are two steps — it's like composite but skips separation
|
||||||
RenderStage rgb_render_stages[] = {
|
const RenderStage svideo_render_stages[] = {
|
||||||
|
{svideo_input_shader_program_.get(), separated_texture_.get(), {0.0, 0.5, 0.5}},
|
||||||
|
{composite_chrominance_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
|
||||||
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
// for RGB video, there's also only two steps; a lowpass filter is still applied per physical reality
|
||||||
|
const RenderStage rgb_render_stages[] = {
|
||||||
{rgb_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
|
{rgb_input_shader_program_.get(), composite_texture_.get(), {0.0, 0.0, 0.0}},
|
||||||
{rgb_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
|
{rgb_filter_shader_program_.get(), filtered_texture_.get(), {0.0, 0.0, 0.0}},
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderStage *active_pipeline = get_is_television_output() ? composite_render_stages : rgb_render_stages;
|
const RenderStage *active_pipeline;
|
||||||
|
switch(video_signal_) {
|
||||||
|
default:
|
||||||
|
case VideoSignal::Composite: active_pipeline = composite_render_stages; break;
|
||||||
|
case VideoSignal::SVideo: active_pipeline = svideo_render_stages; break;
|
||||||
|
case VideoSignal::RGB: active_pipeline = rgb_render_stages; break;
|
||||||
|
}
|
||||||
|
|
||||||
if(array_submission.input_size || array_submission.output_size) {
|
if(array_submission.input_size || array_submission.output_size) {
|
||||||
// all drawing will be from the source vertex array and without blending
|
// all drawing will be from the source vertex array and without blending
|
||||||
@ -245,6 +255,7 @@ void OpenGLOutputBuilder::reset_all_OpenGL_state() {
|
|||||||
composite_input_shader_program_ = nullptr;
|
composite_input_shader_program_ = nullptr;
|
||||||
composite_separation_filter_program_ = nullptr;
|
composite_separation_filter_program_ = nullptr;
|
||||||
composite_chrominance_filter_shader_program_ = nullptr;
|
composite_chrominance_filter_shader_program_ = nullptr;
|
||||||
|
svideo_input_shader_program_ = nullptr;
|
||||||
rgb_input_shader_program_ = nullptr;
|
rgb_input_shader_program_ = nullptr;
|
||||||
rgb_filter_shader_program_ = nullptr;
|
rgb_filter_shader_program_ = nullptr;
|
||||||
output_shader_program_ = nullptr;
|
output_shader_program_ = nullptr;
|
||||||
@ -264,6 +275,12 @@ void OpenGLOutputBuilder::set_composite_sampling_function(const std::string &sha
|
|||||||
reset_all_OpenGL_state();
|
reset_all_OpenGL_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLOutputBuilder::set_svideo_sampling_function(const std::string &shader) {
|
||||||
|
std::lock_guard<std::mutex> lock_guard(output_mutex_);
|
||||||
|
svideo_shader_ = shader;
|
||||||
|
reset_all_OpenGL_state();
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::set_rgb_sampling_function(const std::string &shader) {
|
void OpenGLOutputBuilder::set_rgb_sampling_function(const std::string &shader) {
|
||||||
std::lock_guard<std::mutex> lock_guard(output_mutex_);
|
std::lock_guard<std::mutex> lock_guard(output_mutex_);
|
||||||
rgb_shader_ = shader;
|
rgb_shader_ = shader;
|
||||||
@ -273,7 +290,7 @@ void OpenGLOutputBuilder::set_rgb_sampling_function(const std::string &shader) {
|
|||||||
// MARK: - Program compilation
|
// MARK: - Program compilation
|
||||||
|
|
||||||
void OpenGLOutputBuilder::prepare_composite_input_shaders() {
|
void OpenGLOutputBuilder::prepare_composite_input_shaders() {
|
||||||
composite_input_shader_program_ = OpenGL::IntermediateShader::make_source_conversion_shader(composite_shader_, rgb_shader_);
|
composite_input_shader_program_ = OpenGL::IntermediateShader::make_composite_source_shader(composite_shader_, svideo_shader_, rgb_shader_);
|
||||||
composite_input_shader_program_->set_source_texture_unit(source_data_texture_unit);
|
composite_input_shader_program_->set_source_texture_unit(source_data_texture_unit);
|
||||||
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
composite_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||||
|
|
||||||
@ -285,6 +302,7 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders() {
|
|||||||
composite_chrominance_filter_shader_program_->set_source_texture_unit(work_texture_ ? work_texture_unit : separated_texture_unit);
|
composite_chrominance_filter_shader_program_->set_source_texture_unit(work_texture_ ? work_texture_unit : separated_texture_unit);
|
||||||
composite_chrominance_filter_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
composite_chrominance_filter_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||||
|
|
||||||
|
// TODO: the below is related to texture fencing, which is not yet implemented correctly, so not yet enabled.
|
||||||
if(work_texture_) {
|
if(work_texture_) {
|
||||||
composite_input_shader_program_->set_is_double_height(true, 0.0f, 0.0f);
|
composite_input_shader_program_->set_is_double_height(true, 0.0f, 0.0f);
|
||||||
composite_separation_filter_program_->set_is_double_height(true, 0.0f, 0.5f);
|
composite_separation_filter_program_->set_is_double_height(true, 0.0f, 0.5f);
|
||||||
@ -296,6 +314,19 @@ void OpenGLOutputBuilder::prepare_composite_input_shaders() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenGLOutputBuilder::prepare_svideo_input_shaders() {
|
||||||
|
svideo_input_shader_program_ = OpenGL::IntermediateShader::make_svideo_source_shader(svideo_shader_, rgb_shader_);
|
||||||
|
svideo_input_shader_program_->set_source_texture_unit(source_data_texture_unit);
|
||||||
|
svideo_input_shader_program_->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
|
||||||
|
|
||||||
|
// TODO: the below is related to texture fencing, which is not yet implemented correctly, so not yet enabled.
|
||||||
|
if(work_texture_) {
|
||||||
|
svideo_input_shader_program_->set_is_double_height(true, 0.0f, 0.0f);
|
||||||
|
} else {
|
||||||
|
svideo_input_shader_program_->set_is_double_height(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::prepare_rgb_input_shaders() {
|
void OpenGLOutputBuilder::prepare_rgb_input_shaders() {
|
||||||
if(rgb_shader_.size()) {
|
if(rgb_shader_.size()) {
|
||||||
rgb_input_shader_program_ = OpenGL::IntermediateShader::make_rgb_source_shader(rgb_shader_);
|
rgb_input_shader_program_ = OpenGL::IntermediateShader::make_rgb_source_shader(rgb_shader_);
|
||||||
@ -333,6 +364,11 @@ void OpenGLOutputBuilder::prepare_source_vertex_array() {
|
|||||||
Shader::get_input_name(Shader::Input::PhaseTimeAndAmplitude),
|
Shader::get_input_name(Shader::Input::PhaseTimeAndAmplitude),
|
||||||
3, GL_UNSIGNED_BYTE, GL_FALSE, SourceVertexSize,
|
3, GL_UNSIGNED_BYTE, GL_FALSE, SourceVertexSize,
|
||||||
(void *)SourceVertexOffsetOfPhaseTimeAndAmplitude, 1);
|
(void *)SourceVertexOffsetOfPhaseTimeAndAmplitude, 1);
|
||||||
|
|
||||||
|
svideo_input_shader_program_->enable_vertex_attribute_with_pointer(
|
||||||
|
Shader::get_input_name(Shader::Input::InputStart),
|
||||||
|
2, GL_UNSIGNED_SHORT, GL_FALSE, SourceVertexSize,
|
||||||
|
(void *)SourceVertexOffsetOfInputStart, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,9 +399,9 @@ void OpenGLOutputBuilder::prepare_output_vertex_array() {
|
|||||||
|
|
||||||
// MARK: - Public Configuration
|
// MARK: - Public Configuration
|
||||||
|
|
||||||
void OpenGLOutputBuilder::set_output_device(OutputDevice output_device) {
|
void OpenGLOutputBuilder::set_video_signal(VideoSignal video_signal) {
|
||||||
if(output_device_ != output_device) {
|
if(video_signal_ != video_signal) {
|
||||||
output_device_ = output_device;
|
video_signal_ = video_signal;
|
||||||
composite_src_output_y_ = 0;
|
composite_src_output_y_ = 0;
|
||||||
last_output_width_ = 0;
|
last_output_width_ = 0;
|
||||||
last_output_height_ = 0;
|
last_output_height_ = 0;
|
||||||
@ -413,6 +449,7 @@ void OpenGLOutputBuilder::set_colour_space_uniforms() {
|
|||||||
if(composite_input_shader_program_) composite_input_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
if(composite_input_shader_program_) composite_input_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||||
if(composite_separation_filter_program_) composite_separation_filter_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
if(composite_separation_filter_program_) composite_separation_filter_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||||
if(composite_chrominance_filter_shader_program_) composite_chrominance_filter_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
if(composite_chrominance_filter_shader_program_) composite_chrominance_filter_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||||
|
if(svideo_input_shader_program_) svideo_input_shader_program_->set_colour_conversion_matrices(fromRGB, toRGB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::set_gamma() {
|
void OpenGLOutputBuilder::set_gamma() {
|
||||||
@ -433,7 +470,8 @@ float OpenGLOutputBuilder::get_composite_output_width() const {
|
|||||||
|
|
||||||
void OpenGLOutputBuilder::set_output_shader_width() {
|
void OpenGLOutputBuilder::set_output_shader_width() {
|
||||||
if(output_shader_program_) {
|
if(output_shader_program_) {
|
||||||
const float width = get_is_television_output() ? get_composite_output_width() : 1.0f;
|
// For anything that isn't RGB, scale so that sampling is in-phase with the colour subcarrier.
|
||||||
|
const float width = (video_signal_ == VideoSignal::RGB) ? 1.0f : get_composite_output_width();
|
||||||
output_shader_program_->set_input_width_scaler(width);
|
output_shader_program_->set_input_width_scaler(width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,6 +502,10 @@ void OpenGLOutputBuilder::set_timing_uniforms() {
|
|||||||
composite_input_shader_program_->set_width_scalers(1.0f, output_width);
|
composite_input_shader_program_->set_width_scalers(1.0f, output_width);
|
||||||
composite_input_shader_program_->set_extension(0.0f);
|
composite_input_shader_program_->set_extension(0.0f);
|
||||||
}
|
}
|
||||||
|
if(svideo_input_shader_program_) {
|
||||||
|
svideo_input_shader_program_->set_width_scalers(1.0f, output_width);
|
||||||
|
svideo_input_shader_program_->set_extension(0.0f);
|
||||||
|
}
|
||||||
if(rgb_input_shader_program_) {
|
if(rgb_input_shader_program_) {
|
||||||
rgb_input_shader_program_->set_width_scalers(1.0f, 1.0f);
|
rgb_input_shader_program_->set_width_scalers(1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class OpenGLOutputBuilder {
|
|||||||
ColourSpace colour_space_;
|
ColourSpace colour_space_;
|
||||||
unsigned int colour_cycle_numerator_;
|
unsigned int colour_cycle_numerator_;
|
||||||
unsigned int colour_cycle_denominator_;
|
unsigned int colour_cycle_denominator_;
|
||||||
OutputDevice output_device_;
|
VideoSignal video_signal_;
|
||||||
float gamma_;
|
float gamma_;
|
||||||
|
|
||||||
// timing information to allow reasoning about input information
|
// timing information to allow reasoning about input information
|
||||||
@ -49,12 +49,14 @@ class OpenGLOutputBuilder {
|
|||||||
|
|
||||||
// Other things the caller may have provided.
|
// Other things the caller may have provided.
|
||||||
std::string composite_shader_;
|
std::string composite_shader_;
|
||||||
|
std::string svideo_shader_;
|
||||||
std::string rgb_shader_;
|
std::string rgb_shader_;
|
||||||
GLint target_framebuffer_ = 0;
|
GLint target_framebuffer_ = 0;
|
||||||
|
|
||||||
// Methods used by the OpenGL code
|
// Methods used by the OpenGL code
|
||||||
void prepare_output_shader();
|
void prepare_output_shader();
|
||||||
void prepare_rgb_input_shaders();
|
void prepare_rgb_input_shaders();
|
||||||
|
void prepare_svideo_input_shaders();
|
||||||
void prepare_composite_input_shaders();
|
void prepare_composite_input_shaders();
|
||||||
|
|
||||||
void prepare_output_vertex_array();
|
void prepare_output_vertex_array();
|
||||||
@ -73,6 +75,8 @@ class OpenGLOutputBuilder {
|
|||||||
std::unique_ptr<OpenGL::IntermediateShader> composite_separation_filter_program_;
|
std::unique_ptr<OpenGL::IntermediateShader> composite_separation_filter_program_;
|
||||||
std::unique_ptr<OpenGL::IntermediateShader> composite_chrominance_filter_shader_program_;
|
std::unique_ptr<OpenGL::IntermediateShader> composite_chrominance_filter_shader_program_;
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGL::IntermediateShader> svideo_input_shader_program_;
|
||||||
|
|
||||||
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program_;
|
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program_;
|
||||||
std::unique_ptr<OpenGL::IntermediateShader> rgb_filter_shader_program_;
|
std::unique_ptr<OpenGL::IntermediateShader> rgb_filter_shader_program_;
|
||||||
|
|
||||||
@ -99,7 +103,6 @@ class OpenGLOutputBuilder {
|
|||||||
GLsync fence_;
|
GLsync fence_;
|
||||||
float get_composite_output_width() const;
|
float get_composite_output_width() const;
|
||||||
void set_output_shader_width();
|
void set_output_shader_width();
|
||||||
bool get_is_television_output();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// These two are protected by output_mutex_.
|
// These two are protected by output_mutex_.
|
||||||
@ -130,8 +133,8 @@ class OpenGLOutputBuilder {
|
|||||||
return std::unique_lock<std::mutex>(output_mutex_);
|
return std::unique_lock<std::mutex>(output_mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline OutputDevice get_output_device() {
|
inline VideoSignal get_output_device() {
|
||||||
return output_device_;
|
return video_signal_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint16_t get_composite_output_y() {
|
inline uint16_t get_composite_output_y() {
|
||||||
@ -147,12 +150,13 @@ class OpenGLOutputBuilder {
|
|||||||
composite_src_output_y_++;
|
composite_src_output_y_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_target_framebuffer(GLint target_framebuffer);
|
void set_target_framebuffer(GLint);
|
||||||
void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty);
|
void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty);
|
||||||
void set_openGL_context_will_change(bool should_delete_resources);
|
void set_openGL_context_will_change(bool should_delete_resources);
|
||||||
void set_composite_sampling_function(const std::string &shader);
|
void set_composite_sampling_function(const std::string &);
|
||||||
void set_rgb_sampling_function(const std::string &shader);
|
void set_svideo_sampling_function(const std::string &);
|
||||||
void set_output_device(OutputDevice output_device);
|
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_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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const std::s
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_shader(const std::string &composite_shader, const std::string &rgb_shader) {
|
std::unique_ptr<IntermediateShader> IntermediateShader::make_composite_source_shader(const std::string &composite_shader, const std::string &svideo_shader, const std::string &rgb_shader) {
|
||||||
std::ostringstream fragment_shader;
|
std::ostringstream fragment_shader;
|
||||||
fragment_shader <<
|
fragment_shader <<
|
||||||
"#version 150\n"
|
"#version 150\n"
|
||||||
@ -124,23 +124,30 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_s
|
|||||||
|
|
||||||
"out vec4 fragColour;"
|
"out vec4 fragColour;"
|
||||||
|
|
||||||
"uniform usampler2D texID;";
|
"uniform usampler2D texID;"
|
||||||
|
<< composite_shader;
|
||||||
|
|
||||||
if(!composite_shader.size()) {
|
if(composite_shader.empty()) {
|
||||||
std::ostringstream derived_composite_sample;
|
if(!svideo_shader.empty()) {
|
||||||
derived_composite_sample <<
|
fragment_shader <<
|
||||||
rgb_shader <<
|
svideo_shader <<
|
||||||
"uniform mat3 rgbToLumaChroma;"
|
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||||
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
"{"
|
||||||
"{"
|
"vec2 svideoColour = svideo_sample(texID, coordinate, iCoordinate, phase);"
|
||||||
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
|
"return mix(svideoColour.x, svideoColour.y, amplitude);"
|
||||||
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
|
"}";
|
||||||
"vec2 quadrature = vec2(cos(phase), -sin(phase)) * amplitude;"
|
} else {
|
||||||
"return dot(lumaChromaColour, vec3(1.0 - amplitude, quadrature));"
|
fragment_shader <<
|
||||||
"}";
|
rgb_shader <<
|
||||||
fragment_shader << derived_composite_sample.str();
|
"uniform mat3 rgbToLumaChroma;"
|
||||||
} else {
|
"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
|
||||||
fragment_shader << composite_shader;
|
"{"
|
||||||
|
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
|
||||||
|
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
|
||||||
|
"vec2 quadrature = vec2(cos(phase), -sin(phase)) * amplitude;"
|
||||||
|
"return dot(lumaChromaColour, vec3(1.0 - amplitude, quadrature));"
|
||||||
|
"}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment_shader <<
|
fragment_shader <<
|
||||||
@ -152,6 +159,44 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_s
|
|||||||
return make_shader(fragment_shader.str(), true, true);
|
return make_shader(fragment_shader.str(), true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<IntermediateShader> IntermediateShader::make_svideo_source_shader(const std::string &svideo_shader, const std::string &rgb_shader) {
|
||||||
|
std::ostringstream fragment_shader;
|
||||||
|
fragment_shader <<
|
||||||
|
"#version 150\n"
|
||||||
|
|
||||||
|
"in vec2 inputPositionsVarying[11];"
|
||||||
|
"in vec2 iInputPositionVarying;"
|
||||||
|
"in vec3 phaseAndAmplitudeVarying;"
|
||||||
|
|
||||||
|
"out vec3 fragColour;"
|
||||||
|
|
||||||
|
"uniform usampler2D texID;"
|
||||||
|
<< svideo_shader;
|
||||||
|
|
||||||
|
if(svideo_shader.empty()) {
|
||||||
|
fragment_shader
|
||||||
|
<< rgb_shader <<
|
||||||
|
"uniform mat3 rgbToLumaChroma;"
|
||||||
|
"vec2 svideo_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase)"
|
||||||
|
"{"
|
||||||
|
"vec3 rgbColour = clamp(rgb_sample(texID, coordinate, iCoordinate), vec3(0.0), vec3(1.0));"
|
||||||
|
"vec3 lumaChromaColour = rgbToLumaChroma * rgbColour;"
|
||||||
|
"vec2 quadrature = vec2(cos(phase), -sin(phase));"
|
||||||
|
"return vec2(lumaChromaColour.x, 0.5 + dot(quadrature, lumaChromaColour.yz) * 0.5);"
|
||||||
|
"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment_shader <<
|
||||||
|
"void main(void)"
|
||||||
|
"{"
|
||||||
|
"vec2 sample = svideo_sample(texID, inputPositionsVarying[5], iInputPositionVarying, phaseAndAmplitudeVarying.x);"
|
||||||
|
"vec2 quadrature = vec2(cos(phaseAndAmplitudeVarying.x), -sin(phaseAndAmplitudeVarying.x)) * 0.5 * phaseAndAmplitudeVarying.z;"
|
||||||
|
"fragColour = vec3(sample.x, vec2(0.5) + (sample.y * quadrature));"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
return make_shader(fragment_shader.str(), true, true);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_source_shader(const std::string &rgb_shader) {
|
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_source_shader(const std::string &rgb_shader) {
|
||||||
std::ostringstream fragment_shader;
|
std::ostringstream fragment_shader;
|
||||||
fragment_shader <<
|
fragment_shader <<
|
||||||
|
@ -43,30 +43,50 @@ public:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs and returns an intermediate shader that will take runs from the inputPositions,
|
Constructs and returns an intermediate shader that will take runs from the inputPositions,
|
||||||
converting them to single-channel composite values using @c composite_shader if supplied
|
converting them to single-channel composite values using @c composite_shader if non-empty
|
||||||
or @c rgb_shader and a reference composite conversion if @c composite_shader is @c nullptr.
|
or a reference composite conversion of @c svideo_shader (first preference) or
|
||||||
|
@c rgb_shader (second preference) otherwise.
|
||||||
|
|
||||||
|
[input format] => one-channel composite.
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<IntermediateShader> make_source_conversion_shader(const std::string &composite_shader, const std::string &rgb_shader);
|
static std::unique_ptr<IntermediateShader> make_composite_source_shader(const std::string &composite_shader, const std::string &svideo_shader, const std::string &rgb_shader);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs and returns an intermediate shader that will take runs from the inputPositions,
|
||||||
|
converting them to two-channel svideo values using @c svideo_shader if non-empty
|
||||||
|
or a reference svideo conversion of @c rgb_shader otherwise.
|
||||||
|
|
||||||
|
[input format] => three-channel Y, noisy (m, n).
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<IntermediateShader> make_svideo_source_shader(const std::string &svideo_shader, const std::string &rgb_shader);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs and returns an intermediate shader that will take runs from the inputPositions,
|
Constructs and returns an intermediate shader that will take runs from the inputPositions,
|
||||||
converting them to RGB values using @c rgb_shader.
|
converting them to RGB values using @c rgb_shader.
|
||||||
|
|
||||||
|
[input format] => three-channel RGB.
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<IntermediateShader> make_rgb_source_shader(const std::string &rgb_shader);
|
static std::unique_ptr<IntermediateShader> make_rgb_source_shader(const std::string &rgb_shader);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs and returns an intermediate shader that will read composite samples from the R channel,
|
Constructs and returns an intermediate shader that will read composite samples from the R channel,
|
||||||
filter then to obtain luminance, stored to R, and to separate out unfiltered chrominance, store to G and B.
|
filter then to obtain luminance, stored to R, and to separate out unfiltered chrominance, store to G and B.
|
||||||
|
|
||||||
|
one-channel composite => three-channel Y, noisy (m, n).
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<IntermediateShader> make_chroma_luma_separation_shader();
|
static std::unique_ptr<IntermediateShader> make_chroma_luma_separation_shader();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs and returns an intermediate shader that will pass R through unchanged while filtering G and B.
|
Constructs and returns an intermediate shader that will pass R through unchanged while filtering G and B.
|
||||||
|
|
||||||
|
three-channel Y, noisy (m, n) => three-channel RGB.
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
|
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Constructs and returns an intermediate shader that will filter R, G and B.
|
Constructs and returns an intermediate shader that will filter R, G and B.
|
||||||
|
|
||||||
|
three-channel RGB => frequency-limited three-channel RGB.
|
||||||
*/
|
*/
|
||||||
static std::unique_ptr<IntermediateShader> make_rgb_filter_shader();
|
static std::unique_ptr<IntermediateShader> make_rgb_filter_shader();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user