1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-25 18:30:21 +00:00

With around about a thousand issues, not the least of which being sometimes unsafe memory accesses, I've at last got pixels on screen.

This commit is contained in:
Thomas Harte 2015-07-26 23:50:43 -04:00
parent cd0a62d21e
commit 65bb31d55b
4 changed files with 105 additions and 29 deletions

View File

@ -101,7 +101,7 @@ void Machine::output_state(OutputState state, uint8_t *pixel)
if(state == OutputState::Pixel && _outputBuffer)
{
_outputBuffer[(_lastOutputStateDuration * 4) + 0] = pixel[0];
_outputBuffer[(_lastOutputStateDuration * 4) + 0] = 0x40;//pixel[0];
_outputBuffer[(_lastOutputStateDuration * 4) + 1] = pixel[1];
_outputBuffer[(_lastOutputStateDuration * 4) + 2] = pixel[2];
_outputBuffer[(_lastOutputStateDuration * 4) + 3] = 0xff;

View File

@ -19,6 +19,8 @@
GLuint _arrayBuffer, _vertexArray;
GLint _positionAttribute;
GLint _textureCoordinatesAttribute;
GLuint _textureName;
}
- (void)prepareOpenGL
@ -45,9 +47,12 @@
// Activate the display link
CVDisplayLinkStart(displayLink);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
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)
{
CSCathodeRayView *view = (__bridge CSCathodeRayView *)displayLinkContext;
[view.delegate openGLView:view didUpdateToTime:*now];
@ -101,8 +106,14 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
_crtFrame = crtFrame;
[self setNeedsDisplay:YES];
[self.openGLContext makeCurrentContext];
glBufferData(GL_ARRAY_BUFFER, _crtFrame->number_of_runs * sizeof(GLushort) * 8, _crtFrame->runs, GL_DYNAMIC_DRAW);
if(crtFrame)
{
[self.openGLContext makeCurrentContext];
glBufferData(GL_ARRAY_BUFFER, _crtFrame->number_of_runs * sizeof(GLushort) * 24, _crtFrame->runs, GL_DYNAMIC_DRAW);
glBindTexture(GL_TEXTURE_2D, _textureName);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _crtFrame->size.width, _crtFrame->size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, _crtFrame->buffers[0].data);
}
}
#pragma mark - Frame output
@ -120,7 +131,7 @@ const char *vertexShader =
"\n"
"void main (void)\n"
"{\n"
"srcCoordinatesVarying = srcCoordinates;\n"
"srcCoordinatesVarying = vec2((srcCoordinates.x + 0.5) / 511.0, (srcCoordinates.y + 0.5) / 511.0);\n"
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0, 0.0, 1.0);\n"
"}\n";
@ -130,10 +141,11 @@ const char *fragmentShader =
"\n"
"in vec2 srcCoordinatesVarying;\n"
"out vec4 fragColour;\n"
"uniform sampler2D texID;\n"
"\n"
"void main(void)\n"
"{\n"
"fragColour = vec4(1.0, 1.0, 1.0, 1.0);\n"
"fragColour = texture(texID, srcCoordinatesVarying);\n" // vec4(1.0, 1.0, 1.0, 0.5)
"}\n";
#if defined(DEBUG)
@ -191,7 +203,14 @@ const char *fragmentShader =
glEnableVertexAttribArray(_textureCoordinatesAttribute);
glVertexAttribPointer(_positionAttribute, 2, GL_UNSIGNED_SHORT, GL_TRUE, 4 * sizeof(GLushort), (void *)0);
glVertexAttribPointer(_textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_TRUE, 4 * sizeof(GLushort), (void *)(2 * sizeof(GLushort)));
glVertexAttribPointer(_textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, 4 * sizeof(GLushort), (void *)(2 * sizeof(GLushort)));
glGenTextures(1, &_textureName);
glBindTexture(GL_TEXTURE_2D, _textureName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
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
@ -202,8 +221,7 @@ const char *fragmentShader =
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);
glDrawArrays(GL_TRIANGLES, 0, _crtFrame->number_of_runs*6);
}
glSwapAPPLE();

