From d5c09c9ab1918b6a2f63c86aba2b1ee48569b754 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 24 Jul 2017 15:23:40 +1000 Subject: [PATCH] Update to v103r20 release. byuu says: Changelog: - ruby/audio/xaudio2: ported to new ruby API - ruby/video/cgl: ported to new ruby API (untested, won't compile) - ruby/video/directdraw: ported to new ruby API - ruby/video/gdi: ported to new ruby API - ruby/video/glx: ported to new ruby API - ruby/video/wgl: ported to new ruby API - ruby/video/opengl: code cleanups The macOS CGL driver is sure to have compilation errors. If someone will post the compilation error log, I can hopefully fix it in one or two iterations of WIPs. I am unable to test the Xorg GLX driver, because my FreeBSD desktop video card drivers do not support OpenGL 3.2. If the driver doesn't work, I'm going to need help tracking down what broke from the older releases. The real fun is still yet to come ... all the Linux-only drivers, where I don't have a single Linux machine to test with. Todo: - libco/fiber - libco/ucontext (I should really just delete this) - tomoko: hide main UI window when in exclusive fullscreen mode --- higan/emulator/emulator.hpp | 2 +- higan/target-tomoko/GNUmakefile | 10 +- higan/target-tomoko/settings/settings.cpp | 2 +- icarus/ui/settings-dialog.cpp | 2 +- libco/amd64.c | 6 - libco/arm.c | 6 - libco/fiber.c | 6 - libco/libco.c | 5 - libco/ppc.c | 6 - libco/sjlj.c | 7 +- libco/ucontext.c | 6 - libco/x86.c | 6 - ruby/audio/xaudio2.cpp | 323 +++++++++++----------- ruby/video/cgl.cpp | 150 +++++----- ruby/video/direct3d.cpp | 4 + ruby/video/directdraw.cpp | 314 +++++++++++---------- ruby/video/gdi.cpp | 147 +++++----- ruby/video/glx.cpp | 280 +++++++++---------- ruby/video/glx2.cpp | 5 + ruby/video/opengl/main.hpp | 40 +-- ruby/video/opengl/opengl.hpp | 30 +- ruby/video/opengl/program.hpp | 4 +- ruby/video/opengl/surface.hpp | 6 +- ruby/video/opengl/utility.hpp | 4 +- ruby/video/sdl.cpp | 10 +- ruby/video/wgl.cpp | 174 ++++++------ ruby/video/xshm.cpp | 6 +- ruby/video/xvideo.cpp | 13 +- 28 files changed, 744 insertions(+), 830 deletions(-) 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;