1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-09 01:29:44 +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:
Thomas Harte 2015-07-26 15:13:46 -04:00
parent dd428c5d4d
commit e53fbcf9ea
7 changed files with 113 additions and 128 deletions

View File

@ -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

View File

@ -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);

View File

@ -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!) {

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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
}