1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-22 12:33:29 +00:00

Onward with carrying this through to the bitter end, this at least results in all the appropriate knowledge and call-ins occuring at the CRT.

This commit is contained in:
Thomas Harte 2016-02-04 22:28:50 -05:00
parent 09e11469c3
commit e0d51408e4
20 changed files with 286 additions and 290 deletions

View File

@ -25,6 +25,14 @@ Machine::Machine() :
_tiaInputValue{0xff, 0xff}
{
_crt = new Outputs::CRT(228, Outputs::CRT::DisplayType::NTSC60, 1, 2);
_crt->set_composite_sampling_function(
"float sample(vec2 coordinate, float phase)\n"
"{\n"
"vec2 c = texture(texID, coordinate).rg;"
"float y = 0.1 + c.x * 0.91071428571429;\n"
"float aOffset = 6.283185308 * (c.y - 3.0 / 16.0) * 1.14285714285714;\n"
"return y + step(0.03125, c.y) * 0.1 * cos(phase - aOffset);\n"
"}");
memset(_collisions, 0xff, sizeof(_collisions));
set_reset_line(true);
}
@ -147,19 +155,6 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset)
pixel[1] = outputColour&0xf0;
}
const char *Machine::get_signal_decoder()
{
return
"float sample(vec2 coordinate, float phase)\n"
"{\n"
"vec2 c = texture(texID, coordinate).rg;"
"float y = 0.1 + c.x * 0.91071428571429;\n"
"float aOffset = 6.283185308 * (c.y - 3.0 / 16.0) * 1.14285714285714;\n"
"return y + step(0.03125, c.y) * 0.1 * cos(phase - aOffset);\n"
"}";
}
// in imputing the knowledge that all we're dealing with is the rollover from 159 to 0,
// this is faster than the straightforward +1)%160 per profiling
#define increment_object_counter(c) _objectCounter[c] = (_objectCounter[c]+1)&~((158-_objectCounter[c]) >> 8)

View File