View File

@ -8,10 +8,13 @@
#include "CRT.hpp"
#include <stdarg.h>
#include <math.h>
using namespace Outputs;
#define kRetraceXMask 0x01
#define kRetraceYMask 0x02
CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
{
const int syncCapacityLineChargeThreshold = 5;
@ -36,6 +39,25 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
_retraceSpeed.x = UINT32_MAX / _horizontal_retrace_time;
_retraceSpeed.y = UINT32_MAX / _vertical_retrace_time;
// precompute the lengths of all four combinations of scan direction, for fast triangle
// strip generation later
float scanSpeedXfl = 1.0f / (float)_cycles_per_line;
float scanSpeedYfl = 1.0f / (float)(_height_of_display * _cycles_per_line);
float retraceSpeedXfl = 1.0f / (float)_horizontal_retrace_time;
float retraceSpeedYfl = 1.0f / (float)(_vertical_retrace_time);
float lengths[4];
lengths[0] = sqrtf(scanSpeedXfl*scanSpeedXfl + scanSpeedYfl*scanSpeedYfl);
lengths[kRetraceXMask] = sqrtf(retraceSpeedXfl*retraceSpeedXfl + scanSpeedYfl*scanSpeedYfl);
lengths[kRetraceXMask | kRetraceYMask] = sqrtf(retraceSpeedXfl*retraceSpeedXfl + retraceSpeedYfl*retraceSpeedYfl);
lengths[kRetraceYMask] = sqrtf(scanSpeedXfl*scanSpeedXfl + retraceSpeedYfl*retraceSpeedYfl);
// width should be 1.0 / _height_of_display, rotated to match the direction
float angle = atan2f(scanSpeedYfl, scanSpeedXfl);
float halfLineWidth = (float)_height_of_display * 2.0f;
_widths[0][0] = (sinf(angle) / halfLineWidth) * UINT32_MAX;
_widths[0][1] = (cosf(angle) / halfLineWidth) * UINT32_MAX;
// generate buffers for signal storage as requested — format is
// number of buffers, size of buffer 1, size of buffer 2...
const int bufferWidth = 512;
@ -145,12 +167,20 @@ CRT::SyncEvent CRT::next_horizontal_sync_event(bool hsync_is_requested, int cycl
void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool vsync_charging, const Type type, const char *data_type)
{
number_of_cycles *= _time_multiplier;
const bool is_output_run = ((type == Type::Level) || (type == Type::Data));
bool is_output_run = ((type == Type::Level) || (type == Type::Data));
uint16_t tex_x = 0;
uint16_t tex_y = 0;
// static int o;
// if(is_output_run)
// {
// o++;
// if(o&1) is_output_run = false;
// }
if(is_output_run && _current_frame_builder) {
tex_x = _current_frame_builder->_write_x_position;
tex_y = _current_frame_builder->_write_y_position;
@ -168,14 +198,21 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event);
uint16_t *next_run = (is_output_run && _current_frame_builder && next_run_length) ? _current_frame_builder->get_next_run() : nullptr;
// int lengthMask = (_is_in_hsync ? kRetraceXMask : 0) | ((_vretrace_counter > 0) ? kRetraceXMask : 0);
// uint32_t *width = _widths[lengthMask];
uint32_t *width = _widths[0];
if(next_run)
{
// set the type, initial raster position and type of this run
next_run[0] = _rasterPosition.x >> 16;
next_run[1] = _rasterPosition.y >> 16;
next_run[2] = tex_x;
next_run[3] = tex_y;
next_run[0] = (_rasterPosition.x + width[0]) >> 16;
next_run[1] = (_rasterPosition.y + width[1]) >> 16;
next_run[4] = (_rasterPosition.x - width[0]) >> 16;
next_run[5] = (_rasterPosition.y - width[1]) >> 16;
next_run[2] = next_run[6] = tex_x;
next_run[3] = next_run[7] = tex_y;
}
// advance the raster position as dictated by current sync status
@ -192,15 +229,30 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
if(next_run)
{
// store the final raster position
next_run[4] = _rasterPosition.x >> 16;
next_run[5] = _rasterPosition.y >> 16;
next_run[8] = (_rasterPosition.x - width[0]) >> 16;
next_run[9] = (_rasterPosition.y - width[1]) >> 16;
next_run[12] = (_rasterPosition.x - width[0]) >> 16;
next_run[13] = (_rasterPosition.y - width[1]) >> 16;
next_run[16] = (_rasterPosition.x + width[0]) >> 16;
next_run[17] = (_rasterPosition.y + width[1]) >> 16;
next_run[20] = next_run[0];
next_run[21] = next_run[1];
// if this is a data run then advance the buffer pointer
if(type == Type::Data) tex_x += next_run_length;
if(type == Type::Data) tex_x += next_run_length / _time_multiplier;
// if this is a data or level run then store the end point
next_run[6] = tex_x;
next_run[7] = tex_y;
next_run[10] = tex_x;
next_run[11] = tex_y;
next_run[14] = tex_x;
next_run[15] = tex_y;
next_run[18] = tex_x;
next_run[19] = tex_y;
next_run[22] = next_run[2];
next_run[23] = next_run[3];
}
// decrement the number of cycles left to run for and increment the
@ -261,6 +313,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
_current_frame_builder->complete();
_frames_with_delegate++;
_delegate->crt_did_end_frame(this, &_current_frame_builder->frame);
// o = 0;
}
if(_frames_with_delegate < kCRTNumberOfFrames)
@ -362,7 +415,7 @@ CRTFrameBuilder::~CRTFrameBuilder()
void CRTFrameBuilder::reset()
{
frame.number_of_runs = 0;
_write_x_position = _write_y_position = 0;
_next_write_x_position = _next_write_y_position = 0;
frame.dirty_size.width = frame.dirty_size.height = 0;
}
@ -374,12 +427,12 @@ void CRTFrameBuilder::complete()
uint16_t *CRTFrameBuilder::get_next_run()
{
// get a run from the allocated list, allocating more if we're about to overrun
if(frame.number_of_runs * 8 >= _all_runs.size())
if(frame.number_of_runs * 24 >= _all_runs.size())
{
_all_runs.resize(_all_runs.size() + 4096);
_all_runs.resize(_all_runs.size() + 2400);
}
uint16_t *next_run = &_all_runs[frame.number_of_runs * 8];
uint16_t *next_run = &_all_runs[frame.number_of_runs * 24];
frame.number_of_runs++;
return next_run;
@ -387,16 +440,18 @@ uint16_t *CRTFrameBuilder::get_next_run()
void CRTFrameBuilder::allocate_write_area(int required_length)
{
if (_write_x_position + required_length > frame.size.width)
if (_next_write_x_position + required_length > frame.size.width)
{
_write_x_position = 0;
_write_y_position++;
_next_write_x_position = 0;
_next_write_y_position++;
frame.dirty_size.height++;
}
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
_write_x_position += required_length;
frame.dirty_size.width = std::max(frame.dirty_size.width, _write_x_position);
_write_x_position = _next_write_x_position;
_write_y_position = _next_write_y_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)

View File

@ -39,6 +39,7 @@ struct CRTFrameBuilder {
// a pointer to the section of content buffer currently being
// returned and to where the next section will begin
int _next_write_x_position, _next_write_y_position;
int _write_x_position, _write_y_position;
int _write_target_pointer;
};
@ -82,6 +83,8 @@ class CRT {
uint32_t x, y;
} _rasterPosition, _scanSpeed, _retraceSpeed;
uint32_t _widths[4][2];
// the run delegate and the triple buffer
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
CRTFrameBuilder *_current_frame_builder;