diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 74f8aea6..25c6c5a7 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "103.19"; + static const string Version = "103.20"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index e1e9ff90..0ea2f9bd 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -18,19 +18,19 @@ ui_objects += $(if $(call streq,$(platform),windows),ui-resource) # platform ifeq ($(platform),windows) - ruby += video.direct3d #video.wgl video.directdraw video.gdi - ruby += audio.asio audio.wasapi #audio.xaudio2 audio.directsound + ruby += video.wgl video.direct3d video.directdraw video.gdi + ruby += audio.asio audio.wasapi audio.xaudio2 #audio.directsound ruby += input.windows else ifeq ($(platform),macosx) - ruby += #video.cgl + ruby += video.cgl ruby += audio.openal ruby += #input.quartz input.carbon else ifeq ($(platform),linux) - ruby += video.glx2 video.xvideo video.xshm video.sdl #video.glx + ruby += video.glx video.xvideo video.xshm video.sdl ruby += audio.oss audio.openal #audio.alsa audio.pulseaudio audio.pulseaudiosimple audio.ao ruby += input.sdl input.xlib #input.udev else ifeq ($(platform),bsd) - ruby += video.glx2 video.xvideo video.xshm video.sdl #video.glx + ruby += video.glx video.xvideo video.xshm video.sdl ruby += audio.oss audio.openal ruby += input.sdl input.xlib endif diff --git a/higan/target-tomoko/settings/settings.cpp b/higan/target-tomoko/settings/settings.cpp index 698a1d18..de6fb420 100644 --- a/higan/target-tomoko/settings/settings.cpp +++ b/higan/target-tomoko/settings/settings.cpp @@ -12,7 +12,7 @@ SettingsManager::SettingsManager() { layout.setMargin(5); statusBar.setFont(Font().setBold()); - setTitle("Configuration Settings"); + setTitle("Settings"); setSize({600, 405}); setAlignment({0.0, 1.0}); setDismissable(); diff --git a/icarus/ui/settings-dialog.cpp b/icarus/ui/settings-dialog.cpp index 80640d3f..a5a5191c 100644 --- a/icarus/ui/settings-dialog.cpp +++ b/icarus/ui/settings-dialog.cpp @@ -19,6 +19,6 @@ SettingsDialog::SettingsDialog() { settings["icarus/UseDatabase"].setValue(useDatabaseOption.checked()); }); - setTitle("icarus Settings"); + setTitle("Settings"); setSize({480, layout.minimumSize().height()}); } diff --git a/libco/amd64.c b/libco/amd64.c index 235708ab..15468090 100644 --- a/libco/amd64.c +++ b/libco/amd64.c @@ -1,9 +1,3 @@ -/* - libco.amd64 (2016-09-14) - author: byuu - license: public domain -*/ - #define LIBCO_C #include "libco.h" #include "settings.h" diff --git a/libco/arm.c b/libco/arm.c index 25f0b16c..313f1134 100644 --- a/libco/arm.c +++ b/libco/arm.c @@ -1,9 +1,3 @@ -/* - libco.arm (2016-09-14) - author: byuu - license: public domain -*/ - #define LIBCO_C #include "libco.h" #include "settings.h" diff --git a/libco/fiber.c b/libco/fiber.c index f2c5b726..38a293df 100644 --- a/libco/fiber.c +++ b/libco/fiber.c @@ -1,9 +1,3 @@ -/* - libco.win (2008-01-28) - authors: Nach, byuu - license: public domain -*/ - #define LIBCO_C #include "libco.h" diff --git a/libco/libco.c b/libco/libco.c index 13eb2379..77873fa3 100644 --- a/libco/libco.c +++ b/libco/libco.c @@ -1,8 +1,3 @@ -/* - libco - license: public domain -*/ - #if defined(__clang__) #pragma clang diagnostic ignored "-Wparentheses" #endif diff --git a/libco/ppc.c b/libco/ppc.c index efec3aa8..6d79b44f 100644 --- a/libco/ppc.c +++ b/libco/ppc.c @@ -1,9 +1,3 @@ -/* - libco.ppc (2016-09-14) - author: blargg - license: public domain -*/ - #define LIBCO_C #include "libco.h" #include "settings.h" diff --git a/libco/sjlj.c b/libco/sjlj.c index f62b000e..4d5017f5 100644 --- a/libco/sjlj.c +++ b/libco/sjlj.c @@ -1,9 +1,3 @@ -/* - libco.sjlj (2008-01-28) - author: Nach - license: public domain -*/ - /* note this was designed for UNIX systems. Based on ideas expressed in a paper by Ralf Engelschall. for SJLJ on other systems, one would want to rewrite springboard() and co_create() and hack the jmb_buf stack pointer. @@ -11,6 +5,7 @@ #define LIBCO_C #include "libco.h" +#include "settings.h" #define _XOPEN_SOURCE 500 #include diff --git a/libco/ucontext.c b/libco/ucontext.c index 72ea8719..f4527bfb 100644 --- a/libco/ucontext.c +++ b/libco/ucontext.c @@ -1,9 +1,3 @@ -/* - libco.ucontext (2008-01-28) - author: Nach - license: public domain -*/ - /* WARNING: the overhead of POSIX ucontext is very high, assembly versions of libco or libco_sjlj should be much faster diff --git a/libco/x86.c b/libco/x86.c index def3ac1c..b5c38216 100644 --- a/libco/x86.c +++ b/libco/x86.c @@ -1,9 +1,3 @@ -/* - libco.x86 (2016-09-14) - author: byuu - license: public domain -*/ - #define LIBCO_C #include "libco.h" #include "settings.h" diff --git a/ruby/audio/xaudio2.cpp b/ruby/audio/xaudio2.cpp index 97a5a2a8..fab701b4 100644 --- a/ruby/audio/xaudio2.cpp +++ b/ruby/audio/xaudio2.cpp @@ -2,11 +2,163 @@ #include struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { - ~AudioXAudio2() { term(); } + AudioXAudio2() { initialize(); } + ~AudioXAudio2() { terminate(); } - IXAudio2* pXAudio2 = nullptr; - IXAudio2MasteringVoice* pMasterVoice = nullptr; - IXAudio2SourceVoice* pSourceVoice = nullptr; + auto ready() -> bool { return _ready; } + + auto information() -> Information { + Information information; + information.devices = {"Default"}; + information.channels = {2}; + information.frequencies = {44100.0, 48000.0, 96000.0}; + information.latencies = {20, 40, 60, 80, 100}; + return information; + } + + auto blocking() -> bool { return _blocking; } + auto channels() -> uint { return _channels; } + auto frequency() -> double { return _frequency; } + auto latency() -> uint { return _latency; } + + auto setBlocking(bool blocking) -> bool { + if(_blocking == blocking) return true; + _blocking = blocking; + return true; + } + + auto setFrequency(double frequency) -> bool { + if(_frequency == frequency) return true; + _frequency = frequency; + return initialize(); + } + + auto setLatency(uint latency) -> bool { + if(_latency == latency) return true; + _latency = latency; + return initialize(); + } + + auto clear() -> void { + if(!_sourceVoice) return; + _sourceVoice->Stop(0); + _sourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers + + _bufferIndex = 0; + + _bufferOffset = 0; + if(_buffer) memory::fill(_buffer, _period * _bufferCount * sizeof(uint32_t)); + + _sourceVoice->Start(0); + } + + auto output(const double samples[]) -> void { + _buffer[_bufferIndex * _period + _bufferOffset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; + if(_bufferOffset < _period) return; + _bufferOffset = 0; + + if(_bufferQueue == _bufferCount - 1) { + if(_blocking) { + //wait until there is at least one other free buffer for the next sample + while(_bufferQueue == _bufferCount - 1); + } else { //we need one free buffer for the next sample, so ignore the current contents + return; + } + } + + pushBuffer(_period * 4, _buffer + _bufferIndex * _period); + _bufferIndex = (_bufferIndex + 1) % _bufferCount; + } + +private: + auto initialize() -> bool { + terminate(); + + _bufferCount = 8; + _period = _frequency * _latency / _bufferCount / 1000.0 + 0.5; + _buffer = new uint32_t[_period * _bufferCount]; + _bufferOffset = 0; + _bufferIndex = 0; + _bufferQueue = 0; + + if(FAILED(XAudio2Create(&_interface, 0 , XAUDIO2_DEFAULT_PROCESSOR))) return false; + + uint deviceCount = 0; + _interface->GetDeviceCount(&deviceCount); + if(deviceCount == 0) return terminate(), false; + + uint deviceID = 0; + for(uint deviceIndex : range(deviceCount)) { + XAUDIO2_DEVICE_DETAILS deviceDetails = {}; + _interface->GetDeviceDetails(deviceIndex, &deviceDetails); + if(deviceDetails.Role & DefaultGameDevice) deviceID = deviceIndex; + } + + if(FAILED(_interface->CreateMasteringVoice(&_masterVoice, _channels, (uint)_frequency, 0, deviceID, nullptr))) return terminate(), false; + + WAVEFORMATEX waveFormat; + waveFormat.wFormatTag = WAVE_FORMAT_PCM; + waveFormat.nChannels = _channels; + waveFormat.nSamplesPerSec = (uint)_frequency; + waveFormat.nBlockAlign = 4; + waveFormat.wBitsPerSample = 16; + waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; + waveFormat.cbSize = 0; + + if(FAILED(_interface->CreateSourceVoice(&_sourceVoice, (WAVEFORMATEX*)&waveFormat, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))) return terminate(), false; + + clear(); + return _ready = true; + } + + auto terminate() -> void { + _ready = false; + + if(_sourceVoice) { + _sourceVoice->Stop(0); + _sourceVoice->DestroyVoice(); + _sourceVoice = nullptr; + } + + if(_masterVoice) { + _masterVoice->DestroyVoice(); + _masterVoice = nullptr; + } + + if(_interface) { + _interface->Release(); + _interface = nullptr; + } + + delete[] _buffer; + _buffer = nullptr; + } + + auto pushBuffer(uint bytes, uint32_t* _audioData) -> void { + XAUDIO2_BUFFER buffer = {}; + buffer.AudioBytes = bytes; + buffer.pAudioData = reinterpret_cast(_audioData); + buffer.pContext = 0; + InterlockedIncrement(&_bufferQueue); + _sourceVoice->SubmitSourceBuffer(&buffer); + } + + bool _ready = false; + bool _blocking = true; + uint _channels = 2; + double _frequency = 48000.0; + uint _latency = 80; + + uint32_t* _buffer = nullptr; + uint _period = 0; + uint _bufferCount = 0; + uint _bufferOffset = 0; + uint _bufferIndex = 0; + volatile long _bufferQueue = 0; //how many buffers are queued and ready for playback + + IXAudio2* _interface = nullptr; + IXAudio2MasteringVoice* _masterVoice = nullptr; + IXAudio2SourceVoice* _sourceVoice = nullptr; //inherited from IXAudio2VoiceCallback STDMETHODIMP_(void) OnBufferStart(void* pBufferContext){} @@ -16,168 +168,7 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {} STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {} - struct { - unsigned buffers = 0; - unsigned latency = 0; - - uint32_t* buffer = nullptr; - unsigned bufferoffset = 0; - - volatile long submitbuffers = 0; - unsigned writebuffer = 0; - } device; - - struct { - bool synchronize = false; - unsigned frequency = 48000; - unsigned latency = 120; - } settings; - - auto cap(const string& name) -> bool { - if(name == Audio::Synchronize) return true; - if(name == Audio::Frequency) return true; - if(name == Audio::Latency) return true; - return false; - } - - auto get(const string& name) -> any { - if(name == Audio::Synchronize) return settings.synchronize; - if(name == Audio::Frequency) return settings.frequency; - if(name == Audio::Latency) return settings.latency; - return {}; - } - - auto set(const string& name, const any& value) -> bool { - if(name == Audio::Synchronize && value.is()) { - settings.synchronize = value.get(); - if(pXAudio2) clear(); - return true; - } - - if(name == Audio::Frequency && value.is()) { - settings.frequency = value.get(); - if(pXAudio2) init(); - return true; - } - - if(name == Audio::Latency && value.is()) { - settings.latency = value.get(); - if(pXAudio2) init(); - return true; - } - - return false; - } - - auto pushbuffer(unsigned bytes, uint32_t* pAudioData) -> void { - XAUDIO2_BUFFER xa2buffer = {0}; - xa2buffer.AudioBytes = bytes; - xa2buffer.pAudioData = reinterpret_cast(pAudioData); - xa2buffer.pContext = 0; - InterlockedIncrement(&device.submitbuffers); - pSourceVoice->SubmitSourceBuffer(&xa2buffer); - } - - auto sample(int16_t left, int16_t right) -> void { - device.buffer[device.writebuffer * device.latency + device.bufferoffset++] = (uint16_t)left << 0 | (uint16_t)right << 16; - if(device.bufferoffset < device.latency) return; - device.bufferoffset = 0; - - if(device.submitbuffers == device.buffers - 1) { - if(settings.synchronize == true) { - //wait until there is at least one other free buffer for the next sample - while(device.submitbuffers == device.buffers - 1) { - //Sleep(0); - } - } else { //we need one free buffer for the next sample, so ignore the current contents - return; - } - } - - pushbuffer(device.latency * 4,device.buffer + device.writebuffer * device.latency); - - device.writebuffer = (device.writebuffer + 1) % device.buffers; - } - - auto clear() -> void { - if(!pSourceVoice) return; - pSourceVoice->Stop(0); - pSourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers - - device.writebuffer = 0; - - device.bufferoffset = 0; - if(device.buffer) memset(device.buffer, 0, device.latency * device.buffers * 4); - - pSourceVoice->Start(0); - } - - auto init() -> bool { - device.buffers = 8; - device.latency = settings.frequency * settings.latency / device.buffers / 1000.0 + 0.5; - device.buffer = new uint32_t[device.latency * device.buffers]; - device.bufferoffset = 0; - device.submitbuffers = 0; - - HRESULT hr; - if(FAILED(hr = XAudio2Create(&pXAudio2, 0 , XAUDIO2_DEFAULT_PROCESSOR))) { - return false; - } - - unsigned deviceCount = 0; - pXAudio2->GetDeviceCount(&deviceCount); - if(deviceCount == 0) { term(); return false; } - - unsigned deviceID = 0; - for(unsigned deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { - XAUDIO2_DEVICE_DETAILS deviceDetails; - memset(&deviceDetails, 0, sizeof(XAUDIO2_DEVICE_DETAILS)); - pXAudio2->GetDeviceDetails(deviceIndex, &deviceDetails); - if(deviceDetails.Role & DefaultGameDevice) deviceID = deviceIndex; - } - - if(FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice, 2, settings.frequency, 0, deviceID, NULL))) { - return false; - } - - WAVEFORMATEX wfx; - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = 2; - wfx.nSamplesPerSec = settings.frequency; - wfx.nBlockAlign = 4; - wfx.wBitsPerSample = 16; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - wfx.cbSize = 0; - - if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, NULL, NULL))) { - return false; - } - - clear(); - return true; - } - - auto term() -> void { - if(pSourceVoice) { - pSourceVoice->Stop(0); - pSourceVoice->DestroyVoice(); - pSourceVoice = nullptr; - } - if(pMasterVoice) { - pMasterVoice->DestroyVoice(); - pMasterVoice = nullptr; - } - if(pXAudio2) { - pXAudio2->Release(); - pXAudio2 = nullptr; - } - if(device.buffer) { - delete[] device.buffer; - device.buffer = nullptr; - } - } - STDMETHODIMP_(void) OnBufferEnd(void* pBufferContext) { - InterlockedDecrement(&device.submitbuffers); + InterlockedDecrement(&_bufferQueue); } }; diff --git a/ruby/video/cgl.cpp b/ruby/video/cgl.cpp index b51212a1..79fcd880 100644 --- a/ruby/video/cgl.cpp +++ b/ruby/video/cgl.cpp @@ -12,81 +12,50 @@ struct VideoCGL; @end struct VideoCGL : Video, OpenGL { - ~VideoCGL() { term(); } + VideoCGL() { initialize(); } + ~VideoCGL() { terminate(); } - RubyVideoCGL* view = nullptr; + auto ready() -> bool { return _ready; } - struct { - NSView* handle = nullptr; - bool synchronize = false; - uint filter = Video::FilterNearest; - string shader; - } settings; + auto context() -> uintptr { return (uintptr)_context; } + auto blocking() -> bool { return _blocking; } + auto smooth() -> bool { return _smooth; } + auto shader() -> string { return _shader; } - auto cap(const string& name) -> bool { - if(name == Video::Handle) return true; - if(name == Video::Synchronize) return true; - if(name == Video::Filter) return true; - if(name == Video::Shader) return true; - return false; + auto setContext(uintptr context) -> bool { + if(_context == (NSView*)context) return true; + _context = (NSView*)context; + return initialize(); } - auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; - if(name == Video::Synchronize) return settings.synchronize; - if(name == Video::Filter) return settings.filter; - return {}; + auto setBlocking(bool blocking) -> bool { + if(_blocking == blocking) return true; + _blocking = blocking; + if(!view) return true; + @autoreleasepool { + [[view openGLContext] makeCurrentContext]; + int blocking = _blocking; + [[view openGLContext] setValues:&blocking forParameter:NSOpenGLCPSwapInterval]; + } + return true; } - auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (NSView*)value.get(); - return true; - } - - if(name == Video::Synchronize && value.is()) { - if(settings.synchronize != value.get()) { - settings.synchronize = value.get(); - - if(view) { - @autoreleasepool { - [[view openGLContext] makeCurrentContext]; - int synchronize = settings.synchronize; - [[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval]; - } - } - } - return true; - } - - if(name == Video::Filter && value.is()) { - settings.filter = value.get(); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - if(name == Video::Shader && value.is()) { - settings.shader = value.get(); - @autoreleasepool { - [[view openGLContext] makeCurrentContext]; - } - OpenGL::shader(settings.shader); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - return false; + auto setSmooth(bool smooth) -> bool { + if(_smooth == smooth) return true; + _smooth = smooth; + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; } - auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { - OpenGL::size(width, height); - return OpenGL::lock(data, pitch); - } - - auto unlock() -> void { + auto setShader(string shader) -> string { + if(_shader == shader) return true; + OpenGL::shader(_shader = shader); + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; } auto clear() -> void { + if(!ready()) return; @autoreleasepool { [view lockFocus]; OpenGL::clear(); @@ -95,22 +64,37 @@ struct VideoCGL : Video, OpenGL { } } - auto refresh() -> void { + auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; + OpenGL::size(width, height); + return OpenGL::lock(data, pitch); + } + + auto unlock() -> void { + if(!ready()) return; + } + + auto output() -> void { + if(!ready()) return; @autoreleasepool { if([view lockFocusIfCanDraw]) { auto area = [view frame]; - outputWidth = area.size.width; - outputHeight = area.size.height; - OpenGL::refresh(); + OpenGL::outputWidth = area.size.width; + OpenGL::outputHeight = area.size.height; + OpenGL::output(); [[view openGLContext] flushBuffer]; [view unlockFocus]; } } } - auto init() -> bool { +private: + auto initialize() -> bool { + terminate(); + if(!_context) return false; + @autoreleasepool { - NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPixelFormatAttribute attributeList[] = { NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, @@ -118,41 +102,49 @@ struct VideoCGL : Video, OpenGL { 0 }; - auto size = [settings.handle frame].size; - auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]; + auto size = [_context frame].size; + auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributeList] autorelease]; auto context = [[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease]; view = [[RubyVideoCGL alloc] initWith:this pixelFormat:format]; [view setOpenGLContext:context]; [view setFrame:NSMakeRect(0, 0, size.width, size.height)]; [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [settings.handle addSubview:view]; + [_context addSubview:view]; [context setView:view]; [view lockFocus]; - OpenGL::init(); - //print((const char*)glGetString(GL_VERSION), "\n"); + OpenGL::initialize(); - int synchronize = settings.synchronize; - [[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval]; + int blocking = _blocking; + [[view openGLContext] setValues:&blocking forParameter:NSOpenGLCPSwapInterval]; [view unlockFocus]; } clear(); - return true; + return _ready = true; } auto term() -> void { - OpenGL::term(); + _ready = false; + OpenGL::terminate(); + if(!view) return; @autoreleasepool { [view removeFromSuperview]; [view release]; view = nil; } } + + RubyVideoCGL* view = nullptr; + + NSView* _context = nullptr; + bool _blocking = false; + bool _smooth = true; + string _shader; }; @implementation RubyVideoCGL : NSOpenGLView @@ -165,7 +157,7 @@ struct VideoCGL : Video, OpenGL { } -(void) reshape { - video->refresh(); + video->output(); } @end diff --git a/ruby/video/direct3d.cpp b/ruby/video/direct3d.cpp index 9aa514ec..04f76585 100644 --- a/ruby/video/direct3d.cpp +++ b/ruby/video/direct3d.cpp @@ -42,6 +42,7 @@ struct VideoDirect3D : Video { } auto clear() -> void { + if(!ready()) return; if(_lost && !recover()) return; D3DSURFACE_DESC surfaceDescription; @@ -62,6 +63,7 @@ struct VideoDirect3D : Video { } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; if(_lost && !recover()) return false; if(width != _inputWidth || height != _inputHeight) { @@ -79,12 +81,14 @@ struct VideoDirect3D : Video { } auto unlock() -> void { + if(!ready()) return; _surface->UnlockRect(); _surface->Release(); _surface = nullptr; } auto output() -> void { + if(!ready()) return; if(_lost && !recover()) return; RECT rectangle; diff --git a/ruby/video/directdraw.cpp b/ruby/video/directdraw.cpp index 6cdb3f91..fd40efce 100644 --- a/ruby/video/directdraw.cpp +++ b/ruby/video/directdraw.cpp @@ -1,173 +1,167 @@ #include +#undef interface struct VideoDirectDraw : Video { - ~VideoDirectDraw() { term(); } + VideoDirectDraw() { initialize(); } + ~VideoDirectDraw() { terminate(); } - LPDIRECTDRAW lpdd = nullptr; - LPDIRECTDRAW7 lpdd7 = nullptr; - LPDIRECTDRAWSURFACE7 screen = nullptr; - LPDIRECTDRAWSURFACE7 raster = nullptr; - LPDIRECTDRAWCLIPPER clipper = nullptr; - DDSURFACEDESC2 ddsd; - DDSCAPS2 ddscaps; - uint iwidth; - uint iheight; + auto ready() -> bool { return _ready; } - struct { - HWND handle = nullptr; - bool synchronize = false; + auto context() -> uintptr { return _context; } + auto blocking() -> bool { return _blocking; } - uint width; - uint height; - } settings; - - auto cap(const string& name) -> bool { - if(name == Video::Handle) return true; - if(name == Video::Synchronize) return true; - return false; + auto setContext(uintptr context) -> bool { + if(_context == context) return true; + _context = context; + return initialize(); } - auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; - if(name == Video::Synchronize) return settings.synchronize; - return {}; - } - - auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (HWND)value.get(); - return true; - } - - if(name == Video::Synchronize && value.is()) { - settings.synchronize = value.get(); - return true; - } - - return false; - } - - auto resize(uint width, uint height) -> void { - if(iwidth >= width && iheight >= height) return; - - iwidth = max(width, iwidth); - iheight = max(height, iheight); - - if(raster) raster->Release(); - - screen->GetSurfaceDesc(&ddsd); - int depth = ddsd.ddpfPixelFormat.dwRGBBitCount; - if(depth == 32) goto try_native_surface; - - memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); - ddsd.dwSize = sizeof(DDSURFACEDESC2); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY - ddsd.dwWidth = iwidth; - ddsd.dwHeight = iheight; - - ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); - ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB; - ddsd.ddpfPixelFormat.dwRGBBitCount = 32; - ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000; - ddsd.ddpfPixelFormat.dwGBitMask = 0x00ff00; - ddsd.ddpfPixelFormat.dwBBitMask = 0x0000ff; - - if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear(); - - try_native_surface: - memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); - ddsd.dwSize = sizeof(DDSURFACEDESC2); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY - ddsd.dwWidth = iwidth; - ddsd.dwHeight = iheight; - - if(lpdd7->CreateSurface(&ddsd, &raster, 0) == DD_OK) return clear(); - } - - auto clear() -> void { - DDBLTFX fx; - fx.dwSize = sizeof(DDBLTFX); - fx.dwFillColor = 0x00000000; - screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); - raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); - } - - auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { - if(width != settings.width || height != settings.height) { - resize(settings.width = width, settings.height = height); - } - - if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) { - raster->Restore(); - if(raster->Lock(0, &ddsd, DDLOCK_WAIT, 0) != DD_OK) return false; - } - pitch = ddsd.lPitch; - return data = (uint32_t*)ddsd.lpSurface; - } - - auto unlock() -> void { - raster->Unlock(0); - } - - auto refresh() -> void { - if(settings.synchronize) { - while(true) { - BOOL in_vblank; - lpdd7->GetVerticalBlankStatus(&in_vblank); - if(in_vblank == true) break; - } - } - - HRESULT hr; - RECT rd, rs; - SetRect(&rs, 0, 0, settings.width, settings.height); - - POINT p = {0, 0}; - ClientToScreen(settings.handle, &p); - GetClientRect(settings.handle, &rd); - OffsetRect(&rd, p.x, p.y); - - if(screen->Blt(&rd, raster, &rs, DDBLT_WAIT, 0) == DDERR_SURFACELOST) { - screen->Restore(); - raster->Restore(); - } - } - - auto init() -> bool { - term(); - - DirectDrawCreate(0, &lpdd, 0); - lpdd->QueryInterface(IID_IDirectDraw7, (void**)&lpdd7); - if(lpdd) { lpdd->Release(); lpdd = 0; } - - lpdd7->SetCooperativeLevel(settings.handle, DDSCL_NORMAL); - - memset(&ddsd, 0, sizeof(DDSURFACEDESC2)); - ddsd.dwSize = sizeof(DDSURFACEDESC2); - - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - lpdd7->CreateSurface(&ddsd, &screen, 0); - - lpdd7->CreateClipper(0, &clipper, 0); - clipper->SetHWnd(0, settings.handle); - screen->SetClipper(clipper); - - raster = 0; - iwidth = 0; - iheight = 0; - resize(settings.width = 256, settings.height = 256); - + auto setBlocking(bool blocking) -> bool { + if(_blocking == blocking) return true; + _blocking = blocking; return true; } - auto term() -> void { - if(clipper) { clipper->Release(); clipper = 0; } - if(raster) { raster->Release(); raster = 0; } - if(screen) { screen->Release(); screen = 0; } - if(lpdd7) { lpdd7->Release(); lpdd7 = 0; } - if(lpdd) { lpdd->Release(); lpdd = 0; } + auto clear() -> void { + if(!ready()) return; + DDBLTFX fx = {}; + fx.dwSize = sizeof(DDBLTFX); + fx.dwFillColor = 0x00000000; + _screen->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); + _raster->Blt(0, 0, 0, DDBLT_WAIT | DDBLT_COLORFILL, &fx); } + + auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; + if(width != _width || height != _height) resize(_width = width, _height = height); + DDSURFACEDESC2 description = {}; + description.dwSize = sizeof(DDSURFACEDESC2); + if(_raster->Lock(0, &description, DDLOCK_WAIT, 0) != DD_OK) { + _raster->Restore(); + if(_raster->Lock(0, &description, DDLOCK_WAIT, 0) != DD_OK) return false; + } + pitch = description.lPitch; + return data = (uint32_t*)description.lpSurface; + } + + auto unlock() -> void { + if(!ready()) return; + _raster->Unlock(0); + } + + auto output() -> void { + if(!ready()) return; + if(_blocking) while(true) { + BOOL vblank; + _interface->GetVerticalBlankStatus(&vblank); + if(vblank) break; + } + + RECT source; + SetRect(&source, 0, 0, _width, _height); + + POINT point = {0, 0}; + ClientToScreen((HWND)_context, &point); + + RECT target; + GetClientRect((HWND)_context, &target); + OffsetRect(&target, point.x, point.y); + + if(_screen->Blt(&target, _raster, &source, DDBLT_WAIT, 0) == DDERR_SURFACELOST) { + _screen->Restore(); + _raster->Restore(); + } + } + +private: + auto initialize() -> bool { + terminate(); + if(!_context) return false; + + LPDIRECTDRAW interface = nullptr; + DirectDrawCreate(0, &interface, 0); + interface->QueryInterface(IID_IDirectDraw7, (void**)&_interface); + interface->Release(); + + _interface->SetCooperativeLevel((HWND)_context, DDSCL_NORMAL); + + DDSURFACEDESC2 description = {}; + description.dwSize = sizeof(DDSURFACEDESC2); + description.dwFlags = DDSD_CAPS; + description.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + _interface->CreateSurface(&description, &_screen, 0); + + _interface->CreateClipper(0, &_clipper, 0); + _clipper->SetHWnd(0, (HWND)_context); + _screen->SetClipper(_clipper); + + _raster = nullptr; + _surfaceWidth = 0; + _surfaceHeight = 0; + resize(_width = 256, _height = 256); + return _ready = true; + } + + auto terminate() -> void { + _ready = false; + if(_clipper) { _clipper->Release(); _clipper = nullptr; } + if(_raster) { _raster->Release(); _raster = nullptr; } + if(_screen) { _screen->Release(); _screen = nullptr; } + if(_interface) { _interface->Release(); _interface = nullptr; } + } + + auto resize(uint width, uint height) -> void { + if(_surfaceWidth >= width && _surfaceHeight >= height) return; + + _surfaceWidth = max(width, _surfaceWidth); + _surfaceHeight = max(height, _surfaceHeight); + + if(_raster) _raster->Release(); + + DDSURFACEDESC2 description = {}; + description.dwSize = sizeof(DDSURFACEDESC2); + _screen->GetSurfaceDesc(&description); + int depth = description.ddpfPixelFormat.dwRGBBitCount; + if(depth == 32) goto tryNativeSurface; + + memory::fill(&description, sizeof(DDSURFACEDESC2)); + description.dwSize = sizeof(DDSURFACEDESC2); + description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + description.dwWidth = _surfaceWidth; + description.dwHeight = _surfaceHeight; + + description.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT); + description.ddpfPixelFormat.dwFlags = DDPF_RGB; + description.ddpfPixelFormat.dwRGBBitCount = 32; + description.ddpfPixelFormat.dwRBitMask = 0xff0000; + description.ddpfPixelFormat.dwGBitMask = 0x00ff00; + description.ddpfPixelFormat.dwBBitMask = 0x0000ff; + + if(_interface->CreateSurface(&description, &_raster, 0) == DD_OK) return clear(); + + tryNativeSurface: + memory::fill(&description, sizeof(DDSURFACEDESC2)); + description.dwSize = sizeof(DDSURFACEDESC2); + description.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + description.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; //DDSCAPS_SYSTEMMEMORY + description.dwWidth = _surfaceWidth; + description.dwHeight = _surfaceHeight; + + if(_interface->CreateSurface(&description, &_raster, 0) == DD_OK) return clear(); + } + + bool _ready = false; + uintptr _context = 0; + bool _blocking = false; + + uint _width = 0; + uint _height = 0; + + LPDIRECTDRAW7 _interface = nullptr; + LPDIRECTDRAWSURFACE7 _screen = nullptr; + LPDIRECTDRAWSURFACE7 _raster = nullptr; + LPDIRECTDRAWCLIPPER _clipper = nullptr; + uint _surfaceWidth = 0; + uint _surfaceHeight = 0; }; diff --git a/ruby/video/gdi.cpp b/ruby/video/gdi.cpp index 3afb811e..c1b96cc3 100644 --- a/ruby/video/gdi.cpp +++ b/ruby/video/gdi.cpp @@ -1,103 +1,94 @@ -#include - struct VideoGDI : Video { - ~VideoGDI() { term(); } + VideoGDI() { initialize(); } + ~VideoGDI() { terminate(); } - struct Device { - HBITMAP bitmap = nullptr; - HDC dc = nullptr; - BITMAPINFO info = {}; - } device; + auto ready() -> bool { return _ready; } - struct Settings { - HWND handle = nullptr; + auto context() -> uintptr { return _context; } - uint32_t* buffer = nullptr; - uint width = 0; - uint height = 0; - } settings; - - auto cap(const string& name) -> bool { - if(name == Video::Handle) return true; - return false; + auto setContext(uintptr context) -> bool { + if(_context == context) return true; + _context = context; + return initialize(); } - auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; - return {}; - } - - auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (HWND)value.get(); - return true; - } - - return false; + auto clear() -> void { + if(!ready()) return; } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { - if(!settings.buffer || settings.width != width || settings.height != height) { - if(settings.buffer) { - delete[] settings.buffer; - DeleteObject(device.bitmap); - DeleteObject(device.dc); - } - settings.buffer = new uint32_t[width * height](); - settings.width = width; - settings.height = height; + if(!ready()) return false; - HDC hdc = GetDC(settings.handle); - device.dc = CreateCompatibleDC(hdc); - assert(device.dc); - device.bitmap = CreateCompatibleBitmap(hdc, width, height); - assert(device.bitmap); - SelectObject(device.dc, device.bitmap); - ReleaseDC(settings.handle, hdc); + if(!_buffer || _width != width || _height != height) { + if(_buffer) delete[] _buffer; + if(_bitmap) DeleteObject(_bitmap); + if(_dc) DeleteObject(_dc); - memory::fill(&device.info, sizeof(BITMAPINFO)); - device.info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - device.info.bmiHeader.biWidth = width; - device.info.bmiHeader.biHeight = -height; - device.info.bmiHeader.biPlanes = 1; - device.info.bmiHeader.biBitCount = 32; - device.info.bmiHeader.biCompression = BI_RGB; - device.info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t); + _buffer = new uint32_t[width * height](); + _width = width; + _height = height; + + HDC hdc = GetDC((HWND)_context); + _dc = CreateCompatibleDC(hdc); + _bitmap = CreateCompatibleBitmap(hdc, width, height); + SelectObject(_dc, _bitmap); + ReleaseDC((HWND)_context, hdc); + + memory::fill(&_info, sizeof(BITMAPINFO)); + _info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + _info.bmiHeader.biWidth = width; + _info.bmiHeader.biHeight = -height; + _info.bmiHeader.biPlanes = 1; + _info.bmiHeader.biBitCount = 32; + _info.bmiHeader.biCompression = BI_RGB; + _info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t); } - data = settings.buffer; - pitch = settings.width * sizeof(uint32_t); - return true; + pitch = _width * sizeof(uint32_t); + return data = _buffer; } - auto unlock() -> void {} + auto unlock() -> void { + if(!ready()) return; + } - auto clear() -> void {} + auto output() -> void { + if(!ready()) return; - auto refresh() -> void { RECT rc; - GetClientRect(settings.handle, &rc); + GetClientRect((HWND)_context, &rc); - SetDIBits(device.dc, device.bitmap, 0, settings.height, (void*)settings.buffer, &device.info, DIB_RGB_COLORS); - HDC hdc = GetDC(settings.handle); - StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, device.dc, 0, 0, settings.width, settings.height, SRCCOPY); - ReleaseDC(settings.handle, hdc); + SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS); + HDC hdc = GetDC((HWND)_context); + StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, _dc, 0, 0, _width, _height, SRCCOPY); + ReleaseDC((HWND)_context, hdc); } - auto init() -> bool { - settings.width = 0; - settings.height = 0; - return true; +private: + auto initialize() -> bool { + terminate(); + if(!_context) return false; + + _width = 0; + _height = 0; + return _ready = true; } - auto term() -> void { - if(settings.buffer) { - delete[] settings.buffer; - DeleteObject(device.bitmap); - DeleteDC(device.dc); - settings.buffer = nullptr; - device.bitmap = nullptr; - device.dc = nullptr; - } + auto terminate() -> void { + _ready = false; + if(_buffer) { delete[] _buffer; _buffer = nullptr; } + if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; } + if(_dc) { DeleteDC(_dc); _dc = nullptr; } } + + bool _ready = false; + uintptr _context = 0; + + uint32_t* _buffer = nullptr; + uint _width = 0; + uint _height = 0; + + HBITMAP _bitmap = nullptr; + HDC _dc = nullptr; + BITMAPINFO _info = {}; }; diff --git a/ruby/video/glx.cpp b/ruby/video/glx.cpp index 679a9f74..624cb0a1 100644 --- a/ruby/video/glx.cpp +++ b/ruby/video/glx.cpp @@ -8,185 +8,154 @@ auto VideoGLX_X11ErrorHandler(Display*, XErrorEvent*) -> int { } struct VideoGLX : Video, OpenGL { - ~VideoGLX() { term(); } + VideoGLX() { initialize(); } + ~VideoGLX() { terminate(); } - auto (*glXSwapInterval)(signed) -> signed = nullptr; + auto ready() -> bool { return _ready; } - Display* display = nullptr; - signed screen = 0; - Window xwindow = 0; - Colormap colormap = 0; - GLXContext glxcontext = nullptr; - GLXWindow glxwindow = 0; + auto context() -> uintptr { return _context; } + auto blocking() -> bool { return _blocking; } + auto depth() -> uint { return _depth; } + auto smooth() -> bool { return _smooth; } + auto shader() -> string { return _shader; } - struct { - signed versionMajor = 0; - signed versionMinor = 0; - bool doubleBuffer = false; - bool isDirect = false; - } glx; - - struct { - Window handle = 0; - bool synchronize = false; - unsigned depth = 24; - unsigned filter = 1; //linear - string shader; - } settings; - - auto cap(const string& name) -> bool { - if(name == Video::Handle) return true; - if(name == Video::Synchronize) return true; - if(name == Video::Depth) return true; - if(name == Video::Filter) return true; - if(name == Video::Shader) return true; - return false; + auto setContext(uintptr context) -> bool { + if(_context == context) return true; + _context = context; + return initialize(); } - auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; - if(name == Video::Synchronize) return settings.synchronize; - if(name == Video::Depth) return settings.depth; - if(name == Video::Filter) return settings.filter; - if(name == Video::Shader) return settings.shader; - return {}; + auto setBlocking(bool blocking) -> bool { + if(_blocking == blocking) return true; + _blocking = blocking; + if(glXSwapInterval) glXSwapInterval(_blocking); + return true; } - auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = value.get(); - return true; + auto setDepth(uint depth) -> bool { + if(_depth == depth) return true; + switch(depth) { + case 24: _depth = depth; OpenGL::inputFormat = GL_RGBA8; return true; + case 30: _depth = depth; OpenGL::inputFormat = GL_RGB10_A2; return true; + default: return false; } - - if(name == Video::Synchronize && value.is()) { - if(settings.synchronize != value.get()) { - settings.synchronize = value.get(); - if(glXSwapInterval) glXSwapInterval(settings.synchronize); - return true; - } - } - - if(name == Video::Depth && value.is()) { - unsigned depth = value.get(); - if(depth > DefaultDepth(display, screen)) return false; - - switch(depth) { - case 24: inputFormat = GL_RGBA8; break; - case 30: inputFormat = GL_RGB10_A2; break; - default: return false; - } - - settings.depth = depth; - return true; - } - - if(name == Video::Filter && value.is()) { - settings.filter = value.get(); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - if(name == Video::Shader && value.is()) { - settings.shader = value.get(); - OpenGL::shader(settings.shader); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - return false; } - auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { + auto setSmooth(bool smooth) -> bool { + if(_smooth == smooth) return true; + _smooth = smooth; + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; + } + + auto setShader(string shader) -> bool { + if(_shader == shader) return true; + OpenGL::shader(_shader = shader); + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; + } + + auto clear() -> void { + if(!ready()) return; + OpenGL::clear(); + if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow); + } + + auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; OpenGL::size(width, height); return OpenGL::lock(data, pitch); } auto unlock() -> void { + if(!ready()) return; } - auto clear() -> void { - OpenGL::clear(); - if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow); - } + auto output() -> void { + if(!ready()) return; - auto refresh() -> void { //we must ensure that the child window is the same size as the parent window. //unfortunately, we cannot hook the parent window resize event notification, //as we did not create the parent window, nor have any knowledge of the toolkit used. //therefore, inelegant as it may be, we query each window size and resize as needed. XWindowAttributes parent, child; - XGetWindowAttributes(display, settings.handle, &parent); - XGetWindowAttributes(display, xwindow, &child); + XGetWindowAttributes(_display, (Window)_context, &parent); + XGetWindowAttributes(_display, (Window)_window, &child); if(child.width != parent.width || child.height != parent.height) { - XResizeWindow(display, xwindow, parent.width, parent.height); + XResizeWindow(_display, _window, parent.width, parent.height); } - outputWidth = parent.width, outputHeight = parent.height; - OpenGL::refresh(); - if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow); + OpenGL::outputWidth = parent.width; + OpenGL::outputHeight = parent.height; + OpenGL::output(); + if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow); } - auto init() -> bool { - display = XOpenDisplay(0); - screen = DefaultScreen(display); +private: + auto initialize() -> bool { + terminate(); + if(!_context) return false; + + _display = XOpenDisplay(nullptr); + _screen = DefaultScreen(_display); //require GLX 1.2+ API - glXQueryVersion(display, &glx.versionMajor, &glx.versionMinor); - if(glx.versionMajor < 1 || (glx.versionMajor == 1 && glx.versionMinor < 2)) return false; + glXQueryVersion(_display, &_versionMajor, &_versionMinor); + if(_versionMajor < 1 || (_versionMajor == 1 && _versionMinor < 2)) return false; XWindowAttributes windowAttributes; - XGetWindowAttributes(display, settings.handle, &windowAttributes); + XGetWindowAttributes(_display, (Window)_context, &windowAttributes); //let GLX determine the best Visual to use for GL output; provide a few hints //note: some video drivers will override double buffering attribute - signed attributeList[] = { + int attributeList[] = { GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DOUBLEBUFFER, True, - GLX_RED_SIZE, (signed)(settings.depth / 3), - GLX_GREEN_SIZE, (signed)(settings.depth / 3) + (signed)(settings.depth % 3), - GLX_BLUE_SIZE, (signed)(settings.depth / 3), + GLX_RED_SIZE, (int)(_depth / 3), + GLX_GREEN_SIZE, (int)(_depth / 3) + (int)(_depth % 3), + GLX_BLUE_SIZE, (int)(_depth / 3), None }; - signed fbCount = 0; - GLXFBConfig* fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount); + int fbCount = 0; + GLXFBConfig* fbConfig = glXChooseFBConfig(_display, _screen, attributeList, &fbCount); if(fbCount == 0) return false; - XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbConfig[0]); + XVisualInfo* vi = glXGetVisualFromFBConfig(_display, fbConfig[0]); - //Window settings.handle has already been realized, most likely with DefaultVisual. + //(Window)_context has already been realized, most likely with DefaultVisual. //GLX requires that the GL output window has the same Visual as the GLX context. //it is not possible to change the Visual of an already realized (created) window. - //therefore a new child window, using the same GLX Visual, must be created and binded to settings.handle. - colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); + //therefore a new child window, using the same GLX Visual, must be created and binded to it. + _colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone); XSetWindowAttributes attributes; - attributes.colormap = colormap; + attributes.colormap = _colormap; attributes.border_pixel = 0; - xwindow = XCreateWindow(display, /* parent = */ settings.handle, + _window = XCreateWindow(_display, /* parent = */ (Window)_context, /* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height, /* border_width = */ 0, vi->depth, InputOutput, vi->visual, CWColormap | CWBorderPixel, &attributes); - XSetWindowBackground(display, xwindow, /* color = */ 0); - XMapWindow(display, xwindow); - XFlush(display); + XSetWindowBackground(_display, _window, /* color = */ 0); + XMapWindow(_display, _window); + XFlush(_display); //window must be realized (appear onscreen) before we make the context current - while(XPending(display)) { + while(XPending(_display)) { XEvent event; - XNextEvent(display, &event); + XNextEvent(_display, &event); } - glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); - glXMakeCurrent(display, glxwindow = xwindow, glxcontext); + _glXContext = glXCreateContext(_display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); + glXMakeCurrent(_display, _glXWindow = _window, _glXContext); //glXSwapInterval is used to toggle Vsync //note that the ordering is very important! MESA declares SGI, but the SGI function does nothing - if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalMESA"); - if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalSGI"); + if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA"); + if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI"); - if(auto glXCreateContextAttribs = (auto (*)(Display*, GLXFBConfig, GLXContext, signed, const signed*) -> GLXContext)glGetProcAddress("glXCreateContextAttribsARB")) { - signed attributes[] = { + if(auto glXCreateContextAttribs = (auto (*)(Display*, GLXFBConfig, GLXContext, int, const int*) -> GLXContext)glGetProcAddress("glXCreateContextAttribsARB")) { + int attributes[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 2, None @@ -194,14 +163,14 @@ struct VideoGLX : Video, OpenGL { //glXCreateContextAttribs tends to throw BadRequest errors instead of simply failing gracefully auto originalHandler = XSetErrorHandler(VideoGLX_X11ErrorHandler); - auto context = glXCreateContextAttribs(display, fbConfig[0], nullptr, true, attributes); - XSync(display, False); + auto context = glXCreateContextAttribs(_display, fbConfig[0], nullptr, true, attributes); + XSync(_display, False); XSetErrorHandler(originalHandler); if(context) { - glXMakeCurrent(display, 0, nullptr); - glXDestroyContext(display, glxcontext); - glXMakeCurrent(display, glxwindow, glxcontext = context); + glXMakeCurrent(_display, 0, nullptr); + glXDestroyContext(_display, _glXContext); + glXMakeCurrent(_display, _glXWindow, _glXContext = context); } else { //OpenGL 3.2+ not supported (most likely OpenGL 2.x) return false; @@ -211,39 +180,60 @@ struct VideoGLX : Video, OpenGL { return false; } - if(glXSwapInterval) glXSwapInterval(settings.synchronize); + if(glXSwapInterval) glXSwapInterval(_blocking); //read attributes of frame buffer for later use, as requested attributes from above are not always granted - signed value = 0; - glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); - glx.doubleBuffer = value; - glx.isDirect = glXIsDirect(display, glxcontext); + int value = 0; + glXGetConfig(_display, vi, GLX_DOUBLEBUFFER, &value); + _doubleBuffer = value; + _isDirect = glXIsDirect(_display, _glXContext); - OpenGL::init(); - return true; + return _ready = OpenGL::initialize(); } - auto term() -> void { - OpenGL::term(); + auto terminate() -> void { + _ready = false; + OpenGL::terminate(); - if(glxcontext) { - glXDestroyContext(display, glxcontext); - glxcontext = nullptr; + if(_glXContext) { + glXDestroyContext(_display, _glXContext); + _glXContext = nullptr; } - if(xwindow) { - XUnmapWindow(display, xwindow); - xwindow = 0; + if(_window) { + XUnmapWindow(_display, _window); + _window = 0; } - if(colormap) { - XFreeColormap(display, colormap); - colormap = 0; + if(_colormap) { + XFreeColormap(_display, _colormap); + _colormap = 0; } - if(display) { - XCloseDisplay(display); - display = nullptr; + if(_display) { + XCloseDisplay(_display); + _display = nullptr; } } + + bool _ready = false; + uintptr _context = 0; + bool _blocking = false; + uint _depth = 24; + bool _smooth = true; + string _shader; + + auto (*glXSwapInterval)(int) -> int = nullptr; + + Display* _display = nullptr; + int _screen = 0; + Window _window = 0; + Colormap _colormap = 0; + GLXContext _glXContext = nullptr; + GLXWindow _glXWindow = 0; + + int _versionMajor = 0; + int _versionMinor = 0; + bool _doubleBuffer = false; + bool _isDirect = false; }; diff --git a/ruby/video/glx2.cpp b/ruby/video/glx2.cpp index cdd411b3..44596e19 100644 --- a/ruby/video/glx2.cpp +++ b/ruby/video/glx2.cpp @@ -51,6 +51,7 @@ struct VideoGLX2 : Video { } auto clear() -> void { + if(!ready()) return; memory::fill(_glBuffer, _glWidth * _glHeight * sizeof(uint32_t)); glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); @@ -59,15 +60,19 @@ struct VideoGLX2 : Video { } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; if(width != _width || height != _height) resize(width, height); pitch = _glWidth * sizeof(uint32_t); return data = _glBuffer; } auto unlock() -> void { + if(!ready()) return; } auto output() -> void { + if(!ready()) return; + XWindowAttributes parent, child; XGetWindowAttributes(_display, (Window)_context, &parent); XGetWindowAttributes(_display, _window, &child); diff --git a/ruby/video/opengl/main.hpp b/ruby/video/opengl/main.hpp index a5ac409f..70a66152 100644 --- a/ruby/video/opengl/main.hpp +++ b/ruby/video/opengl/main.hpp @@ -10,7 +10,7 @@ auto OpenGL::shader(const string& pathname) -> void { absoluteWidth = 0, absoluteHeight = 0; relativeWidth = 0, relativeHeight = 0; - unsigned historySize = 0; + uint historySize = 0; if(pathname) { auto document = BML::unserialize(file::read({pathname, "manifest.bml"})); @@ -38,7 +38,7 @@ auto OpenGL::shader(const string& pathname) -> void { } for(auto node : document.find("program")) { - unsigned n = programs.size(); + uint n = programs.size(); programs(n).bind(this, node, pathname); } } @@ -51,7 +51,7 @@ auto OpenGL::shader(const string& pathname) -> void { allocateHistory(historySize); } -auto OpenGL::allocateHistory(unsigned size) -> void { +auto OpenGL::allocateHistory(uint size) -> void { for(auto& frame : history) glDeleteTextures(1, &frame.texture); history.reset(); while(size--) { @@ -65,11 +65,6 @@ auto OpenGL::allocateHistory(unsigned size) -> void { } } -auto OpenGL::lock(uint32_t*& data, unsigned& pitch) -> bool { - pitch = width * sizeof(uint32_t); - return data = buffer; -} - auto OpenGL::clear() -> void { for(auto& p : programs) { glUseProgram(p.program); @@ -83,7 +78,12 @@ auto OpenGL::clear() -> void { glClear(GL_COLOR_BUFFER_BIT); } -auto OpenGL::refresh() -> void { +auto OpenGL::lock(uint32_t*& data, uint& pitch) -> bool { + pitch = width * sizeof(uint32_t); + return data = buffer; +} + +auto OpenGL::output() -> void { clear(); glActiveTexture(GL_TEXTURE0); @@ -92,15 +92,15 @@ auto OpenGL::refresh() -> void { struct Source { GLuint texture; - unsigned width, height; + uint width, height; GLuint filter, wrap; }; vector sources; sources.prepend({texture, width, height, filter, wrap}); for(auto& p : programs) { - unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth; - unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight; + uint targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth; + uint targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight; if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth; if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight; @@ -115,7 +115,7 @@ auto OpenGL::refresh() -> void { glrUniform4f("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight); glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight); - unsigned aid = 0; + uint aid = 0; for(auto& frame : history) { glrUniform1i({"history[", aid, "]"}, aid); glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height); @@ -124,7 +124,7 @@ auto OpenGL::refresh() -> void { glrParameters(frame.filter, frame.wrap); } - unsigned bid = 0; + uint bid = 0; for(auto& source : sources) { glrUniform1i({"source[", bid, "]"}, aid + bid); glrUniform4f({"sourceSize[", bid, "]"}, source.width, source.height, 1.0 / source.width, 1.0 / source.height); @@ -133,7 +133,7 @@ auto OpenGL::refresh() -> void { glrParameters(source.filter, source.wrap); } - unsigned cid = 0; + uint cid = 0; for(auto& pixmap : p.pixmaps) { glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid); glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height); @@ -151,8 +151,8 @@ auto OpenGL::refresh() -> void { sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap}); } - unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth; - unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight; + uint targetWidth = absoluteWidth ? absoluteWidth : outputWidth; + uint targetHeight = absoluteHeight ? absoluteHeight : outputHeight; if(relativeWidth) targetWidth = sources[0].width * relativeWidth; if(relativeHeight) targetHeight = sources[0].height * relativeHeight; @@ -180,7 +180,7 @@ auto OpenGL::refresh() -> void { } } -auto OpenGL::init() -> bool { +auto OpenGL::initialize() -> bool { if(!OpenGLBind()) return false; glDisable(GL_ALPHA_TEST); @@ -203,8 +203,8 @@ auto OpenGL::init() -> bool { return initialized = true; } -auto OpenGL::term() -> void { - if(initialized == false) return; +auto OpenGL::terminate() -> void { + if(!initialized) return; shader(""); //release shader resources (eg frame[] history) OpenGLSurface::release(); if(buffer) { delete[] buffer; buffer = nullptr; } diff --git a/ruby/video/opengl/opengl.hpp b/ruby/video/opengl/opengl.hpp index d917f860..8c36e583 100644 --- a/ruby/video/opengl/opengl.hpp +++ b/ruby/video/opengl/opengl.hpp @@ -27,8 +27,8 @@ struct OpenGLTexture { auto getType() const -> GLuint; GLuint texture = 0; - unsigned width = 0; - unsigned height = 0; + uint width = 0; + uint height = 0; GLuint format = GL_RGBA8; GLuint filter = GL_LINEAR; GLuint wrap = GL_CLAMP_TO_BORDER; @@ -36,9 +36,9 @@ struct OpenGLTexture { struct OpenGLSurface : OpenGLTexture { auto allocate() -> void; - auto size(unsigned width, unsigned height) -> void; + auto size(uint width, uint height) -> void; auto release() -> void; - auto render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) -> void; + auto render(uint sourceWidth, uint sourceHeight, uint targetWidth, uint targetHeight) -> void; GLuint program = 0; GLuint framebuffer = 0; @@ -55,10 +55,10 @@ struct OpenGLProgram : OpenGLSurface { auto parse(OpenGL* instance, string& source) -> void; auto release() -> void; - unsigned phase = 0; //frame counter - unsigned modulo = 0; //frame counter modulus - unsigned absoluteWidth = 0; - unsigned absoluteHeight = 0; + uint phase = 0; //frame counter + uint modulo = 0; //frame counter modulus + uint absoluteWidth = 0; + uint absoluteHeight = 0; double relativeWidth = 0; double relativeHeight = 0; vector pixmaps; @@ -66,18 +66,18 @@ struct OpenGLProgram : OpenGLSurface { struct OpenGL : OpenGLProgram { auto shader(const string& pathname) -> void; - auto allocateHistory(unsigned size) -> void; - auto lock(uint32_t*& data, unsigned& pitch) -> bool; + auto allocateHistory(uint size) -> void; auto clear() -> void; - auto refresh() -> void; - auto init() -> bool; - auto term() -> void; + auto lock(uint32_t*& data, uint& pitch) -> bool; + auto output() -> void; + auto initialize() -> bool; + auto terminate() -> void; vector programs; vector history; GLuint inputFormat = GL_RGBA8; - unsigned outputWidth = 0; - unsigned outputHeight = 0; + uint outputWidth = 0; + uint outputHeight = 0; struct Setting { string name; string value; diff --git a/ruby/video/opengl/program.hpp b/ruby/video/opengl/program.hpp index 04b727bf..b2cff3dc 100644 --- a/ruby/video/opengl/program.hpp +++ b/ruby/video/opengl/program.hpp @@ -47,7 +47,7 @@ auto OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin GLuint texture; glGenTextures(1, &texture); - unsigned n = pixmaps.size(); + uint n = pixmaps.size(); pixmaps(n).texture = texture; pixmaps(n).width = image.width(); pixmaps(n).height = image.height(); @@ -58,7 +58,7 @@ auto OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin if(leaf["filter"]) pixmaps(n).filter = glrFilter(leaf["filter"].text()); if(leaf["wrap"]) pixmaps(n).wrap = glrWrap(leaf["wrap"].text()); - unsigned w = glrSize(image.width()), h = glrSize(image.height()); + uint w = glrSize(image.width()), h = glrSize(image.height()); uint32_t* buffer = new uint32_t[w * h](); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer); diff --git a/ruby/video/opengl/surface.hpp b/ruby/video/opengl/surface.hpp index 7922c47c..62eadc43 100644 --- a/ruby/video/opengl/surface.hpp +++ b/ruby/video/opengl/surface.hpp @@ -4,7 +4,7 @@ auto OpenGLSurface::allocate() -> void { glGenBuffers(3, &vbo[0]); } -auto OpenGLSurface::size(unsigned w, unsigned h) -> void { +auto OpenGLSurface::size(uint w, uint h) -> void { if(width == w && height == h) return; width = w, height = h; w = glrSize(w), h = glrSize(h); @@ -37,7 +37,7 @@ auto OpenGLSurface::release() -> void { width = 0, height = 0; } -auto OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned targetWidth, unsigned targetHeight) -> void { +auto OpenGLSurface::render(uint sourceWidth, uint sourceHeight, uint targetWidth, uint targetHeight) -> void { glViewport(0, 0, targetWidth, targetHeight); float w = (float)sourceWidth / (float)glrSize(sourceWidth); @@ -70,7 +70,7 @@ auto OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned }; GLfloat positions[4 * 4]; - for(unsigned n = 0; n < 16; n += 4) { + for(uint n = 0; n < 16; n += 4) { Matrix::Multiply(&positions[n], &vertices[n], 1, 4, modelViewProjection, 4, 4); } diff --git a/ruby/video/opengl/utility.hpp b/ruby/video/opengl/utility.hpp index 6a632c17..656ef532 100644 --- a/ruby/video/opengl/utility.hpp +++ b/ruby/video/opengl/utility.hpp @@ -1,4 +1,4 @@ -static auto glrSize(unsigned size) -> unsigned { +static auto glrSize(uint size) -> uint { return size; //return bit::round(size); //return nearest power of two } @@ -28,7 +28,7 @@ static auto glrWrap(const string& wrap) -> GLuint { return GL_CLAMP_TO_BORDER; } -static auto glrModulo(unsigned modulo) -> unsigned { +static auto glrModulo(uint modulo) -> uint { if(modulo) return modulo; return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150 } diff --git a/ruby/video/sdl.cpp b/ruby/video/sdl.cpp index 598e3c5b..f98fef81 100644 --- a/ruby/video/sdl.cpp +++ b/ruby/video/sdl.cpp @@ -20,6 +20,7 @@ struct VideoSDL : Video { } auto clear() -> void { + if(!ready()) return; if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer); for(uint y : range(_bufferHeight)) { uint32_t* data = (uint32_t*)_buffer->pixels + y * (_buffer->pitch >> 2); @@ -30,20 +31,21 @@ struct VideoSDL : Video { } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { - if(width != _width || height != _height) { - resize(_width = width, _height = height); - } - + if(!ready()) return false; + if(width != _width || height != _height) resize(_width = width, _height = height); if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer); pitch = _buffer->pitch; return data = (uint32_t*)_buffer->pixels; } auto unlock() -> void { + if(!ready()) return; if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer); } auto output() -> void { + if(!ready()) return; + //ruby input is X8R8G8B8, top 8-bits are ignored. //as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity) //to prevent blending against the window beneath when X window visual is 32-bits. diff --git a/ruby/video/wgl.cpp b/ruby/video/wgl.cpp index a6c6ffbc..e20591e3 100644 --- a/ruby/video/wgl.cpp +++ b/ruby/video/wgl.cpp @@ -4,139 +4,129 @@ #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 struct VideoWGL : Video, OpenGL { - ~VideoWGL() { term(); } + VideoWGL() { initialize(); } + ~VideoWGL() { terminate(); } - auto (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) -> HGLRC = nullptr; - auto (APIENTRY* wglSwapInterval)(int) -> BOOL = nullptr; + auto ready() -> bool { return _ready; } - HDC display = nullptr; - HGLRC wglcontext = nullptr; - HWND window = nullptr; - HINSTANCE glwindow = nullptr; + auto context() -> uintptr { return _context; } + auto blocking() -> bool { return _blocking; } + auto smooth() -> bool { return _smooth; } + auto shader() -> string { return _shader; } - struct { - HWND handle = nullptr; - bool synchronize = false; - uint filter = Video::FilterNearest; - string shader; - } settings; - - auto cap(const string& name) -> bool { - if(name == Video::Handle) return true; - if(name == Video::Synchronize) return true; - if(name == Video::Filter) return true; - if(name == Video::Shader) return true; - return false; + auto setContext(uintptr context) -> bool { + if(_context == context) return true; + _context = context; + return initialize(); } - auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; - if(name == Video::Synchronize) return settings.synchronize; - if(name == Video::Filter) return settings.filter; - return {}; + auto setBlocking(bool blocking) -> bool { + if(_blocking == blocking) return true; + _blocking = blocking; + if(wglSwapInterval) wglSwapInterval(_blocking); + return true; } - auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (HWND)value.get(); - return true; - } + auto setSmooth(bool smooth) -> bool { + if(_smooth == smooth) return true; + _smooth = smooth; + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; + } - if(name == Video::Synchronize && value.is()) { - if(settings.synchronize != value.get()) { - settings.synchronize = value.get(); - if(wglcontext) { - init(); - OpenGL::shader(settings.shader); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - } - } - } + auto setShader(string shader) -> bool { + if(_shader == shader) return true; + OpenGL::shader(_shader = shader); + if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST; + return true; + } - if(name == Video::Filter && value.is()) { - settings.filter = value.get(); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - if(name == Video::Shader && value.is()) { - settings.shader = value.get(); - OpenGL::shader(settings.shader); - if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; - return true; - } - - return false; + auto clear() -> void { + if(!ready()) return; + OpenGL::clear(); + SwapBuffers(_display); } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; OpenGL::size(width, height); return OpenGL::lock(data, pitch); } auto unlock() -> void { + if(!ready()) return; } - auto clear() -> void { - OpenGL::clear(); - SwapBuffers(display); + auto output() -> void { + if(!ready()) return; + RECT rectangle; + GetClientRect((HWND)_context, &rectangle); + OpenGL::outputWidth = rectangle.right - rectangle.left; + OpenGL::outputHeight = rectangle.bottom - rectangle.top; + OpenGL::output(); + SwapBuffers(_display); } - auto refresh() -> void { - RECT rc; - GetClientRect(settings.handle, &rc); - outputWidth = rc.right - rc.left, outputHeight = rc.bottom - rc.top; - OpenGL::refresh(); - SwapBuffers(display); - } +private: + auto initialize() -> bool { + terminate(); + if(!_context) return false; - auto init() -> bool { - GLuint pixel_format; - PIXELFORMATDESCRIPTOR pfd; - memory::fill(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); - pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; + PIXELFORMATDESCRIPTOR descriptor = {}; + descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR); + descriptor.nVersion = 1; + descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + descriptor.iPixelType = PFD_TYPE_RGBA; - display = GetDC(settings.handle); - pixel_format = ChoosePixelFormat(display, &pfd); - SetPixelFormat(display, pixel_format, &pfd); + _display = GetDC((HWND)_context); + GLuint pixelFormat = ChoosePixelFormat(_display, &descriptor); + SetPixelFormat(_display, pixelFormat, &descriptor); - wglcontext = wglCreateContext(display); - wglMakeCurrent(display, wglcontext); + _wglContext = wglCreateContext(_display); + wglMakeCurrent(_display, _wglContext); wglCreateContextAttribs = (HGLRC (APIENTRY*)(HDC, HGLRC, const int*))glGetProcAddress("wglCreateContextAttribsARB"); wglSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT"); if(wglCreateContextAttribs) { - int attributes[] = { + int attributeList[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, 0 }; - HGLRC context = wglCreateContextAttribs(display, 0, attributes); + HGLRC context = wglCreateContextAttribs(_display, 0, attributeList); if(context) { wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(wglcontext); - wglMakeCurrent(display, wglcontext = context); + wglDeleteContext(_wglContext); + wglMakeCurrent(_display, _wglContext = context); } } - if(wglSwapInterval) { - wglSwapInterval(settings.synchronize); - } - - OpenGL::init(); - return true; + if(wglSwapInterval) wglSwapInterval(_blocking); + return _ready = OpenGL::initialize(); } - auto term() -> void { - OpenGL::term(); + auto terminate() -> void { + _ready = false; + OpenGL::terminate(); - if(wglcontext) { - wglDeleteContext(wglcontext); - wglcontext = nullptr; + if(_wglContext) { + wglDeleteContext(_wglContext); + _wglContext = nullptr; } } + + auto (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) -> HGLRC = nullptr; + auto (APIENTRY* wglSwapInterval)(int) -> BOOL = nullptr; + + bool _ready = false; + uintptr _context = 0; + bool _blocking = false; + bool _smooth = true; + string _shader; + + HDC _display = nullptr; + HGLRC _wglContext = nullptr; + HWND _window = nullptr; + HINSTANCE _glWindow = nullptr; }; diff --git a/ruby/video/xshm.cpp b/ruby/video/xshm.cpp index 79fc2a95..1d076262 100644 --- a/ruby/video/xshm.cpp +++ b/ruby/video/xshm.cpp @@ -29,7 +29,7 @@ struct VideoXShm : Video { } auto clear() -> void { - if(!_ready) return; + if(!ready()) return; auto dp = _inputBuffer; uint length = _inputWidth * _inputHeight; while(length--) *dp++ = 255u << 24; @@ -37,6 +37,7 @@ struct VideoXShm : Video { } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { + if(!ready()) return false; if(!_inputBuffer || _inputWidth != width || _inputHeight != height) { if(_inputBuffer) delete[] _inputBuffer; _inputWidth = width; @@ -50,10 +51,11 @@ struct VideoXShm : Video { } auto unlock() -> void { + if(!ready()) return; } auto output() -> void { - if(!_ready) return; + if(!ready()) return; size(); float xratio = (float)_inputWidth / (float)_outputWidth; diff --git a/ruby/video/xvideo.cpp b/ruby/video/xvideo.cpp index f8c79a1b..cfe78527 100644 --- a/ruby/video/xvideo.cpp +++ b/ruby/video/xvideo.cpp @@ -37,6 +37,7 @@ struct VideoXVideo : Video { } auto clear() -> void { + if(!ready()) return; memory::fill(_buffer, _bufferWidth * _bufferHeight * sizeof(uint32_t)); //clear twice in case video is double buffered ... output(); @@ -44,21 +45,18 @@ struct VideoXVideo : Video { } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { - if(!_ready) return false; - - if(width != _width || height != _height) { - resize(_width = width, _height = height); - } - + if(!ready()) return false; + if(width != _width || height != _height) resize(_width = width, _height = height); pitch = _bufferWidth * 4; return data = _buffer; } auto unlock() -> void { + if(!ready()) return; } auto output() -> void { - if(!_ready) return; + if(!ready()) return; XWindowAttributes target; XGetWindowAttributes(_display, _window, &target); @@ -93,6 +91,7 @@ struct VideoXVideo : Video { true); } +private: auto initialize() -> bool { terminate(); if(!_context) return false;