mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-03 07:33:29 +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:
parent
dd428c5d4d
commit
e53fbcf9ea
@ -7,19 +7,13 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class CSAtari2600;
|
||||
@protocol CSAtari2600Delegate
|
||||
- (void)atari2600NeedsRedraw:(CSAtari2600 *)atari2600;
|
||||
@end
|
||||
#import "OpenGLView.h"
|
||||
|
||||
@interface CSAtari2600 : NSObject
|
||||
|
||||
@property (nonatomic, weak) id <CSAtari2600Delegate> delegate;
|
||||
@property (nonatomic, strong) CSCathodeRayView *view;
|
||||
|
||||
- (void)runForNumberOfCycles:(int)cycles;
|
||||
- (void)setROM:(NSData *)rom;
|
||||
|
||||
- (void)draw;
|
||||
|
||||
@end
|
||||
|
@ -8,31 +8,6 @@
|
||||
|
||||
#import "Atari2600.h"
|
||||
#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)
|
||||
- (void)crtDidEndFrame:(CRTFrame *)frame;
|
||||
@ -48,79 +23,21 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
Atari2600CRTDelegate _crtDelegate;
|
||||
|
||||
dispatch_queue_t _serialDispatchQueue;
|
||||
CRTFrame *_queuedFrame;
|
||||
|
||||
GLuint _vertexShader, _fragmentShader;
|
||||
GLuint _shaderProgram;
|
||||
|
||||
GLuint _arrayBuffer, _vertexArray;
|
||||
}
|
||||
|
||||
- (void)crtDidEndFrame:(CRTFrame *)frame {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(_queuedFrame) {
|
||||
BOOL hasReturn = !!self.view.crtFrame;
|
||||
self.view.crtFrame = frame;
|
||||
|
||||
if(hasReturn)
|
||||
dispatch_async(_serialDispatchQueue, ^{
|
||||
_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 {
|
||||
dispatch_async(_serialDispatchQueue, ^{
|
||||
_atari2600.run_for_cycles(cycles);
|
||||
|
@ -8,18 +8,19 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
class Atari2600Document: NSDocument, CSOpenGLViewDelegate, CSAtari2600Delegate {
|
||||
class Atari2600Document: NSDocument, CSCathodeRayViewDelegate {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
// Add your subclass-specific initialization here.
|
||||
}
|
||||
|
||||
@IBOutlet weak var openGLView: CSOpenGLView?
|
||||
@IBOutlet weak var openGLView: CSCathodeRayView?
|
||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||
super.windowControllerDidLoadNib(aController)
|
||||
|
||||
openGLView!.delegate = self
|
||||
atari2600!.view = openGLView!
|
||||
|
||||
// bind the content aspect ratio to remain 4:3 from now on
|
||||
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 {
|
||||
atari2600 = CSAtari2600()
|
||||
atari2600!.setROM(data)
|
||||
atari2600!.delegate = self
|
||||
}
|
||||
|
||||
// MARK: CSOpenGLViewDelegate
|
||||
|
||||
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
|
||||
|
||||
@ -70,10 +70,6 @@ class Atari2600Document: NSDocument, CSOpenGLViewDelegate, CSAtari2600Delegate {
|
||||
lastCycleCount = cycleCount
|
||||
}
|
||||
|
||||
func openGLViewDrawView(view: CSOpenGLView!) {
|
||||
atari2600!.draw()
|
||||
}
|
||||
|
||||
// MARK: CSAtari2600Delegate
|
||||
|
||||
func atari2600NeedsRedraw(atari2600: CSAtari2600!) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<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"/>
|
||||
</openGLView>
|
||||
</subviews>
|
||||
|
@ -7,17 +7,18 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@import AppKit;
|
||||
#import "CRTFrame.h"
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class CSOpenGLView;
|
||||
@class CSCathodeRayView;
|
||||
|
||||
@protocol CSOpenGLViewDelegate
|
||||
- (void)openGLView:(CSOpenGLView *)view didUpdateToTime:(CVTimeStamp)time;
|
||||
- (void)openGLViewDrawView:(CSOpenGLView *)view;
|
||||
@protocol CSCathodeRayViewDelegate
|
||||
- (void)openGLView:(CSCathodeRayView * __nonnull)view didUpdateToTime:(CVTimeStamp)time;
|
||||
@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
|
||||
|
@ -8,10 +8,16 @@
|
||||
|
||||
#import "OpenGLView.h"
|
||||
@import CoreVideo;
|
||||
#import <OpenGL/gl.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
#import <OpenGL/gl3ext.h>
|
||||
|
||||
@implementation CSOpenGLView {
|
||||
@implementation CSCathodeRayView {
|
||||
CVDisplayLinkRef displayLink;
|
||||
|
||||
GLuint _vertexShader, _fragmentShader;
|
||||
GLuint _shaderProgram;
|
||||
GLuint _arrayBuffer, _vertexArray;
|
||||
GLint _positionAttribute;
|
||||
}
|
||||
|
||||
- (void)prepareOpenGL
|
||||
@ -30,14 +36,18 @@
|
||||
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||
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
|
||||
CVDisplayLinkStart(displayLink);
|
||||
}
|
||||
|
||||
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];
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
@ -58,15 +68,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
glViewport(0, 0, viewSize.x, viewSize.y);
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
[self.openGLContext makeCurrentContext];
|
||||
|
||||
[self.delegate openGLViewDrawView:self];
|
||||
|
||||
glSwapAPPLE();
|
||||
}
|
||||
|
||||
- (void) awakeFromNib
|
||||
{
|
||||
NSOpenGLPixelFormatAttribute attributes[] =
|
||||
@ -93,4 +94,80 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
||||
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
|
||||
|
@ -13,16 +13,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct CRTBuffer {
|
||||
typedef struct {
|
||||
uint8_t *data;
|
||||
int depth;
|
||||
};
|
||||
} CRTBuffer;
|
||||
|
||||
typedef struct {
|
||||
int width, height;
|
||||
} CRTSize;
|
||||
|
||||
struct CRTFrame {
|
||||
typedef struct {
|
||||
CRTSize size, dirty_size;
|
||||
|
||||
int number_of_buffers;
|
||||
@ -30,7 +30,7 @@ struct CRTFrame {
|
||||
|
||||
int number_of_runs;
|
||||
uint16_t *runs;
|
||||
};
|
||||
} CRTFrame;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user