@ -10,7 +10,7 @@
#define Atari2600_cpp
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Outputs/CRT.hpp"
#include "../../Outputs/CRT/CRT.hpp"
#include <stdint.h>
#include "Atari2600Inputs.h"
@ -19,7 +19,6 @@ namespace Atari2600 {
class Machine: public CPU6502::Processor<Machine> {
public:
Machine();
~Machine();
@ -32,8 +31,6 @@ class Machine: public CPU6502::Processor<Machine> {
Outputs::CRT *get_crt() { return _crt; }
const char *get_signal_decoder();
private:
uint8_t *_rom, *_romPages[4], _ram[128];
size_t _rom_size;

View File

@ -29,6 +29,13 @@ Machine::Machine() :
_currentOutputLine(0),
_crt(Outputs::CRT(crt_cycles_per_line, Outputs::CRT::DisplayType::PAL50, 1, 1))
{
_crt.set_rgb_sampling_function(
"vec4 sample(vec2 coordinate)\n"
"{\n"
"float texValue = texture(texID, coordinate).r;\n"
"return vec4( step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)), 1.0);\n"
"}");
memset(_keyStates, 0, sizeof(_keyStates));
memset(_palette, 0xf, sizeof(_palette));
_interruptStatus = 0x02;
@ -455,7 +462,7 @@ inline void Machine::update_display()
}
int pixels_to_output = std::min(_frameCycles - _displayOutputPosition, 104 - line_position);
int pixels_to_output = std::min(104 - line_position, cycles_left);
_displayOutputPosition += pixels_to_output;
// printf("<- %d ->", pixels_to_output);
if(_screenMode >= 4)
@ -528,7 +535,7 @@ inline void Machine::update_display()
if(line_position >= 104)
{
int pixels_to_output = std::min(_frameCycles - _displayOutputPosition, 128 - line_position);
int pixels_to_output = std::min(128 - line_position, cycles_left);
_crt.output_blank((unsigned int)pixels_to_output * crt_cycles_multiplier);
_displayOutputPosition += pixels_to_output;
@ -556,16 +563,6 @@ inline void Machine::update_display()
}
}
const char *Machine::get_signal_decoder()
{
return
"vec4 sample(vec2 coordinate)\n"
"{\n"
"float texValue = texture(texID, coordinate).r;\n"
"return vec4( step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)), 1.0);\n"
"}";
}
void Machine::set_key_state(Key key, bool isPressed)
{
if(key == KeyBreak)

View File

@ -10,7 +10,7 @@
#define Electron_hpp
#include "../../Processors/6502/CPU6502.hpp"
#include "../../Outputs/CRT.hpp"
#include "../../Outputs/CRT/CRT.hpp"
#include "../../Outputs/Speaker.hpp"
#include "../../Storage/Tape/Tape.hpp"
#include <stdint.h>
@ -148,7 +148,6 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
Outputs::CRT *get_crt() { return &_crt; }
Outputs::Speaker *get_speaker() { return &_speaker; }
const char *get_signal_decoder();
virtual void tape_did_change_interrupt_status(Tape *tape);

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC421C62D0B3001CAC5F /* CRT.cpp */; };
4B0CCC471C62D1A8001CAC5F /* CRTOpenGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */; };
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */; };
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B1414571B58879D00E04248 /* CPU6502.cpp */; };
@ -17,7 +19,6 @@
4B2E2D951C399D1200138695 /* ElectronDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B2E2D931C399D1200138695 /* ElectronDocument.xib */; };
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */; };
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E2D9B1C3A070400138695 /* Electron.cpp */; };
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B366DFA1B5C165A0026627B /* CRT.cpp */; };
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE4A1C3B3B0C0093A61B /* CSAtari2600.mm */; };
4B55CE4E1C3B3BDA0093A61B /* CSMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE4D1C3B3BDA0093A61B /* CSMachine.mm */; };
4B55CE541C3B7ABF0093A61B /* CSElectron.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE531C3B7ABF0093A61B /* CSElectron.mm */; };
@ -28,6 +29,7 @@
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */; };
4B69FB461C4D950F00B5F0AA /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B69FB451C4D950F00B5F0AA /* libz.tbd */; };
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */; };
4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B92EAC91B7C112B00246143 /* TimingTests.swift */; };
4BB298EE1B587D8400A49093 /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E01B587D8300A49093 /* 6502_functional_test.bin */; };
4BB298EF1B587D8400A49093 /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E11B587D8300A49093 /* AllSuiteA.bin */; };
@ -325,6 +327,10 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRT.cpp; sourceTree = "<group>"; };
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRT.hpp; sourceTree = "<group>"; };
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRTFrame.h; sourceTree = "<group>"; };
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTOpenGL.cpp; sourceTree = "<group>"; };
4B0EBFB61C487F2F00A11F35 /* AudioQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioQueue.h; sourceTree = "<group>"; };
4B0EBFB71C487F2F00A11F35 /* AudioQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioQueue.m; sourceTree = "<group>"; };
4B1414501B58848C00E04248 /* ClockSignal-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ClockSignal-Bridging-Header.h"; sourceTree = "<group>"; };
@ -337,15 +343,12 @@
4B2409531C45AB05004DA684 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = ../../Outputs/Speaker.cpp; sourceTree = "<group>"; };
4B2409541C45AB05004DA684 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = ../../Outputs/Speaker.hpp; sourceTree = "<group>"; };
4B24095A1C45DF85004DA684 /* Stepper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Stepper.hpp; sourceTree = "<group>"; };
4B2632551B631A510082A461 /* CRTFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CRTFrame.h; path = ../../Outputs/CRTFrame.h; sourceTree = "<group>"; };
4B2E2D941C399D1200138695 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ElectronDocument.xib; sourceTree = "<group>"; };
4B2E2D971C3A06EC00138695 /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
4B2E2D981C3A06EC00138695 /* Atari2600.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Atari2600.hpp; sourceTree = "<group>"; };
4B2E2D991C3A06EC00138695 /* Atari2600Inputs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Atari2600Inputs.h; sourceTree = "<group>"; };
4B2E2D9B1C3A070400138695 /* Electron.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Electron.cpp; path = Electron/Electron.cpp; sourceTree = "<group>"; };
4B2E2D9C1C3A070400138695 /* Electron.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Electron.hpp; path = Electron/Electron.hpp; sourceTree = "<group>"; };
4B366DFA1B5C165A0026627B /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CRT.cpp; path = ../../Outputs/CRT.cpp; sourceTree = "<group>"; };
4B366DFB1B5C165A0026627B /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRT.hpp; path = ../../Outputs/CRT.hpp; sourceTree = "<group>"; };
4B55CE491C3B3B0C0093A61B /* CSAtari2600.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSAtari2600.h; sourceTree = "<group>"; };
4B55CE4A1C3B3B0C0093A61B /* CSAtari2600.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CSAtari2600.mm; sourceTree = "<group>"; };
4B55CE4C1C3B3BDA0093A61B /* CSMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSMachine.h; sourceTree = "<group>"; };
@ -363,6 +366,7 @@
4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TapeUEF.cpp; sourceTree = "<group>"; };
4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TapeUEF.hpp; sourceTree = "<group>"; };
4B69FB451C4D950F00B5F0AA /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CRTFrameBuilder.cpp; sourceTree = "<group>"; };
4B92EAC91B7C112B00246143 /* TimingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimingTests.swift; sourceTree = "<group>"; };
4BAE587D1C447B7A005B9AF0 /* KeyCodes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyCodes.h; sourceTree = "<group>"; };
4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clock SignalTests-Bridging-Header.h"; sourceTree = "<group>"; };
@ -678,6 +682,19 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4B0CCC411C62D0B3001CAC5F /* CRT */ = {
isa = PBXGroup;
children = (
4B0CCC421C62D0B3001CAC5F /* CRT.cpp */,
4B0CCC431C62D0B3001CAC5F /* CRT.hpp */,
4B0CCC441C62D0B3001CAC5F /* CRTFrame.h */,
4B0CCC461C62D1A8001CAC5F /* CRTOpenGL.cpp */,
4B7BFEFD1C6446EF00089C1C /* CRTFrameBuilder.cpp */,
);
name = CRT;
path = ../../Outputs/CRT;
sourceTree = "<group>";
};
4B1414561B58879D00E04248 /* 6502 */ = {
isa = PBXGroup;
children = (
@ -730,9 +747,7 @@
4B366DFD1B5C165F0026627B /* Outputs */ = {
isa = PBXGroup;
children = (
4B366DFA1B5C165A0026627B /* CRT.cpp */,
4B366DFB1B5C165A0026627B /* CRT.hpp */,
4B2632551B631A510082A461 /* CRTFrame.h */,
4B0CCC411C62D0B3001CAC5F /* CRT */,
4B2409531C45AB05004DA684 /* Speaker.cpp */,
4B2409541C45AB05004DA684 /* Speaker.hpp */,
);
@ -1588,10 +1603,13 @@
buildActionMask = 2147483647;
files = (
4B55CE541C3B7ABF0093A61B /* CSElectron.mm in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
4B55CE4B1C3B3B0C0093A61B /* CSAtari2600.mm in Sources */,
4B55CE581C3B7D360093A61B /* Atari2600Document.swift in Sources */,
4B0CCC471C62D1A8001CAC5F /* CRTOpenGL.cpp in Sources */,
4B0EBFB81C487F2F00A11F35 /* AudioQueue.m in Sources */,
4B7BFEFE1C6446EF00089C1C /* CRTFrameBuilder.cpp in Sources */,
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
4B69FB441C4D941400B5F0AA /* TapeUEF.cpp in Sources */,
4B2409551C45AB05004DA684 /* Speaker.cpp in Sources */,
@ -1599,7 +1617,6 @@
4B2E2D9D1C3A070400138695 /* Electron.cpp in Sources */,
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */,
4B55CE5D1C3B7D6F0093A61B /* CSCathodeRayView.m in Sources */,
4B366DFC1B5C165A0026627B /* CRT.cpp in Sources */,
4B2E2D9A1C3A06EC00138695 /* Atari2600.cpp in Sources */,
4B14145B1B58879D00E04248 /* CPU6502.cpp in Sources */,
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */,

View File

@ -19,7 +19,7 @@ class Atari2600Document: MachineDocument {
override func windowControllerDidLoadNib(aController: NSWindowController) {
super.windowControllerDidLoadNib(aController)
atari2600.view = openGLView
openGLView.frameBounds = CGRectMake(0.1, 0.1, 0.8, 0.8)
// openGLView.frameBounds = CGRectMake(0.1, 0.1, 0.8, 0.8)
}
override class func autosavesInPlace() -> Bool {

View File

@ -61,6 +61,10 @@ class ElectronDocument: MachineDocument {
electron.runForNumberOfCycles(numberOfCycles)
}
override func openGLViewDrawView(view: CSCathodeRayView) {
electron.drawViewForPixelSize(view.backingSize)
}
// MARK: CSOpenGLViewResponderDelegate
override func keyDown(event: NSEvent) {
electron.setKey(event.keyCode, isPressed: true)

View File

@ -46,6 +46,7 @@ class MachineDocument: NSDocument, CSCathodeRayViewDelegate, CSCathodeRayViewRes
lastCycleCount = cycleCount
}
func openGLViewDrawView(view: CSCathodeRayView) {}
func runForNumberOfCycles(numberOfCycles: Int32) {}
// MARK: CSOpenGLViewResponderDelegate

View File

@ -7,13 +7,13 @@
//
#import <Foundation/Foundation.h>
#import "CRTFrame.h"
#import <AppKit/AppKit.h>
@class CSCathodeRayView;
@protocol CSCathodeRayViewDelegate
- (void)openGLView:(nonnull CSCathodeRayView *)view didUpdateToTime:(CVTimeStamp)time;
- (void)openGLViewDrawView:(nonnull CSCathodeRayView *)view;
@end
@protocol CSCathodeRayViewResponderDelegate <NSObject>
@ -22,10 +22,6 @@
- (void)flagsChanged:(nonnull NSEvent *)newModifiers;
@end
typedef NS_ENUM(NSInteger, CSCathodeRayViewSignalType) {
CSCathodeRayViewSignalTypeNTSC,
CSCathodeRayViewSignalTypeRGB
};
@interface CSCathodeRayView : NSOpenGLView
@ -34,11 +30,6 @@ typedef NS_ENUM(NSInteger, CSCathodeRayViewSignalType) {
- (void)invalidate;
- (BOOL)pushFrame:(nonnull CRTFrame *)crtFrame;
- (void)setSignalDecoder:(nonnull NSString *)decoder type:(CSCathodeRayViewSignalType)type;
// these are relative to a [0, 1] range in both width and height;
// default is .origin = (0, 0), .size = (1, 1)
@property (nonatomic, assign) CGRect frameBounds;
@property (nonatomic, readonly) CGSize backingSize;
@end

View File

@ -10,38 +10,16 @@
@import CoreVideo;
@import GLKit;
#import <OpenGL/gl3.h>
#import <OpenGL/gl3ext.h>
#import <libkern/OSAtomic.h>
//#import <OpenGL/gl3ext.h>
//#import <libkern/OSAtomic.h>
@implementation CSCathodeRayView {
CVDisplayLinkRef displayLink;
GLuint _vertexShader, _fragmentShader;
GLuint _shaderProgram;
GLuint _arrayBuffer, _vertexArray;
GLint _positionAttribute;
GLint _textureCoordinatesAttribute;
GLint _lateralAttribute;
GLint _textureSizeUniform, _windowSizeUniform;
GLint _boundsOriginUniform, _boundsSizeUniform;
GLint _alphaUniform;
GLuint _textureName, _shadowMaskTextureName;
CRTSize _textureSize;
CRTFrame *_crtFrame;
NSString *_signalDecoder;
CSCathodeRayViewSignalType _signalType;
int32_t _signalDecoderGeneration;
int32_t _compiledSignalDecoderGeneration;
CVDisplayLinkRef _displayLink;
CGRect _aspectRatioCorrectedBounds;
}
- (GLuint)textureForImageNamed:(NSString *)name
/*- (GLuint)textureForImageNamed:(NSString *)name
{
NSImage *const image = [NSImage imageNamed:name];
NSBitmapImageRep *bitmapRepresentation = [[NSBitmapImageRep alloc] initWithData: [image TIFFRepresentation]];
@ -57,7 +35,7 @@
glGenerateMipmap(GL_TEXTURE_2D);
return textureName;
}
}*/
- (void)prepareOpenGL
{
@ -66,29 +44,29 @@
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
// Create a display link capable of being used with all active displays
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
// Set the renderer output callback function
CVDisplayLinkSetOutputCallback(displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self));
// Set the display link for the current renderer
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
// install the shadow mask texture as the second texture
glActiveTexture(GL_TEXTURE1);
/* glActiveTexture(GL_TEXTURE1);
_shadowMaskTextureName = [self textureForImageNamed:@"ShadowMask"];
// otherwise, we'll be working on the first texture
glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE0);*/
// get the shader ready, set the clear colour
[self.openGLContext makeCurrentContext];
glClearColor(0.0, 0.0, 0.0, 1.0);
// Activate the display link
CVDisplayLinkStart(displayLink);
CVDisplayLinkStart(_displayLink);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
@ -103,27 +81,26 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
- (void)invalidate
{
CVDisplayLinkStop(displayLink);
CVDisplayLinkStop(_displayLink);
}
- (void)dealloc
{
// Release the display link
CVDisplayLinkRelease(displayLink);
CVDisplayLinkRelease(_displayLink);
// Release OpenGL buffers
[self.openGLContext makeCurrentContext];
glDeleteBuffers(1, &_arrayBuffer);
glDeleteVertexArrays(1, &_vertexArray);
glDeleteTextures(1, &_textureName);
glDeleteTextures(1, &_shadowMaskTextureName);
glDeleteProgram(_shaderProgram);
// [self.openGLContext makeCurrentContext];
// glDeleteBuffers(1, &_arrayBuffer);
// glDeleteVertexArrays(1, &_vertexArray);
// glDeleteTextures(1, &_textureName);
// glDeleteTextures(1, &_shadowMaskTextureName);
// glDeleteProgram(_shaderProgram);
}
- (NSPoint)backingViewSize
- (CGSize)backingSize
{
NSPoint backingSize = {.x = self.bounds.size.width, .y = self.bounds.size.height};
return [self convertPointToBacking:backingSize];
return [self convertSizeToBacking:self.bounds.size];
}
- (void)reshape
@ -133,49 +110,49 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self.openGLContext makeCurrentContext];
CGLLockContext([[self openGLContext] CGLContextObj]);
NSPoint viewSize = [self backingViewSize];
glViewport(0, 0, (GLsizei)viewSize.x, (GLsizei)viewSize.y);
CGSize viewSize = [self backingSize];
glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height);
[self pushSizeUniforms];
// [self pushSizeUniforms];
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
- (void)setFrameBounds:(CGRect)frameBounds
{
_frameBounds = frameBounds;
[self.openGLContext makeCurrentContext];
CGLLockContext([[self openGLContext] CGLContextObj]);
[self pushSizeUniforms];
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
- (void)pushSizeUniforms
{
if(_shaderProgram)
{
NSPoint viewSize = [self backingViewSize];
if(_windowSizeUniform >= 0)
{
glUniform2f(_windowSizeUniform, (GLfloat)viewSize.x, (GLfloat)viewSize.y);
}
CGFloat outputAspectRatioMultiplier = (viewSize.x / viewSize.y) / (4.0 / 3.0);
// NSLog(@"%0.2f v %0.2f", outputAspectRatio, desiredOutputAspectRatio);
_aspectRatioCorrectedBounds = _frameBounds;
CGFloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _frameBounds.size.width;
_aspectRatioCorrectedBounds.origin.x -= bonusWidth * 0.5f * _aspectRatioCorrectedBounds.size.width;
_aspectRatioCorrectedBounds.size.width *= outputAspectRatioMultiplier;
if(_boundsOriginUniform >= 0) glUniform2f(_boundsOriginUniform, (GLfloat)_aspectRatioCorrectedBounds.origin.x, (GLfloat)_aspectRatioCorrectedBounds.origin.y);
if(_boundsSizeUniform >= 0) glUniform2f(_boundsSizeUniform, (GLfloat)_aspectRatioCorrectedBounds.size.width, (GLfloat)_aspectRatioCorrectedBounds.size.height);
}
}
//- (void)setFrameBounds:(CGRect)frameBounds
//{
// _frameBounds = frameBounds;
//
// [self.openGLContext makeCurrentContext];
// CGLLockContext([[self openGLContext] CGLContextObj]);
//
// [self pushSizeUniforms];
//
// CGLUnlockContext([[self openGLContext] CGLContextObj]);
//}
//
//- (void)pushSizeUniforms
//{
// if(_shaderProgram)
// {
// NSPoint viewSize = [self backingViewSize];
// if(_windowSizeUniform >= 0)
// {
// glUniform2f(_windowSizeUniform, (GLfloat)viewSize.x, (GLfloat)viewSize.y);
// }
//
// CGFloat outputAspectRatioMultiplier = (viewSize.x / viewSize.y) / (4.0 / 3.0);
//
//// NSLog(@"%0.2f v %0.2f", outputAspectRatio, desiredOutputAspectRatio);
// _aspectRatioCorrectedBounds = _frameBounds;
//
// CGFloat bonusWidth = (outputAspectRatioMultiplier - 1.0f) * _frameBounds.size.width;
// _aspectRatioCorrectedBounds.origin.x -= bonusWidth * 0.5f * _aspectRatioCorrectedBounds.size.width;
// _aspectRatioCorrectedBounds.size.width *= outputAspectRatioMultiplier;
//
// if(_boundsOriginUniform >= 0) glUniform2f(_boundsOriginUniform, (GLfloat)_aspectRatioCorrectedBounds.origin.x, (GLfloat)_aspectRatioCorrectedBounds.origin.y);
// if(_boundsSizeUniform >= 0) glUniform2f(_boundsSizeUniform, (GLfloat)_aspectRatioCorrectedBounds.size.width, (GLfloat)_aspectRatioCorrectedBounds.size.height);
// }
//}
- (void)awakeFromNib
{
@ -205,10 +182,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
self.wantsBestResolutionOpenGLSurface = YES;
// establish default instance variable values
self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0);
// self.frameBounds = CGRectMake(0.0, 0.0, 1.0, 1.0);
}
- (GLint)formatForDepth:(unsigned int)depth
/*- (GLint)formatForDepth:(unsigned int)depth
{
switch(depth)
{
@ -489,7 +466,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
}*/
- (void)drawRect:(NSRect)dirtyRect
{
@ -501,18 +478,20 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self.openGLContext makeCurrentContext];
CGLLockContext([[self openGLContext] CGLContextObj]);
while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) {
_compiledSignalDecoderGeneration = _signalDecoderGeneration;
[self prepareShader];
}
glClear(GL_COLOR_BUFFER_BIT);
if (_crtFrame)
{
if(_textureSizeUniform >= 0) glUniform2f(_textureSizeUniform, _crtFrame->size.width, _crtFrame->size.height);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_crtFrame->number_of_vertices);
}
[self.delegate openGLViewDrawView:self];
// while((!_shaderProgram || (_signalDecoderGeneration != _compiledSignalDecoderGeneration)) && _signalDecoder) {
// _compiledSignalDecoderGeneration = _signalDecoderGeneration;
// [self prepareShader];
// }
//
// glClear(GL_COLOR_BUFFER_BIT);
//
// if (_crtFrame)
// {
// if(_textureSizeUniform >= 0) glUniform2f(_textureSizeUniform, _crtFrame->size.width, _crtFrame->size.height);
// glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_crtFrame->number_of_vertices);
// }
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
CGLUnlockContext([[self openGLContext] CGLContextObj]);

View File

@ -19,7 +19,7 @@
BOOL _didDecideRegion;
}
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
/*- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
if(!_didDecideRegion)
{
_frameCount++;
@ -41,7 +41,7 @@
}
[super crt:crt didEndFrame:frame didDetectVSync:didDetectVSync];
}
}*/
- (void)doRunForNumberOfCycles:(int)numberOfCycles {
_atari2600.run_for_cycles(numberOfCycles);
@ -65,13 +65,4 @@
}];
}
- (void)setView:(CSCathodeRayView *)view {
[super setView:view];
[view setSignalDecoder:[NSString stringWithUTF8String:_atari2600.get_signal_decoder()] type:CSCathodeRayViewSignalTypeNTSC];
}
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate{
_atari2600.get_crt()->set_delegate(delegate);
}
@end

