diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 8f33622f6..f76b3033e 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -26,12 +26,12 @@ Machine::Machine() : { _crt = new Outputs::CRT(228, 1, Outputs::CRT::DisplayType::NTSC60, 1, 2); _crt->set_composite_sampling_function( - "float sample(vec2 coordinate, float phase)\n" + "float composite_sample(vec2 coordinate, float phase)\n" "{\n" "vec2 c = texture(texID, coordinate).rg;" "float y = 0.1 + c.x * 0.91071428571429;\n" "float aOffset = 6.283185308 * (c.y - 3.0 / 16.0) * 1.14285714285714;\n" - "return y + step(0.03125, c.y) * 0.1 * cos(phase - aOffset);\n" + "return y + step(0.03125, c.y) * 0.1 * cos((coordinate.x * 2.0 * 3.141592654) - aOffset);\n" "}"); _crt->set_output_device(Outputs::CRT::Television); memset(_collisions, 0xff, sizeof(_collisions)); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 5031f594d..d0a996ce3 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -40,7 +40,7 @@ Machine::Machine() : "float texValue = texture(texID, coordinate).r;" "return vec4(step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)), 1.0);" "}"); - _crt->set_output_device(Outputs::CRT::Monitor); + _crt->set_output_device(Outputs::CRT::Television); // _crt->set_visible_area(Outputs::Rect(0.23108f, 0.0f, 0.8125f, 0.98f)); //1875 memset(_key_states, 0, sizeof(_key_states)); diff --git a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift index ec6b1e3ce..c9b385399 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift @@ -54,6 +54,10 @@ class Atari2600Document: MachineDocument { atari2600.runForNumberOfCycles(numberOfCycles) } + override func openGLView(view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) { + atari2600.drawViewForPixelSize(view.backingSize, onlyIfDirty: onlyIfDirty) + } + // MARK: CSOpenGLViewResponderDelegate private func inputForKey(event: NSEvent) -> Atari2600DigitalInput? { diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.h b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.h index 05a0b70c3..c0e097c52 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.h +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.h @@ -15,4 +15,6 @@ - (void)setState:(BOOL)state forDigitalInput:(Atari2600DigitalInput)digitalInput; - (void)setResetLineEnabled:(BOOL)enabled; +- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty; + @end diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm index 80e9d564f..8239d90df 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm @@ -47,6 +47,10 @@ _atari2600.run_for_cycles(numberOfCycles); } +- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { + _atari2600.get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); +} + - (void)setROM:(NSData *)rom { [self perform:^{ _atari2600.set_rom(rom.length, (const uint8_t *)rom.bytes); diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm index a24c5f788..d7dce90ce 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm @@ -54,10 +54,6 @@ return YES; } -- (void)setView:(CSOpenGLView *)view { - [super setView:view]; -} - - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { switch(key) { diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 946bd4ac5..560c124ae 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -373,6 +373,7 @@ class CRT { // Methods used by the OpenGL code void prepare_rgb_output_shader(); + void prepare_composite_input_shader(); void prepare_vertex_array(); void push_size_uniforms(unsigned int output_width, unsigned int output_height); diff --git a/Outputs/CRT/CRTOpenGL.cpp b/Outputs/CRT/CRTOpenGL.cpp index 7e5e50fe4..7607b826f 100644 --- a/Outputs/CRT/CRTOpenGL.cpp +++ b/Outputs/CRT/CRTOpenGL.cpp @@ -17,7 +17,7 @@ using namespace Outputs; struct CRT::OpenGLState { - std::unique_ptr shaderProgram; + std::unique_ptr rgb_shader_program, composite_input_shader_program; GLuint arrayBuffer, vertexArray; size_t verticesPerSlice; @@ -38,6 +38,10 @@ struct CRT::OpenGLState { std::unique_ptr filteredTexture; // receives filtered YIQ or YUV }; +namespace { + static const GLenum first_supplied_buffer_texture_unit = 3; +} + static GLenum formatForDepth(size_t depth) { switch(depth) @@ -75,7 +79,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++) { glGenTextures(1, &_openGL_state->textureName); - glActiveTexture(GL_TEXTURE3 + buffer); + glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); glBindTexture(GL_TEXTURE_2D, _openGL_state->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); @@ -90,6 +94,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool glGenBuffers(1, &_openGL_state->arrayBuffer); _openGL_state->verticesPerSlice = 0; + prepare_composite_input_shader(); prepare_rgb_output_shader(); glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->arrayBuffer); @@ -131,7 +136,7 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool // time as it may have had extra data appended to it for(unsigned int buffer = 0; buffer < _buffer_builder->number_of_buffers; buffer++) { - glActiveTexture(GL_TEXTURE3 + buffer); + glActiveTexture(GL_TEXTURE0 + first_supplied_buffer_texture_unit + buffer); GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel); if(_buffer_builder->_next_write_y_position < _buffer_builder->last_uploaded_line) { @@ -260,13 +265,13 @@ char *CRT::get_input_vertex_shader() "uniform vec2 textureSize;" "uniform float phaseCyclesPerTick;" - "out vec2 inputPositionVerying;" + "out vec2 inputPositionVarying;" "out float phaseVarying;" "void main(void)" "{" - "inputPositionVerying = inputPosition;" - "gl_Position = vec4(outputPosition.x * 2.0 / textureSize.width - 1.0, outputPosition.y * 2.0 / textureSize.height - 1.0, 0.0, 1.0);" + "inputPositionVarying = inputPosition;" + "gl_Position = vec4(outputPosition.x * 2.0 / textureSize.x - 1.0, outputPosition.y * 2.0 / textureSize.y - 1.0, 0.0, 1.0);" "phaseVarying = (phaseCyclesPerTick * phaseTime + phaseAndAmplitude.x) * 2.0 * 3.141592654;" "}"); } @@ -281,7 +286,7 @@ char *CRT::get_input_fragment_shader() return get_compound_shader( "#version 150\n" - "in vec2 inputPositionVerying;" + "in vec2 inputPositionVarying;" "in float phaseVarying;" "out vec4 fragColour;" @@ -439,6 +444,7 @@ char *CRT::get_output_fragment_shader(const char *sampling_function) char *CRT::get_compound_shader(const char *base, const char *insert) { + if(!base || !insert) return nullptr; size_t totalLength = strlen(base) + strlen(insert) + 1; char *text = new char[totalLength]; snprintf(text, totalLength, base, insert); @@ -447,43 +453,62 @@ char *CRT::get_compound_shader(const char *base, const char *insert) #pragma mark - Program compilation +void CRT::prepare_composite_input_shader() +{ + char *vertex_shader = get_input_vertex_shader(); + char *fragment_shader = get_input_fragment_shader(); + if(vertex_shader && fragment_shader) + { + _openGL_state->composite_input_shader_program = std::unique_ptr(new OpenGL::Shader(vertex_shader, fragment_shader)); + } + free(vertex_shader); + free(fragment_shader); +} + void CRT::prepare_rgb_output_shader() { char *vertex_shader = get_output_vertex_shader(); char *fragment_shader = get_rgb_output_fragment_shader(); - _openGL_state->shaderProgram = std::unique_ptr(new OpenGL::Shader(vertex_shader, fragment_shader)); - _openGL_state->shaderProgram->bind(); + if(vertex_shader && fragment_shader) + { + _openGL_state->rgb_shader_program = std::unique_ptr(new OpenGL::Shader(vertex_shader, fragment_shader)); - _openGL_state->positionAttribute = _openGL_state->shaderProgram->get_attrib_location("position"); - _openGL_state->textureCoordinatesAttribute = _openGL_state->shaderProgram->get_attrib_location("srcCoordinates"); - _openGL_state->lateralAttribute = _openGL_state->shaderProgram->get_attrib_location("lateral"); - _openGL_state->timestampAttribute = _openGL_state->shaderProgram->get_attrib_location("timestamp"); + _openGL_state->rgb_shader_program->bind(); - _openGL_state->windowSizeUniform = _openGL_state->shaderProgram->get_uniform_location("windowSize"); - _openGL_state->boundsSizeUniform = _openGL_state->shaderProgram->get_uniform_location("boundsSize"); - _openGL_state->boundsOriginUniform = _openGL_state->shaderProgram->get_uniform_location("boundsOrigin"); - _openGL_state->timestampBaseUniform = _openGL_state->shaderProgram->get_uniform_location("timestampBase"); + _openGL_state->positionAttribute = _openGL_state->rgb_shader_program->get_attrib_location("position"); + _openGL_state->textureCoordinatesAttribute = _openGL_state->rgb_shader_program->get_attrib_location("srcCoordinates"); + _openGL_state->lateralAttribute = _openGL_state->rgb_shader_program->get_attrib_location("lateral"); + _openGL_state->timestampAttribute = _openGL_state->rgb_shader_program->get_attrib_location("timestamp"); - GLint texIDUniform = _openGL_state->shaderProgram->get_uniform_location("texID"); - GLint shadowMaskTexIDUniform = _openGL_state->shaderProgram->get_uniform_location("shadowMaskTexID"); - GLint textureSizeUniform = _openGL_state->shaderProgram->get_uniform_location("textureSize"); - GLint ticksPerFrameUniform = _openGL_state->shaderProgram->get_uniform_location("ticksPerFrame"); - GLint scanNormalUniform = _openGL_state->shaderProgram->get_uniform_location("scanNormal"); - GLint positionConversionUniform = _openGL_state->shaderProgram->get_uniform_location("positionConversion"); + _openGL_state->windowSizeUniform = _openGL_state->rgb_shader_program->get_uniform_location("windowSize"); + _openGL_state->boundsSizeUniform = _openGL_state->rgb_shader_program->get_uniform_location("boundsSize"); + _openGL_state->boundsOriginUniform = _openGL_state->rgb_shader_program->get_uniform_location("boundsOrigin"); + _openGL_state->timestampBaseUniform = _openGL_state->rgb_shader_program->get_uniform_location("timestampBase"); - glUniform1i(texIDUniform, 3); - glUniform1i(shadowMaskTexIDUniform, 1); - glUniform2f(textureSizeUniform, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight); - glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display)); - glUniform2f(positionConversionUniform, _horizontal_flywheel->get_scan_period(), _vertical_flywheel->get_scan_period() / (unsigned int)_vertical_flywheel_output_divider); + GLint texIDUniform = _openGL_state->rgb_shader_program->get_uniform_location("texID"); + GLint shadowMaskTexIDUniform = _openGL_state->rgb_shader_program->get_uniform_location("shadowMaskTexID"); + GLint textureSizeUniform = _openGL_state->rgb_shader_program->get_uniform_location("textureSize"); + GLint ticksPerFrameUniform = _openGL_state->rgb_shader_program->get_uniform_location("ticksPerFrame"); + GLint scanNormalUniform = _openGL_state->rgb_shader_program->get_uniform_location("scanNormal"); + GLint positionConversionUniform = _openGL_state->rgb_shader_program->get_uniform_location("positionConversion"); - float scan_angle = atan2f(1.0f / (float)_height_of_display, 1.0f); - float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)}; - float multiplier = (float)_horizontal_flywheel->get_standard_period() / ((float)_height_of_display * (float)_horizontal_flywheel->get_scan_period()); - scan_normal[0] *= multiplier; - scan_normal[1] *= multiplier; - glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]); + glUniform1i(texIDUniform, first_supplied_buffer_texture_unit); + glUniform1i(shadowMaskTexIDUniform, 1); + glUniform2f(textureSizeUniform, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight); + glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display)); + glUniform2f(positionConversionUniform, _horizontal_flywheel->get_scan_period(), _vertical_flywheel->get_scan_period() / (unsigned int)_vertical_flywheel_output_divider); + + float scan_angle = atan2f(1.0f / (float)_height_of_display, 1.0f); + float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)}; + float multiplier = (float)_horizontal_flywheel->get_standard_period() / ((float)_height_of_display * (float)_horizontal_flywheel->get_scan_period()); + scan_normal[0] *= multiplier; + scan_normal[1] *= multiplier; + glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]); + } + + free(vertex_shader); + free(fragment_shader); } void CRT::prepare_vertex_array()