mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-09 05:25:01 +00:00
Reshuffled to make the OpenGL view explicitly a conduit for CRT-style output, and to give it responsibility for frame drawing. Which is still an awkward thread hop for the time being, but I've yet to read up on the advocated approach to multithreading with an NSOpenGLView; it looked like special provisions were available.
This commit is contained in:
@@ -7,19 +7,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "OpenGLView.h"
|
||||||
@class CSAtari2600;
|
|
||||||
@protocol CSAtari2600Delegate
|
|
||||||
- (void)atari2600NeedsRedraw:(CSAtari2600 *)atari2600;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface CSAtari2600 : NSObject
|
@interface CSAtari2600 : NSObject
|
||||||
|
|
||||||
@property (nonatomic, weak) id <CSAtari2600Delegate> delegate;
|
@property (nonatomic, strong) CSCathodeRayView *view;
|
||||||
|
|
||||||
- (void)runForNumberOfCycles:(int)cycles;
|
- (void)runForNumberOfCycles:(int)cycles;
|
||||||
- (void)setROM:(NSData *)rom;
|
- (void)setROM:(NSData *)rom;
|
||||||
|
|
||||||
- (void)draw;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -8,31 +8,6 @@
|
|||||||
|
|
||||||
#import "Atari2600.h"
|
#import "Atari2600.h"
|
||||||
#import "Atari2600.hpp"
|
#import "Atari2600.hpp"
|
||||||
#import <OpenGL/gl3.h>
|
|
||||||
|
|
||||||
const char *vertexShader =
|
|
||||||
"#version 150\n"
|
|
||||||
"\n"
|
|
||||||
"in vec2 position;\n"
|
|
||||||
"\n"
|
|
||||||
"out vec4 colour;\n"
|
|
||||||
"\n"
|
|
||||||
"void main (void)\n"
|
|
||||||
"{\n"
|
|
||||||
"colour = vec4(1.0, 1.0, 1.0, 1.0);\n"
|
|
||||||
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0, 0.0, 1.0);\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const char *fragmentShader =
|
|
||||||
"#version 150\n"
|
|
||||||
"\n"
|
|
||||||
"in vec4 colour;\n"
|
|
||||||
"out vec4 fragColour;\n"
|
|
||||||
"\n"
|
|
||||||
"void main(void)\n"
|
|
||||||
"{\n"
|
|
||||||
"fragColour = colour;\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
@interface CSAtari2600 (Callbacks)
|
@interface CSAtari2600 (Callbacks)
|
||||||
- (void)crtDidEndFrame:(CRTFrame *)frame;
|
- (void)crtDidEndFrame:(CRTFrame *)frame;
|
||||||
@@ -48,79 +23,21 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
|||||||
Atari2600CRTDelegate _crtDelegate;
|
Atari2600CRTDelegate _crtDelegate;
|
||||||
|
|
||||||
dispatch_queue_t _serialDispatchQueue;
|
dispatch_queue_t _serialDispatchQueue;
|
||||||
CRTFrame *_queuedFrame;
|
|
||||||
|
|
||||||
GLuint _vertexShader, _fragmentShader;
|
|
||||||
GLuint _shaderProgram;
|
|
||||||
|
|
||||||
GLuint _arrayBuffer, _vertexArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)crtDidEndFrame:(CRTFrame *)frame {
|
- (void)crtDidEndFrame:(CRTFrame *)frame {
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if(_queuedFrame) {
|
BOOL hasReturn = !!self.view.crtFrame;
|
||||||
|
self.view.crtFrame = frame;
|
||||||
|
|
||||||
|
if(hasReturn)
|
||||||
dispatch_async(_serialDispatchQueue, ^{
|
dispatch_async(_serialDispatchQueue, ^{
|
||||||
_atari2600.get_crt()->return_frame();
|
_atari2600.get_crt()->return_frame();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
_queuedFrame = frame;
|
|
||||||
|
|
||||||
[self.delegate atari2600NeedsRedraw:self];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (GLuint)compileShader:(const char *)source type:(GLenum)type
|
|
||||||
{
|
|
||||||
GLuint shader = glCreateShader(type);
|
|
||||||
glShaderSource(shader, 1, &source, NULL);
|
|
||||||
glCompileShader(shader);
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)draw {
|
|
||||||
|
|
||||||
if(!_shaderProgram)
|
|
||||||
{
|
|
||||||
_shaderProgram = glCreateProgram();
|
|
||||||
_vertexShader = [self compileShader:vertexShader type:GL_VERTEX_SHADER];
|
|
||||||
_fragmentShader = [self compileShader:fragmentShader type:GL_FRAGMENT_SHADER];
|
|
||||||
|
|
||||||
glAttachShader(_shaderProgram, _vertexShader);
|
|
||||||
glAttachShader(_shaderProgram, _fragmentShader);
|
|
||||||
glLinkProgram(_shaderProgram);
|
|
||||||
|
|
||||||
glGenVertexArrays(1, &_vertexArray);
|
|
||||||
glBindVertexArray(_vertexArray);
|
|
||||||
glGenBuffers(1, &_arrayBuffer);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer);
|
|
||||||
|
|
||||||
GLushort vertices[] = {
|
|
||||||
0, 0,
|
|
||||||
32767, 32767,
|
|
||||||
};
|
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_queuedFrame)
|
|
||||||
{
|
|
||||||
glClearColor(1.0, 0.0, 0.0, 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
glUseProgram(_shaderProgram);
|
|
||||||
|
|
||||||
GLint position = glGetAttribLocation(_shaderProgram, "position");
|
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, _queuedFrame->number_of_runs * sizeof(GLushort) * 8, _queuedFrame->runs, GL_DYNAMIC_DRAW);
|
|
||||||
|
|
||||||
glEnableVertexAttribArray(position);
|
|
||||||
glVertexAttribPointer(position, 2, GL_UNSIGNED_SHORT, GL_TRUE, 4 * sizeof(GLushort), (void *)0);
|
|
||||||
|
|
||||||
glDrawArrays(GL_LINES, 0, _queuedFrame->number_of_runs*2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)runForNumberOfCycles:(int)cycles {
|
- (void)runForNumberOfCycles:(int)cycles {
|
||||||
dispatch_async(_serialDispatchQueue, ^{
|
dispatch_async(_serialDispatchQueue, ^{
|
||||||
_atari2600.run_for_cycles(cycles);
|
_atari2600.run_for_cycles(cycles);
|
||||||
|
@@ -8,18 +8,19 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Atari2600Document: NSDocument, CSOpenGLViewDelegate, CSAtari2600Delegate {
|
class Atari2600Document: NSDocument, CSCathodeRayViewDelegate {
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
// Add your subclass-specific initialization here.
|
// Add your subclass-specific initialization here.
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet weak var openGLView: CSOpenGLView?
|
@IBOutlet weak var openGLView: CSCathodeRayView?
|
||||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||||
super.windowControllerDidLoadNib(aController)
|
super.windowControllerDidLoadNib(aController)
|
||||||
|
|
||||||
openGLView!.delegate = self
|
openGLView!.delegate = self
|
||||||
|
atari2600!.view = openGLView!
|
||||||
|
|
||||||
// bind the content aspect ratio to remain 4:3 from now on
|
// bind the content aspect ratio to remain 4:3 from now on
|
||||||
aController.window!.contentAspectRatio = NSSize(width: 4.0, height: 3.0)
|
aController.window!.contentAspectRatio = NSSize(width: 4.0, height: 3.0)
|
||||||
@@ -45,13 +46,12 @@ class Atari2600Document: NSDocument, CSOpenGLViewDelegate, CSAtari2600Delegate {
|
|||||||
override func readFromData(data: NSData, ofType typeName: String) throws {
|
override func readFromData(data: NSData, ofType typeName: String) throws {
|
||||||
atari2600 = CSAtari2600()
|
atari2600 = CSAtari2600()
|
||||||
atari2600!.setROM(data)
|
atari2600!.setROM(data)
|
||||||
atari2600!.delegate = self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: CSOpenGLViewDelegate
|
// MARK: CSOpenGLViewDelegate
|
||||||
|
|
||||||
private var lastCycleCount: Int64?
|
private var lastCycleCount: Int64?
|
||||||
func openGLView(view: CSOpenGLView!, didUpdateToTime time: CVTimeStamp) {
|
func openGLView(view: CSCathodeRayView, didUpdateToTime time: CVTimeStamp) {
|
||||||
|
|
||||||
// TODO: treat time as a delta from old time, work out how many cycles that is plus error
|
// TODO: treat time as a delta from old time, work out how many cycles that is plus error
|
||||||
|
|
||||||
@@ -70,10 +70,6 @@ class Atari2600Document: NSDocument, CSOpenGLViewDelegate, CSAtari2600Delegate {
|
|||||||
lastCycleCount = cycleCount
|
lastCycleCount = cycleCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func openGLViewDrawView(view: CSOpenGLView!) {
|
|
||||||
atari2600!.draw()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: CSAtari2600Delegate
|
// MARK: CSAtari2600Delegate
|
||||||
|
|
||||||
func atari2600NeedsRedraw(atari2600: CSAtari2600!) {
|
func atari2600NeedsRedraw(atari2600: CSAtari2600!) {
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
|
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<openGLView useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSOpenGLView">
|
<openGLView useAuxiliaryDepthBufferStencil="NO" allowOffline="YES" wantsBestResolutionOpenGLSurface="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DEG-fq-cjd" customClass="CSCathodeRayView">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
|
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
|
||||||
</openGLView>
|
</openGLView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
@@ -7,17 +7,18 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
@import AppKit;
|
#import "CRTFrame.h"
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
|
||||||
@class CSOpenGLView;
|
@class CSCathodeRayView;
|
||||||
|
|
||||||
@protocol CSOpenGLViewDelegate
|
@protocol CSCathodeRayViewDelegate
|
||||||
- (void)openGLView:(CSOpenGLView *)view didUpdateToTime:(CVTimeStamp)time;
|
- (void)openGLView:(CSCathodeRayView * __nonnull)view didUpdateToTime:(CVTimeStamp)time;
|
||||||
- (void)openGLViewDrawView:(CSOpenGLView *)view;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface CSOpenGLView : NSOpenGLView
|
@interface CSCathodeRayView : NSOpenGLView
|
||||||
|
|
||||||
@property (nonatomic, weak) id <CSOpenGLViewDelegate> delegate;
|
@property (nonatomic, weak) id <CSCathodeRayViewDelegate> delegate;
|
||||||
|
@property (nonatomic, assign, nullable) CRTFrame *crtFrame;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -8,10 +8,16 @@
|
|||||||
|
|
||||||
#import "OpenGLView.h"
|
#import "OpenGLView.h"
|
||||||
@import CoreVideo;
|
@import CoreVideo;
|
||||||
#import <OpenGL/gl.h>
|
#import <OpenGL/gl3.h>
|
||||||
|
#import <OpenGL/gl3ext.h>
|
||||||
|
|
||||||
@implementation CSOpenGLView {
|
@implementation CSCathodeRayView {
|
||||||
CVDisplayLinkRef displayLink;
|
CVDisplayLinkRef displayLink;
|
||||||
|
|
||||||
|
GLuint _vertexShader, _fragmentShader;
|
||||||
|
GLuint _shaderProgram;
|
||||||
|
GLuint _arrayBuffer, _vertexArray;
|
||||||
|
GLint _positionAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareOpenGL
|
- (void)prepareOpenGL
|
||||||
@@ -31,13 +37,17 @@
|
|||||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);
|
||||||
|
|
||||||
|
// get the shader ready, set the clear colour
|
||||||
|
glClearColor(1.0, 0.0, 0.0, 1.0);
|
||||||
|
[self prepareShader];
|
||||||
|
|
||||||
// Activate the display link
|
// Activate the display link
|
||||||
CVDisplayLinkStart(displayLink);
|
CVDisplayLinkStart(displayLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
|
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
|
||||||
{
|
{
|
||||||
CSOpenGLView *view = (__bridge CSOpenGLView *)displayLinkContext;
|
CSCathodeRayView *view = (__bridge CSCathodeRayView *)displayLinkContext;
|
||||||
[view.delegate openGLView:view didUpdateToTime:*now];
|
[view.delegate openGLView:view didUpdateToTime:*now];
|
||||||
return kCVReturnSuccess;
|
return kCVReturnSuccess;
|
||||||
}
|
}
|
||||||
@@ -58,15 +68,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
glViewport(0, 0, viewSize.x, viewSize.y);
|
glViewport(0, 0, viewSize.x, viewSize.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)drawRect:(NSRect)dirtyRect
|
|
||||||
{
|
|
||||||
[self.openGLContext makeCurrentContext];
|
|
||||||
|
|
||||||
[self.delegate openGLViewDrawView:self];
|
|
||||||
|
|
||||||
glSwapAPPLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) awakeFromNib
|
- (void) awakeFromNib
|
||||||
{
|
{
|
||||||
NSOpenGLPixelFormatAttribute attributes[] =
|
NSOpenGLPixelFormatAttribute attributes[] =
|
||||||
@@ -93,4 +94,80 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
self.wantsBestResolutionOpenGLSurface = YES;
|
self.wantsBestResolutionOpenGLSurface = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setCrtFrame:(CRTFrame *)crtFrame
|
||||||
|
{
|
||||||
|
_crtFrame = crtFrame;
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Frame output
|
||||||
|
|
||||||
|
const char *vertexShader =
|
||||||
|
"#version 150\n"
|
||||||
|
"\n"
|
||||||
|
"in vec2 position;\n"
|
||||||
|
"\n"
|
||||||
|
"out vec4 colour;\n"
|
||||||
|
"\n"
|
||||||
|
"void main (void)\n"
|
||||||
|
"{\n"
|
||||||
|
"colour = vec4(1.0, 1.0, 1.0, 1.0);\n"
|
||||||
|
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0, 0.0, 1.0);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
const char *fragmentShader =
|
||||||
|
"#version 150\n"
|
||||||
|
"\n"
|
||||||
|
"in vec4 colour;\n"
|
||||||
|
"out vec4 fragColour;\n"
|
||||||
|
"\n"
|
||||||
|
"void main(void)\n"
|
||||||
|
"{\n"
|
||||||
|
"fragColour = colour;\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
- (GLuint)compileShader:(const char *)source type:(GLenum)type
|
||||||
|
{
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
glShaderSource(shader, 1, &source, NULL);
|
||||||
|
glCompileShader(shader);
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareShader
|
||||||
|
{
|
||||||
|
_shaderProgram = glCreateProgram();
|
||||||
|
_vertexShader = [self compileShader:vertexShader type:GL_VERTEX_SHADER];
|
||||||
|
_fragmentShader = [self compileShader:fragmentShader type:GL_FRAGMENT_SHADER];
|
||||||
|
|
||||||
|
glAttachShader(_shaderProgram, _vertexShader);
|
||||||
|
glAttachShader(_shaderProgram, _fragmentShader);
|
||||||
|
glLinkProgram(_shaderProgram);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &_vertexArray);
|
||||||
|
glBindVertexArray(_vertexArray);
|
||||||
|
glGenBuffers(1, &_arrayBuffer);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer);
|
||||||
|
|
||||||
|
glUseProgram(_shaderProgram);
|
||||||
|
_positionAttribute = glGetAttribLocation(_shaderProgram, "position");
|
||||||
|
glEnableVertexAttribArray(_positionAttribute);
|
||||||
|
glVertexAttribPointer(_positionAttribute, 2, GL_UNSIGNED_SHORT, GL_TRUE, 4 * sizeof(GLushort), (void *)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawRect:(NSRect)dirtyRect
|
||||||
|
{
|
||||||
|
[self.openGLContext makeCurrentContext];
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
if (_crtFrame)
|
||||||
|
{
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, _crtFrame->number_of_runs * sizeof(GLushort) * 8, _crtFrame->runs, GL_DYNAMIC_DRAW);
|
||||||
|
glDrawArrays(GL_LINES, 0, _crtFrame->number_of_runs*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
glSwapAPPLE();
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -13,16 +13,16 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct CRTBuffer {
|
typedef struct {
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
int depth;
|
int depth;
|
||||||
};
|
} CRTBuffer;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int width, height;
|
int width, height;
|
||||||
} CRTSize;
|
} CRTSize;
|
||||||
|
|
||||||
struct CRTFrame {
|
typedef struct {
|
||||||
CRTSize size, dirty_size;
|
CRTSize size, dirty_size;
|
||||||
|
|
||||||
int number_of_buffers;
|
int number_of_buffers;
|
||||||
@@ -30,7 +30,7 @@ struct CRTFrame {
|
|||||||
|
|
||||||
int number_of_runs;
|
int number_of_runs;
|
||||||
uint16_t *runs;
|
uint16_t *runs;
|
||||||
};
|
} CRTFrame;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user