View File

@ -18,4 +18,6 @@
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
- (void)drawViewForPixelSize:(CGSize)pixelSize;
@end

View File

@ -14,21 +14,9 @@
@implementation CSElectron {
Electron::Machine _electron;
// NSTimeInterval _periodicStart;
// int _numberOfCycles;
}
- (void)doRunForNumberOfCycles:(int)numberOfCycles {
/* _numberOfCycles += numberOfCycles;
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval difference = timeNow - _periodicStart;
if(difference > 1.0)
{
NSLog(@"cycles: %0.0f", (double)_numberOfCycles / difference);
_periodicStart = timeNow;
_numberOfCycles = 0;
}*/
_electron.run_for_cycles(numberOfCycles);
}
@ -44,8 +32,8 @@
_electron.set_rom((Electron::ROMSlot)slot, rom.length, (const uint8_t *)rom.bytes);
}
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate {
_electron.get_crt()->set_delegate(delegate);
- (void)drawViewForPixelSize:(CGSize)pixelSize {
_electron.get_crt()->draw_frame((int)pixelSize.width, (int)pixelSize.height);
}
- (BOOL)openUEFAtURL:(NSURL *)URL {
@ -67,7 +55,6 @@
- (void)setView:(CSCathodeRayView *)view {
[super setView:view];
[view setSignalDecoder:[NSString stringWithUTF8String:_electron.get_signal_decoder()] type:CSCathodeRayViewSignalTypeRGB];
}
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed {

View File

@ -13,12 +13,10 @@
@interface CSMachine (Subclassing)
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate;
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate;
- (void)doRunForNumberOfCycles:(int)numberOfCycles;
- (void)perform:(dispatch_block_t)action;
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync;
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
@end

View File

@ -9,13 +9,6 @@
#import "CSMachine.h"
#import "CSMachine+Subclassing.h"
struct CRTDelegate: public Outputs::CRT::Delegate {
__weak CSMachine *machine;
void crt_did_end_frame(Outputs::CRT *crt, CRTFrame *frame, bool did_detect_vsync) {
[machine crt:crt didEndFrame:frame didDetectVSync:did_detect_vsync];
}
};
struct SpeakerDelegate: public Outputs::Speaker::Delegate {
__weak CSMachine *machine;
void speaker_did_complete_samples(Outputs::Speaker *speaker, const int16_t *buffer, int buffer_size) {
@ -29,7 +22,6 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
};
@implementation CSMachine {
CRTDelegate _crtDelegate;
SpeakerDelegate _speakerDelegate;
dispatch_queue_t _serialDispatchQueue;
@ -40,10 +32,6 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
dispatch_async(_serialDispatchQueue, action);
}
- (void)crt:(Outputs::CRT *)crt didEndFrame:(CRTFrame *)frame didDetectVSync:(BOOL)didDetectVSync {
if([self.view pushFrame:frame]) crt->return_frame();
}
- (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length {
[self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length];
}
@ -66,16 +54,13 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) {
_serialDispatchQueue = dispatch_queue_create("Machine queue", DISPATCH_QUEUE_SERIAL);
_runningLock = [[NSConditionLock alloc] initWithCondition:CSMachineRunningStateStopped];
_crtDelegate.machine = self;
_speakerDelegate.machine = self;
[self setCRTDelegate:&_crtDelegate];
[self setSpeakerDelegate:&_speakerDelegate sampleRate:44100];
}
return self;
}
- (void)setCRTDelegate:(Outputs::CRT::Delegate *)delegate {}
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
return NO;
}

View File

@ -99,13 +99,14 @@ void CRT::allocate_buffers(unsigned int number, va_list sizes)
CRT::CRT() :
_next_scan(0),
_frames_with_delegate(0),
_frame_read_pointer(0),
_horizontal_counter(0),
_sync_capacitor_charge_level(0),
_is_receiving_sync(false),
_is_in_hsync(false),
_is_in_vsync(false),
_current_frame(nullptr),
_current_frame_mutex(new std::mutex),
_rasterPosition({.x = 0, .y = 0})
{}
@ -332,21 +333,24 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
// end of vertical sync: tell the delegate that we finished vertical sync,
// releasing all runs back into the common pool
case SyncEvent::EndVSync:
if(_delegate && _current_frame_builder)
if(_current_frame_builder)
{
_current_frame_builder->complete();
_frames_with_delegate++;
_delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
_current_frame_mutex->lock();
_current_frame = &_current_frame_builder->frame;
_current_frame_mutex->unlock();
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
}
if(_frames_with_delegate < kCRTNumberOfFrames)
// if(_frames_with_delegate < kCRTNumberOfFrames)
{
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
_current_frame_builder = _frame_builders[_frame_read_pointer];
_current_frame_builder->reset();
}
else
_current_frame_builder = nullptr;
// else
// _current_frame_builder = nullptr;
_is_in_vsync = false;
_did_detect_vsync = false;
@ -358,18 +362,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
}
}
void CRT::return_frame()
{
_frames_with_delegate--;
}
#pragma mark - delegate
void CRT::set_delegate(Delegate *delegate)
{
_delegate = delegate;
}
#pragma mark - stream feeding methods
void CRT::output_scan()
@ -443,82 +435,7 @@ uint8_t *CRT::get_write_target_for_buffer(int buffer)
return _current_frame_builder->get_write_target_for_buffer(buffer);
}
#pragma mark - CRTFrame
CRTFrameBuilder::CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes)
{
frame.size.width = width;
frame.size.height = height;
frame.number_of_buffers = number_of_buffers;
frame.buffers = new CRTBuffer[number_of_buffers];
frame.size_per_vertex = kCRTSizeOfVertex;
frame.geometry_mode = CRTGeometryModeTriangles;
for(int buffer = 0; buffer < number_of_buffers; buffer++)
{
frame.buffers[buffer].depth = va_arg(buffer_sizes, unsigned int);
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
}
reset();
}
CRTFrameBuilder::~CRTFrameBuilder()
{
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
delete[] frame.buffers[buffer].data;
delete frame.buffers;
}
void CRTFrameBuilder::reset()
{
frame.number_of_vertices = 0;
_next_write_x_position = _next_write_y_position = 0;
frame.dirty_size.width = 0;
frame.dirty_size.height = 1;
}
void CRTFrameBuilder::complete()
{
frame.vertices = &_all_runs[0];
}
uint8_t *CRTFrameBuilder::get_next_run()
{
const size_t vertices_per_run = 6;
// get a run from the allocated list, allocating more if we're about to overrun
if((frame.number_of_vertices + vertices_per_run) * frame.size_per_vertex >= _all_runs.size())
{
_all_runs.resize(_all_runs.size() + frame.size_per_vertex * vertices_per_run * 100);
}
uint8_t *next_run = &_all_runs[frame.number_of_vertices * frame.size_per_vertex];
frame.number_of_vertices += vertices_per_run;
return next_run;
}
void CRTFrameBuilder::allocate_write_area(int required_length)
{
if (_next_write_x_position + required_length > frame.size.width)
{
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)&(frame.size.height-1);
frame.dirty_size.height++;
}
_write_x_position = _next_write_x_position;
_write_y_position = _next_write_y_position;
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
_next_write_x_position += required_length;
frame.dirty_size.width = std::max(frame.dirty_size.width, _next_write_x_position);
}
uint8_t *CRTFrameBuilder::get_write_target_for_buffer(int buffer)
{
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
}
char *CRT::get_vertex_shader()
{

View File

@ -13,6 +13,7 @@
#include <stdarg.h>
#include <string>
#include <vector>
#include <mutex>
#include "CRTFrame.h"
@ -136,12 +137,10 @@ class CRT {
*/
uint8_t *get_write_target_for_buffer(int buffer);
// MARK: Binding
/*! Causes appropriate OpenGL or OpenGL ES calls to be issued in order to draw the current CRT state.
The caller is responsible for ensuring that a valid OpenGL context exists for the duration of this call.
*/
void draw_frame();
void draw_frame(int output_width, int output_height);
/*! Tells the CRT that the next call to draw_frame will occur on a different OpenGL context than
the previous.
@ -308,12 +307,13 @@ class CRT {
static const int kCRTNumberOfFrames = 4;
// the run delegate and the triple buffer
// the triple buffer and OpenGL state
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
CRTFrameBuilder *_current_frame_builder;
int _frames_with_delegate;
CRTFrame *_current_frame;
std::shared_ptr<std::mutex> _current_frame_mutex;
int _frame_read_pointer;
Delegate *_delegate;
void *openGLState;
};
}

View File

@ -0,0 +1,86 @@
//
// CRTFrameBuilder.cpp
// Clock Signal
//
// Created by Thomas Harte on 04/02/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "CRT.hpp"
using namespace Outputs;
CRT::CRTFrameBuilder::CRTFrameBuilder(uint16_t width, uint16_t height, unsigned int number_of_buffers, va_list buffer_sizes)
{
frame.size.width = width;
frame.size.height = height;
frame.number_of_buffers = number_of_buffers;
frame.buffers = new CRTBuffer[number_of_buffers];
frame.size_per_vertex = kCRTSizeOfVertex;
frame.geometry_mode = CRTGeometryModeTriangles;
for(int buffer = 0; buffer < number_of_buffers; buffer++)
{
frame.buffers[buffer].depth = va_arg(buffer_sizes, unsigned int);
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
}
reset();
}
CRT::CRTFrameBuilder::~CRTFrameBuilder()
{
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
delete[] frame.buffers[buffer].data;
delete frame.buffers;
}
void CRT::CRTFrameBuilder::reset()
{
frame.number_of_vertices = 0;
_next_write_x_position = _next_write_y_position = 0;
frame.dirty_size.width = 0;
frame.dirty_size.height = 1;
}
void CRT::CRTFrameBuilder::complete()
{
frame.vertices = &_all_runs[0];
}
uint8_t *CRT::CRTFrameBuilder::get_next_run()
{
const size_t vertices_per_run = 6;
// get a run from the allocated list, allocating more if we're about to overrun
if((frame.number_of_vertices + vertices_per_run) * frame.size_per_vertex >= _all_runs.size())
{
_all_runs.resize(_all_runs.size() + frame.size_per_vertex * vertices_per_run * 100);
}
uint8_t *next_run = &_all_runs[frame.number_of_vertices * frame.size_per_vertex];
frame.number_of_vertices += vertices_per_run;
return next_run;
}
void CRT::CRTFrameBuilder::allocate_write_area(int required_length)
{
if (_next_write_x_position + required_length > frame.size.width)
{
_next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)&(frame.size.height-1);
frame.dirty_size.height++;
}
_write_x_position = _next_write_x_position;
_write_y_position = _next_write_y_position;
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
_next_write_x_position += required_length;
frame.dirty_size.width = std::max(frame.dirty_size.width, _next_write_x_position);
}
uint8_t *CRT::CRTFrameBuilder::get_write_target_for_buffer(int buffer)
{
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
}

50
Outputs/CRT/CRTOpenGL.cpp Normal file
View File

@ -0,0 +1,50 @@
//
// CRTOpenGL.cpp
// Clock Signal
//
// Created by Thomas Harte on 03/02/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "CRT.hpp"
// TODO: figure out correct include paths for other platforms.
#include <OpenGL/OpenGL.h>
using namespace Outputs;
namespace {
struct OpenGLState {
GLuint _vertexShader, _fragmentShader;
GLuint _shaderProgram;
GLuint _arrayBuffer, _vertexArray;
GLint _positionAttribute;
GLint _textureCoordinatesAttribute;
GLint _lateralAttribute;
GLint _textureSizeUniform, _windowSizeUniform;
GLint _boundsOriginUniform, _boundsSizeUniform;
GLint _alphaUniform;
GLuint _textureName, _shadowMaskTextureName;
};
}
void CRT::draw_frame(int output_width, int output_height)
{
printf("%d %d\n", output_width, output_height);
}
void CRT::set_openGL_context_will_change(bool should_delete_resources)
{
}
void CRT::set_composite_sampling_function(const char *shader)
{
}
void CRT::set_rgb_sampling_function(const char *shader)
{
printf("%s\n", shader);
}