mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 01:32:55 +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:
parent
cd0a62d21e
commit
65bb31d55b
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user