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
This commit is contained in:
Tim Allen 2017-07-24 15:23:40 +10:00
parent 8be474b0ac
commit d5c09c9ab1
28 changed files with 744 additions and 830 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -18,19 +18,19 @@ ui_objects += $(if $(call streq,$(platform),windows),ui-resource)
# platform # platform
ifeq ($(platform),windows) ifeq ($(platform),windows)
ruby += video.direct3d #video.wgl video.directdraw video.gdi ruby += video.wgl video.direct3d video.directdraw video.gdi
ruby += audio.asio audio.wasapi #audio.xaudio2 audio.directsound ruby += audio.asio audio.wasapi audio.xaudio2 #audio.directsound
ruby += input.windows ruby += input.windows
else ifeq ($(platform),macosx) else ifeq ($(platform),macosx)
ruby += #video.cgl ruby += video.cgl
ruby += audio.openal ruby += audio.openal
ruby += #input.quartz input.carbon ruby += #input.quartz input.carbon
else ifeq ($(platform),linux) 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 += audio.oss audio.openal #audio.alsa audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib #input.udev ruby += input.sdl input.xlib #input.udev
else ifeq ($(platform),bsd) 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 += audio.oss audio.openal
ruby += input.sdl input.xlib ruby += input.sdl input.xlib
endif endif

View File

@ -12,7 +12,7 @@ SettingsManager::SettingsManager() {
layout.setMargin(5); layout.setMargin(5);
statusBar.setFont(Font().setBold()); statusBar.setFont(Font().setBold());
setTitle("Configuration Settings"); setTitle("Settings");
setSize({600, 405}); setSize({600, 405});
setAlignment({0.0, 1.0}); setAlignment({0.0, 1.0});
setDismissable(); setDismissable();

View File

@ -19,6 +19,6 @@ SettingsDialog::SettingsDialog() {
settings["icarus/UseDatabase"].setValue(useDatabaseOption.checked()); settings["icarus/UseDatabase"].setValue(useDatabaseOption.checked());
}); });
setTitle("icarus Settings"); setTitle("Settings");
setSize({480, layout.minimumSize().height()}); setSize({480, layout.minimumSize().height()});
} }

View File

@ -1,9 +1,3 @@
/*
libco.amd64 (2016-09-14)
author: byuu
license: public domain
*/
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h" #include "settings.h"

View File

@ -1,9 +1,3 @@
/*
libco.arm (2016-09-14)
author: byuu
license: public domain
*/
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h" #include "settings.h"

View File

@ -1,9 +1,3 @@
/*
libco.win (2008-01-28)
authors: Nach, byuu
license: public domain
*/
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"

View File

@ -1,8 +1,3 @@
/*
libco
license: public domain
*/
#if defined(__clang__) #if defined(__clang__)
#pragma clang diagnostic ignored "-Wparentheses" #pragma clang diagnostic ignored "-Wparentheses"
#endif #endif

View File

@ -1,9 +1,3 @@
/*
libco.ppc (2016-09-14)
author: blargg
license: public domain
*/
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h" #include "settings.h"

View File

@ -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. 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. 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 #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h"
#define _XOPEN_SOURCE 500 #define _XOPEN_SOURCE 500
#include <stdlib.h> #include <stdlib.h>

View File

@ -1,9 +1,3 @@
/*
libco.ucontext (2008-01-28)
author: Nach
license: public domain
*/
/* /*
WARNING: the overhead of POSIX ucontext is very high, WARNING: the overhead of POSIX ucontext is very high,
assembly versions of libco or libco_sjlj should be much faster assembly versions of libco or libco_sjlj should be much faster

View File

@ -1,9 +1,3 @@
/*
libco.x86 (2016-09-14)
author: byuu
license: public domain
*/
#define LIBCO_C #define LIBCO_C
#include "libco.h" #include "libco.h"
#include "settings.h" #include "settings.h"

View File

@ -2,11 +2,163 @@
#include <windows.h> #include <windows.h>
struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback {
~AudioXAudio2() { term(); } AudioXAudio2() { initialize(); }
~AudioXAudio2() { terminate(); }
IXAudio2* pXAudio2 = nullptr; auto ready() -> bool { return _ready; }
IXAudio2MasteringVoice* pMasterVoice = nullptr;
IXAudio2SourceVoice* pSourceVoice = nullptr; 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<BYTE*>(_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 //inherited from IXAudio2VoiceCallback
STDMETHODIMP_(void) OnBufferStart(void* pBufferContext){} STDMETHODIMP_(void) OnBufferStart(void* pBufferContext){}
@ -16,168 +168,7 @@ struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback {
STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {} STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {}
STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {} 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<bool>()) {
settings.synchronize = value.get<bool>();
if(pXAudio2) clear();
return true;
}
if(name == Audio::Frequency && value.is<unsigned>()) {
settings.frequency = value.get<unsigned>();
if(pXAudio2) init();
return true;
}
if(name == Audio::Latency && value.is<unsigned>()) {
settings.latency = value.get<unsigned>();
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<BYTE*>(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) { STDMETHODIMP_(void) OnBufferEnd(void* pBufferContext) {
InterlockedDecrement(&device.submitbuffers); InterlockedDecrement(&_bufferQueue);
} }
}; };

View File

@ -12,81 +12,50 @@ struct VideoCGL;
@end @end
struct VideoCGL : Video, OpenGL { struct VideoCGL : Video, OpenGL {
~VideoCGL() { term(); } VideoCGL() { initialize(); }
~VideoCGL() { terminate(); }
RubyVideoCGL* view = nullptr; auto ready() -> bool { return _ready; }
struct { auto context() -> uintptr { return (uintptr)_context; }
NSView* handle = nullptr; auto blocking() -> bool { return _blocking; }
bool synchronize = false; auto smooth() -> bool { return _smooth; }
uint filter = Video::FilterNearest; auto shader() -> string { return _shader; }
string shader;
} settings;
auto cap(const string& name) -> bool { auto setContext(uintptr context) -> bool {
if(name == Video::Handle) return true; if(_context == (NSView*)context) return true;
if(name == Video::Synchronize) return true; _context = (NSView*)context;
if(name == Video::Filter) return true; return initialize();
if(name == Video::Shader) return true;
return false;
} }
auto get(const string& name) -> any { auto setBlocking(bool blocking) -> bool {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(_blocking == blocking) return true;
if(name == Video::Synchronize) return settings.synchronize; _blocking = blocking;
if(name == Video::Filter) return settings.filter; if(!view) return true;
return {}; @autoreleasepool {
[[view openGLContext] makeCurrentContext];
int blocking = _blocking;
[[view openGLContext] setValues:&blocking forParameter:NSOpenGLCPSwapInterval];
}
return true;
} }
auto set(const string& name, const any& value) -> bool { auto setSmooth(bool smooth) -> bool {
if(name == Video::Handle && value.is<uintptr_t>()) { if(_smooth == smooth) return true;
settings.handle = (NSView*)value.get<uintptr_t>(); _smooth = smooth;
return true; if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
} return true;
if(name == Video::Synchronize && value.is<bool>()) {
if(settings.synchronize != value.get<bool>()) {
settings.synchronize = value.get<bool>();
if(view) {
@autoreleasepool {
[[view openGLContext] makeCurrentContext];
int synchronize = settings.synchronize;
[[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval];
}
}
}
return true;
}
if(name == Video::Filter && value.is<uint>()) {
settings.filter = value.get<uint>();
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
if(name == Video::Shader && value.is<string>()) {
settings.shader = value.get<string>();
@autoreleasepool {
[[view openGLContext] makeCurrentContext];
}
OpenGL::shader(settings.shader);
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
return false;
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto setShader(string shader) -> string {
OpenGL::size(width, height); if(_shader == shader) return true;
return OpenGL::lock(data, pitch); OpenGL::shader(_shader = shader);
} if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
return true;
auto unlock() -> void {
} }
auto clear() -> void { auto clear() -> void {
if(!ready()) return;
@autoreleasepool { @autoreleasepool {
[view lockFocus]; [view lockFocus];
OpenGL::clear(); 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 { @autoreleasepool {
if([view lockFocusIfCanDraw]) { if([view lockFocusIfCanDraw]) {
auto area = [view frame]; auto area = [view frame];
outputWidth = area.size.width; OpenGL::outputWidth = area.size.width;
outputHeight = area.size.height; OpenGL::outputHeight = area.size.height;
OpenGL::refresh(); OpenGL::output();
[[view openGLContext] flushBuffer]; [[view openGLContext] flushBuffer];
[view unlockFocus]; [view unlockFocus];
} }
} }
} }
auto init() -> bool { private:
auto initialize() -> bool {
terminate();
if(!_context) return false;
@autoreleasepool { @autoreleasepool {
NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPixelFormatAttribute attributeList[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAColorSize, 24, NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8, NSOpenGLPFAAlphaSize, 8,
@ -118,41 +102,49 @@ struct VideoCGL : Video, OpenGL {
0 0
}; };
auto size = [settings.handle frame].size; auto size = [_context frame].size;
auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes] autorelease]; auto format = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attributeList] autorelease];
auto context = [[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease]; auto context = [[[NSOpenGLContext alloc] initWithFormat:format shareContext:nil] autorelease];
view = [[RubyVideoCGL alloc] initWith:this pixelFormat:format]; view = [[RubyVideoCGL alloc] initWith:this pixelFormat:format];
[view setOpenGLContext:context]; [view setOpenGLContext:context];
[view setFrame:NSMakeRect(0, 0, size.width, size.height)]; [view setFrame:NSMakeRect(0, 0, size.width, size.height)];
[view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[settings.handle addSubview:view]; [_context addSubview:view];
[context setView:view]; [context setView:view];
[view lockFocus]; [view lockFocus];
OpenGL::init(); OpenGL::initialize();
//print((const char*)glGetString(GL_VERSION), "\n");
int synchronize = settings.synchronize; int blocking = _blocking;
[[view openGLContext] setValues:&synchronize forParameter:NSOpenGLCPSwapInterval]; [[view openGLContext] setValues:&blocking forParameter:NSOpenGLCPSwapInterval];
[view unlockFocus]; [view unlockFocus];
} }
clear(); clear();
return true; return _ready = true;
} }
auto term() -> void { auto term() -> void {
OpenGL::term(); _ready = false;
OpenGL::terminate();
if(!view) return;
@autoreleasepool { @autoreleasepool {
[view removeFromSuperview]; [view removeFromSuperview];
[view release]; [view release];
view = nil; view = nil;
} }
} }
RubyVideoCGL* view = nullptr;
NSView* _context = nullptr;
bool _blocking = false;
bool _smooth = true;
string _shader;
}; };
@implementation RubyVideoCGL : NSOpenGLView @implementation RubyVideoCGL : NSOpenGLView
@ -165,7 +157,7 @@ struct VideoCGL : Video, OpenGL {
} }
-(void) reshape { -(void) reshape {
video->refresh(); video->output();
} }
@end @end

View File

@ -42,6 +42,7 @@ struct VideoDirect3D : Video {
} }
auto clear() -> void { auto clear() -> void {
if(!ready()) return;
if(_lost && !recover()) return; if(_lost && !recover()) return;
D3DSURFACE_DESC surfaceDescription; D3DSURFACE_DESC surfaceDescription;
@ -62,6 +63,7 @@ struct VideoDirect3D : Video {
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!ready()) return false;
if(_lost && !recover()) return false; if(_lost && !recover()) return false;
if(width != _inputWidth || height != _inputHeight) { if(width != _inputWidth || height != _inputHeight) {
@ -79,12 +81,14 @@ struct VideoDirect3D : Video {
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
_surface->UnlockRect(); _surface->UnlockRect();
_surface->Release(); _surface->Release();
_surface = nullptr; _surface = nullptr;
} }
auto output() -> void { auto output() -> void {
if(!ready()) return;
if(_lost && !recover()) return; if(_lost && !recover()) return;
RECT rectangle; RECT rectangle;

View File

@ -1,173 +1,167 @@
#include <ddraw.h> #include <ddraw.h>
#undef interface
struct VideoDirectDraw : Video { struct VideoDirectDraw : Video {
~VideoDirectDraw() { term(); } VideoDirectDraw() { initialize(); }
~VideoDirectDraw() { terminate(); }
LPDIRECTDRAW lpdd = nullptr; auto ready() -> bool { return _ready; }
LPDIRECTDRAW7 lpdd7 = nullptr;
LPDIRECTDRAWSURFACE7 screen = nullptr;
LPDIRECTDRAWSURFACE7 raster = nullptr;
LPDIRECTDRAWCLIPPER clipper = nullptr;
DDSURFACEDESC2 ddsd;
DDSCAPS2 ddscaps;
uint iwidth;
uint iheight;
struct { auto context() -> uintptr { return _context; }
HWND handle = nullptr; auto blocking() -> bool { return _blocking; }
bool synchronize = false;
uint width; auto setContext(uintptr context) -> bool {
uint height; if(_context == context) return true;
} settings; _context = context;
return initialize();
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true;
if(name == Video::Synchronize) return true;
return false;
} }
auto get(const string& name) -> any { auto setBlocking(bool blocking) -> bool {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(_blocking == blocking) return true;
if(name == Video::Synchronize) return settings.synchronize; _blocking = blocking;
return {};
}
auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle && value.is<uintptr>()) {
settings.handle = (HWND)value.get<uintptr>();
return true;
}
if(name == Video::Synchronize && value.is<bool>()) {
settings.synchronize = value.get<bool>();
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);
return true; return true;
} }
auto term() -> void { auto clear() -> void {
if(clipper) { clipper->Release(); clipper = 0; } if(!ready()) return;
if(raster) { raster->Release(); raster = 0; } DDBLTFX fx = {};
if(screen) { screen->Release(); screen = 0; } fx.dwSize = sizeof(DDBLTFX);
if(lpdd7) { lpdd7->Release(); lpdd7 = 0; } fx.dwFillColor = 0x00000000;
if(lpdd) { lpdd->Release(); lpdd = 0; } _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;
}; };

View File

@ -1,103 +1,94 @@
#include <assert.h>
struct VideoGDI : Video { struct VideoGDI : Video {
~VideoGDI() { term(); } VideoGDI() { initialize(); }
~VideoGDI() { terminate(); }
struct Device { auto ready() -> bool { return _ready; }
HBITMAP bitmap = nullptr;
HDC dc = nullptr;
BITMAPINFO info = {};
} device;
struct Settings { auto context() -> uintptr { return _context; }
HWND handle = nullptr;
uint32_t* buffer = nullptr; auto setContext(uintptr context) -> bool {
uint width = 0; if(_context == context) return true;
uint height = 0; _context = context;
} settings; return initialize();
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true;
return false;
} }
auto get(const string& name) -> any { auto clear() -> void {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(!ready()) return;
return {};
}
auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle && value.is<uintptr>()) {
settings.handle = (HWND)value.get<uintptr>();
return true;
}
return false;
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!settings.buffer || settings.width != width || settings.height != height) { if(!ready()) return false;
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;
HDC hdc = GetDC(settings.handle); if(!_buffer || _width != width || _height != height) {
device.dc = CreateCompatibleDC(hdc); if(_buffer) delete[] _buffer;
assert(device.dc); if(_bitmap) DeleteObject(_bitmap);
device.bitmap = CreateCompatibleBitmap(hdc, width, height); if(_dc) DeleteObject(_dc);
assert(device.bitmap);
SelectObject(device.dc, device.bitmap);
ReleaseDC(settings.handle, hdc);
memory::fill(&device.info, sizeof(BITMAPINFO)); _buffer = new uint32_t[width * height]();
device.info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); _width = width;
device.info.bmiHeader.biWidth = width; _height = height;
device.info.bmiHeader.biHeight = -height;
device.info.bmiHeader.biPlanes = 1; HDC hdc = GetDC((HWND)_context);
device.info.bmiHeader.biBitCount = 32; _dc = CreateCompatibleDC(hdc);
device.info.bmiHeader.biCompression = BI_RGB; _bitmap = CreateCompatibleBitmap(hdc, width, height);
device.info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t); 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 = _width * sizeof(uint32_t);
pitch = settings.width * sizeof(uint32_t); return data = _buffer;
return true;
} }
auto unlock() -> void {} auto unlock() -> void {
if(!ready()) return;
}
auto clear() -> void {} auto output() -> void {
if(!ready()) return;
auto refresh() -> void {
RECT rc; 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); SetDIBits(_dc, _bitmap, 0, _height, (void*)_buffer, &_info, DIB_RGB_COLORS);
HDC hdc = GetDC(settings.handle); HDC hdc = GetDC((HWND)_context);
StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, device.dc, 0, 0, settings.width, settings.height, SRCCOPY); StretchBlt(hdc, rc.left, rc.top, rc.right, rc.bottom, _dc, 0, 0, _width, _height, SRCCOPY);
ReleaseDC(settings.handle, hdc); ReleaseDC((HWND)_context, hdc);
} }
auto init() -> bool { private:
settings.width = 0; auto initialize() -> bool {
settings.height = 0; terminate();
return true; if(!_context) return false;
_width = 0;
_height = 0;
return _ready = true;
} }
auto term() -> void { auto terminate() -> void {
if(settings.buffer) { _ready = false;
delete[] settings.buffer; if(_buffer) { delete[] _buffer; _buffer = nullptr; }
DeleteObject(device.bitmap); if(_bitmap) { DeleteObject(_bitmap); _bitmap = nullptr; }
DeleteDC(device.dc); if(_dc) { DeleteDC(_dc); _dc = nullptr; }
settings.buffer = nullptr;
device.bitmap = nullptr;
device.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 = {};
}; };

View File

@ -8,185 +8,154 @@ auto VideoGLX_X11ErrorHandler(Display*, XErrorEvent*) -> int {
} }
struct VideoGLX : Video, OpenGL { struct VideoGLX : Video, OpenGL {
~VideoGLX() { term(); } VideoGLX() { initialize(); }
~VideoGLX() { terminate(); }
auto (*glXSwapInterval)(signed) -> signed = nullptr; auto ready() -> bool { return _ready; }
Display* display = nullptr; auto context() -> uintptr { return _context; }
signed screen = 0; auto blocking() -> bool { return _blocking; }
Window xwindow = 0; auto depth() -> uint { return _depth; }
Colormap colormap = 0; auto smooth() -> bool { return _smooth; }
GLXContext glxcontext = nullptr; auto shader() -> string { return _shader; }
GLXWindow glxwindow = 0;
struct { auto setContext(uintptr context) -> bool {
signed versionMajor = 0; if(_context == context) return true;
signed versionMinor = 0; _context = context;
bool doubleBuffer = false; return initialize();
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 get(const string& name) -> any { auto setBlocking(bool blocking) -> bool {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(_blocking == blocking) return true;
if(name == Video::Synchronize) return settings.synchronize; _blocking = blocking;
if(name == Video::Depth) return settings.depth; if(glXSwapInterval) glXSwapInterval(_blocking);
if(name == Video::Filter) return settings.filter; return true;
if(name == Video::Shader) return settings.shader;
return {};
} }
auto set(const string& name, const any& value) -> bool { auto setDepth(uint depth) -> bool {
if(name == Video::Handle && value.is<uintptr_t>()) { if(_depth == depth) return true;
settings.handle = value.get<uintptr_t>(); switch(depth) {
return true; 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<bool>()) {
if(settings.synchronize != value.get<bool>()) {
settings.synchronize = value.get<bool>();
if(glXSwapInterval) glXSwapInterval(settings.synchronize);
return true;
}
}
if(name == Video::Depth && value.is<unsigned>()) {
unsigned depth = value.get<unsigned>();
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<unsigned>()) {
settings.filter = value.get<unsigned>();
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
if(name == Video::Shader && value.is<string>()) {
settings.shader = value.get<string>();
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); OpenGL::size(width, height);
return OpenGL::lock(data, pitch); return OpenGL::lock(data, pitch);
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
} }
auto clear() -> void { auto output() -> void {
OpenGL::clear(); if(!ready()) return;
if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow);
}
auto refresh() -> void {
//we must ensure that the child window is the same size as the parent window. //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, //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. //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. //therefore, inelegant as it may be, we query each window size and resize as needed.
XWindowAttributes parent, child; XWindowAttributes parent, child;
XGetWindowAttributes(display, settings.handle, &parent); XGetWindowAttributes(_display, (Window)_context, &parent);
XGetWindowAttributes(display, xwindow, &child); XGetWindowAttributes(_display, (Window)_window, &child);
if(child.width != parent.width || child.height != parent.height) { 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::outputWidth = parent.width;
OpenGL::refresh(); OpenGL::outputHeight = parent.height;
if(glx.doubleBuffer) glXSwapBuffers(display, glxwindow); OpenGL::output();
if(_doubleBuffer) glXSwapBuffers(_display, _glXWindow);
} }
auto init() -> bool { private:
display = XOpenDisplay(0); auto initialize() -> bool {
screen = DefaultScreen(display); terminate();
if(!_context) return false;
_display = XOpenDisplay(nullptr);
_screen = DefaultScreen(_display);
//require GLX 1.2+ API //require GLX 1.2+ API
glXQueryVersion(display, &glx.versionMajor, &glx.versionMinor); glXQueryVersion(_display, &_versionMajor, &_versionMinor);
if(glx.versionMajor < 1 || (glx.versionMajor == 1 && glx.versionMinor < 2)) return false; if(_versionMajor < 1 || (_versionMajor == 1 && _versionMinor < 2)) return false;
XWindowAttributes windowAttributes; 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 //let GLX determine the best Visual to use for GL output; provide a few hints
//note: some video drivers will override double buffering attribute //note: some video drivers will override double buffering attribute
signed attributeList[] = { int attributeList[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True, GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, (signed)(settings.depth / 3), GLX_RED_SIZE, (int)(_depth / 3),
GLX_GREEN_SIZE, (signed)(settings.depth / 3) + (signed)(settings.depth % 3), GLX_GREEN_SIZE, (int)(_depth / 3) + (int)(_depth % 3),
GLX_BLUE_SIZE, (signed)(settings.depth / 3), GLX_BLUE_SIZE, (int)(_depth / 3),
None None
}; };
signed fbCount = 0; int fbCount = 0;
GLXFBConfig* fbConfig = glXChooseFBConfig(display, screen, attributeList, &fbCount); GLXFBConfig* fbConfig = glXChooseFBConfig(_display, _screen, attributeList, &fbCount);
if(fbCount == 0) return false; 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. //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. //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. //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); _colormap = XCreateColormap(_display, RootWindow(_display, vi->screen), vi->visual, AllocNone);
XSetWindowAttributes attributes; XSetWindowAttributes attributes;
attributes.colormap = colormap; attributes.colormap = _colormap;
attributes.border_pixel = 0; attributes.border_pixel = 0;
xwindow = XCreateWindow(display, /* parent = */ settings.handle, _window = XCreateWindow(_display, /* parent = */ (Window)_context,
/* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height, /* x = */ 0, /* y = */ 0, windowAttributes.width, windowAttributes.height,
/* border_width = */ 0, vi->depth, InputOutput, vi->visual, /* border_width = */ 0, vi->depth, InputOutput, vi->visual,
CWColormap | CWBorderPixel, &attributes); CWColormap | CWBorderPixel, &attributes);
XSetWindowBackground(display, xwindow, /* color = */ 0); XSetWindowBackground(_display, _window, /* color = */ 0);
XMapWindow(display, xwindow); XMapWindow(_display, _window);
XFlush(display); XFlush(_display);
//window must be realized (appear onscreen) before we make the context current //window must be realized (appear onscreen) before we make the context current
while(XPending(display)) { while(XPending(_display)) {
XEvent event; XEvent event;
XNextEvent(display, &event); XNextEvent(_display, &event);
} }
glxcontext = glXCreateContext(display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE); _glXContext = glXCreateContext(_display, vi, /* sharelist = */ 0, /* direct = */ GL_TRUE);
glXMakeCurrent(display, glxwindow = xwindow, glxcontext); glXMakeCurrent(_display, _glXWindow = _window, _glXContext);
//glXSwapInterval is used to toggle Vsync //glXSwapInterval is used to toggle Vsync
//note that the ordering is very important! MESA declares SGI, but the SGI function does nothing //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 = (int (*)(int))glGetProcAddress("glXSwapIntervalMESA");
if(!glXSwapInterval) glXSwapInterval = (signed (*)(signed))glGetProcAddress("glXSwapIntervalSGI"); if(!glXSwapInterval) glXSwapInterval = (int (*)(int))glGetProcAddress("glXSwapIntervalSGI");
if(auto glXCreateContextAttribs = (auto (*)(Display*, GLXFBConfig, GLXContext, signed, const signed*) -> GLXContext)glGetProcAddress("glXCreateContextAttribsARB")) { if(auto glXCreateContextAttribs = (auto (*)(Display*, GLXFBConfig, GLXContext, int, const int*) -> GLXContext)glGetProcAddress("glXCreateContextAttribsARB")) {
signed attributes[] = { int attributes[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 2, GLX_CONTEXT_MINOR_VERSION_ARB, 2,
None None
@ -194,14 +163,14 @@ struct VideoGLX : Video, OpenGL {
//glXCreateContextAttribs tends to throw BadRequest errors instead of simply failing gracefully //glXCreateContextAttribs tends to throw BadRequest errors instead of simply failing gracefully
auto originalHandler = XSetErrorHandler(VideoGLX_X11ErrorHandler); auto originalHandler = XSetErrorHandler(VideoGLX_X11ErrorHandler);
auto context = glXCreateContextAttribs(display, fbConfig[0], nullptr, true, attributes); auto context = glXCreateContextAttribs(_display, fbConfig[0], nullptr, true, attributes);
XSync(display, False); XSync(_display, False);
XSetErrorHandler(originalHandler); XSetErrorHandler(originalHandler);
if(context) { if(context) {
glXMakeCurrent(display, 0, nullptr); glXMakeCurrent(_display, 0, nullptr);
glXDestroyContext(display, glxcontext); glXDestroyContext(_display, _glXContext);
glXMakeCurrent(display, glxwindow, glxcontext = context); glXMakeCurrent(_display, _glXWindow, _glXContext = context);
} else { } else {
//OpenGL 3.2+ not supported (most likely OpenGL 2.x) //OpenGL 3.2+ not supported (most likely OpenGL 2.x)
return false; return false;
@ -211,39 +180,60 @@ struct VideoGLX : Video, OpenGL {
return false; 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 //read attributes of frame buffer for later use, as requested attributes from above are not always granted
signed value = 0; int value = 0;
glXGetConfig(display, vi, GLX_DOUBLEBUFFER, &value); glXGetConfig(_display, vi, GLX_DOUBLEBUFFER, &value);
glx.doubleBuffer = value; _doubleBuffer = value;
glx.isDirect = glXIsDirect(display, glxcontext); _isDirect = glXIsDirect(_display, _glXContext);
OpenGL::init(); return _ready = OpenGL::initialize();
return true;
} }
auto term() -> void { auto terminate() -> void {
OpenGL::term(); _ready = false;
OpenGL::terminate();
if(glxcontext) { if(_glXContext) {
glXDestroyContext(display, glxcontext); glXDestroyContext(_display, _glXContext);
glxcontext = nullptr; _glXContext = nullptr;
} }
if(xwindow) { if(_window) {
XUnmapWindow(display, xwindow); XUnmapWindow(_display, _window);
xwindow = 0; _window = 0;
} }
if(colormap) { if(_colormap) {
XFreeColormap(display, colormap); XFreeColormap(_display, _colormap);
colormap = 0; _colormap = 0;
} }
if(display) { if(_display) {
XCloseDisplay(display); XCloseDisplay(_display);
display = nullptr; _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;
}; };

View File

@ -51,6 +51,7 @@ struct VideoGLX2 : Video {
} }
auto clear() -> void { auto clear() -> void {
if(!ready()) return;
memory::fill(_glBuffer, _glWidth * _glHeight * sizeof(uint32_t)); memory::fill(_glBuffer, _glWidth * _glHeight * sizeof(uint32_t));
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -59,15 +60,19 @@ struct VideoGLX2 : Video {
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!ready()) return false;
if(width != _width || height != _height) resize(width, height); if(width != _width || height != _height) resize(width, height);
pitch = _glWidth * sizeof(uint32_t); pitch = _glWidth * sizeof(uint32_t);
return data = _glBuffer; return data = _glBuffer;
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
} }
auto output() -> void { auto output() -> void {
if(!ready()) return;
XWindowAttributes parent, child; XWindowAttributes parent, child;
XGetWindowAttributes(_display, (Window)_context, &parent); XGetWindowAttributes(_display, (Window)_context, &parent);
XGetWindowAttributes(_display, _window, &child); XGetWindowAttributes(_display, _window, &child);

View File

@ -10,7 +10,7 @@ auto OpenGL::shader(const string& pathname) -> void {
absoluteWidth = 0, absoluteHeight = 0; absoluteWidth = 0, absoluteHeight = 0;
relativeWidth = 0, relativeHeight = 0; relativeWidth = 0, relativeHeight = 0;
unsigned historySize = 0; uint historySize = 0;
if(pathname) { if(pathname) {
auto document = BML::unserialize(file::read({pathname, "manifest.bml"})); 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")) { for(auto node : document.find("program")) {
unsigned n = programs.size(); uint n = programs.size();
programs(n).bind(this, node, pathname); programs(n).bind(this, node, pathname);
} }
} }
@ -51,7 +51,7 @@ auto OpenGL::shader(const string& pathname) -> void {
allocateHistory(historySize); allocateHistory(historySize);
} }
auto OpenGL::allocateHistory(unsigned size) -> void { auto OpenGL::allocateHistory(uint size) -> void {
for(auto& frame : history) glDeleteTextures(1, &frame.texture); for(auto& frame : history) glDeleteTextures(1, &frame.texture);
history.reset(); history.reset();
while(size--) { 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 { auto OpenGL::clear() -> void {
for(auto& p : programs) { for(auto& p : programs) {
glUseProgram(p.program); glUseProgram(p.program);
@ -83,7 +78,12 @@ auto OpenGL::clear() -> void {
glClear(GL_COLOR_BUFFER_BIT); 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(); clear();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -92,15 +92,15 @@ auto OpenGL::refresh() -> void {
struct Source { struct Source {
GLuint texture; GLuint texture;
unsigned width, height; uint width, height;
GLuint filter, wrap; GLuint filter, wrap;
}; };
vector<Source> sources; vector<Source> sources;
sources.prepend({texture, width, height, filter, wrap}); sources.prepend({texture, width, height, filter, wrap});
for(auto& p : programs) { for(auto& p : programs) {
unsigned targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth; uint targetWidth = p.absoluteWidth ? p.absoluteWidth : outputWidth;
unsigned targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight; uint targetHeight = p.absoluteHeight ? p.absoluteHeight : outputHeight;
if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth; if(p.relativeWidth) targetWidth = sources[0].width * p.relativeWidth;
if(p.relativeHeight) targetHeight = sources[0].height * p.relativeHeight; 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("targetSize", targetWidth, targetHeight, 1.0 / targetWidth, 1.0 / targetHeight);
glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight); glrUniform4f("outputSize", outputWidth, outputHeight, 1.0 / outputWidth, 1.0 / outputHeight);
unsigned aid = 0; uint aid = 0;
for(auto& frame : history) { for(auto& frame : history) {
glrUniform1i({"history[", aid, "]"}, aid); glrUniform1i({"history[", aid, "]"}, aid);
glrUniform4f({"historySize[", aid, "]"}, frame.width, frame.height, 1.0 / frame.width, 1.0 / frame.height); 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); glrParameters(frame.filter, frame.wrap);
} }
unsigned bid = 0; uint bid = 0;
for(auto& source : sources) { for(auto& source : sources) {
glrUniform1i({"source[", bid, "]"}, aid + bid); glrUniform1i({"source[", bid, "]"}, aid + bid);
glrUniform4f({"sourceSize[", bid, "]"}, source.width, source.height, 1.0 / source.width, 1.0 / source.height); 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); glrParameters(source.filter, source.wrap);
} }
unsigned cid = 0; uint cid = 0;
for(auto& pixmap : p.pixmaps) { for(auto& pixmap : p.pixmaps) {
glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid); glrUniform1i({"pixmap[", cid, "]"}, aid + bid + cid);
glrUniform4f({"pixmapSize[", bid, "]"}, pixmap.width, pixmap.height, 1.0 / pixmap.width, 1.0 / pixmap.height); 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}); sources.prepend({p.texture, p.width, p.height, p.filter, p.wrap});
} }
unsigned targetWidth = absoluteWidth ? absoluteWidth : outputWidth; uint targetWidth = absoluteWidth ? absoluteWidth : outputWidth;
unsigned targetHeight = absoluteHeight ? absoluteHeight : outputHeight; uint targetHeight = absoluteHeight ? absoluteHeight : outputHeight;
if(relativeWidth) targetWidth = sources[0].width * relativeWidth; if(relativeWidth) targetWidth = sources[0].width * relativeWidth;
if(relativeHeight) targetHeight = sources[0].height * relativeHeight; 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; if(!OpenGLBind()) return false;
glDisable(GL_ALPHA_TEST); glDisable(GL_ALPHA_TEST);
@ -203,8 +203,8 @@ auto OpenGL::init() -> bool {
return initialized = true; return initialized = true;
} }
auto OpenGL::term() -> void { auto OpenGL::terminate() -> void {
if(initialized == false) return; if(!initialized) return;
shader(""); //release shader resources (eg frame[] history) shader(""); //release shader resources (eg frame[] history)
OpenGLSurface::release(); OpenGLSurface::release();
if(buffer) { delete[] buffer; buffer = nullptr; } if(buffer) { delete[] buffer; buffer = nullptr; }

View File

@ -27,8 +27,8 @@ struct OpenGLTexture {
auto getType() const -> GLuint; auto getType() const -> GLuint;
GLuint texture = 0; GLuint texture = 0;
unsigned width = 0; uint width = 0;
unsigned height = 0; uint height = 0;
GLuint format = GL_RGBA8; GLuint format = GL_RGBA8;
GLuint filter = GL_LINEAR; GLuint filter = GL_LINEAR;
GLuint wrap = GL_CLAMP_TO_BORDER; GLuint wrap = GL_CLAMP_TO_BORDER;
@ -36,9 +36,9 @@ struct OpenGLTexture {
struct OpenGLSurface : OpenGLTexture { struct OpenGLSurface : OpenGLTexture {
auto allocate() -> void; auto allocate() -> void;
auto size(unsigned width, unsigned height) -> void; auto size(uint width, uint height) -> void;
auto release() -> 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 program = 0;
GLuint framebuffer = 0; GLuint framebuffer = 0;
@ -55,10 +55,10 @@ struct OpenGLProgram : OpenGLSurface {
auto parse(OpenGL* instance, string& source) -> void; auto parse(OpenGL* instance, string& source) -> void;
auto release() -> void; auto release() -> void;
unsigned phase = 0; //frame counter uint phase = 0; //frame counter
unsigned modulo = 0; //frame counter modulus uint modulo = 0; //frame counter modulus
unsigned absoluteWidth = 0; uint absoluteWidth = 0;
unsigned absoluteHeight = 0; uint absoluteHeight = 0;
double relativeWidth = 0; double relativeWidth = 0;
double relativeHeight = 0; double relativeHeight = 0;
vector<OpenGLTexture> pixmaps; vector<OpenGLTexture> pixmaps;
@ -66,18 +66,18 @@ struct OpenGLProgram : OpenGLSurface {
struct OpenGL : OpenGLProgram { struct OpenGL : OpenGLProgram {
auto shader(const string& pathname) -> void; auto shader(const string& pathname) -> void;
auto allocateHistory(unsigned size) -> void; auto allocateHistory(uint size) -> void;
auto lock(uint32_t*& data, unsigned& pitch) -> bool;
auto clear() -> void; auto clear() -> void;
auto refresh() -> void; auto lock(uint32_t*& data, uint& pitch) -> bool;
auto init() -> bool; auto output() -> void;
auto term() -> void; auto initialize() -> bool;
auto terminate() -> void;
vector<OpenGLProgram> programs; vector<OpenGLProgram> programs;
vector<OpenGLTexture> history; vector<OpenGLTexture> history;
GLuint inputFormat = GL_RGBA8; GLuint inputFormat = GL_RGBA8;
unsigned outputWidth = 0; uint outputWidth = 0;
unsigned outputHeight = 0; uint outputHeight = 0;
struct Setting { struct Setting {
string name; string name;
string value; string value;

View File

@ -47,7 +47,7 @@ auto OpenGLProgram::bind(OpenGL* instance, const Markup::Node& node, const strin
GLuint texture; GLuint texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
unsigned n = pixmaps.size(); uint n = pixmaps.size();
pixmaps(n).texture = texture; pixmaps(n).texture = texture;
pixmaps(n).width = image.width(); pixmaps(n).width = image.width();
pixmaps(n).height = image.height(); 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["filter"]) pixmaps(n).filter = glrFilter(leaf["filter"].text());
if(leaf["wrap"]) pixmaps(n).wrap = glrWrap(leaf["wrap"].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](); uint32_t* buffer = new uint32_t[w * h]();
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer); glTexImage2D(GL_TEXTURE_2D, 0, pixmaps(n).format, w, h, 0, pixmaps(n).getFormat(), pixmaps(n).getType(), buffer);

View File

@ -4,7 +4,7 @@ auto OpenGLSurface::allocate() -> void {
glGenBuffers(3, &vbo[0]); 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; if(width == w && height == h) return;
width = w, height = h; width = w, height = h;
w = glrSize(w), h = glrSize(h); w = glrSize(w), h = glrSize(h);
@ -37,7 +37,7 @@ auto OpenGLSurface::release() -> void {
width = 0, height = 0; 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); glViewport(0, 0, targetWidth, targetHeight);
float w = (float)sourceWidth / (float)glrSize(sourceWidth); float w = (float)sourceWidth / (float)glrSize(sourceWidth);
@ -70,7 +70,7 @@ auto OpenGLSurface::render(unsigned sourceWidth, unsigned sourceHeight, unsigned
}; };
GLfloat positions[4 * 4]; 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); Matrix::Multiply(&positions[n], &vertices[n], 1, 4, modelViewProjection, 4, 4);
} }

View File

@ -1,4 +1,4 @@
static auto glrSize(unsigned size) -> unsigned { static auto glrSize(uint size) -> uint {
return size; return size;
//return bit::round(size); //return nearest power of two //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; return GL_CLAMP_TO_BORDER;
} }
static auto glrModulo(unsigned modulo) -> unsigned { static auto glrModulo(uint modulo) -> uint {
if(modulo) return modulo; if(modulo) return modulo;
return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150 return 300; //divisible by 2, 3, 4, 5, 6, 10, 12, 15, 20, 25, 30, 50, 60, 100, 150
} }

View File

@ -20,6 +20,7 @@ struct VideoSDL : Video {
} }
auto clear() -> void { auto clear() -> void {
if(!ready()) return;
if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer); if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer);
for(uint y : range(_bufferHeight)) { for(uint y : range(_bufferHeight)) {
uint32_t* data = (uint32_t*)_buffer->pixels + y * (_buffer->pitch >> 2); 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 { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(width != _width || height != _height) { if(!ready()) return false;
resize(_width = width, _height = height); if(width != _width || height != _height) resize(_width = width, _height = height);
}
if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer); if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer);
pitch = _buffer->pitch; pitch = _buffer->pitch;
return data = (uint32_t*)_buffer->pixels; return data = (uint32_t*)_buffer->pixels;
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer); if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer);
} }
auto output() -> void { auto output() -> void {
if(!ready()) return;
//ruby input is X8R8G8B8, top 8-bits are ignored. //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) //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. //to prevent blending against the window beneath when X window visual is 32-bits.

View File

@ -4,139 +4,129 @@
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
struct VideoWGL : Video, OpenGL { struct VideoWGL : Video, OpenGL {
~VideoWGL() { term(); } VideoWGL() { initialize(); }
~VideoWGL() { terminate(); }
auto (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) -> HGLRC = nullptr; auto ready() -> bool { return _ready; }
auto (APIENTRY* wglSwapInterval)(int) -> BOOL = nullptr;
HDC display = nullptr; auto context() -> uintptr { return _context; }
HGLRC wglcontext = nullptr; auto blocking() -> bool { return _blocking; }
HWND window = nullptr; auto smooth() -> bool { return _smooth; }
HINSTANCE glwindow = nullptr; auto shader() -> string { return _shader; }
struct { auto setContext(uintptr context) -> bool {
HWND handle = nullptr; if(_context == context) return true;
bool synchronize = false; _context = context;
uint filter = Video::FilterNearest; return initialize();
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 get(const string& name) -> any { auto setBlocking(bool blocking) -> bool {
if(name == Video::Handle) return (uintptr_t)settings.handle; if(_blocking == blocking) return true;
if(name == Video::Synchronize) return settings.synchronize; _blocking = blocking;
if(name == Video::Filter) return settings.filter; if(wglSwapInterval) wglSwapInterval(_blocking);
return {}; return true;
} }
auto set(const string& name, const any& value) -> bool { auto setSmooth(bool smooth) -> bool {
if(name == Video::Handle && value.is<uintptr_t>()) { if(_smooth == smooth) return true;
settings.handle = (HWND)value.get<uintptr_t>(); _smooth = smooth;
return true; if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
} return true;
}
if(name == Video::Synchronize && value.is<bool>()) { auto setShader(string shader) -> bool {
if(settings.synchronize != value.get<bool>()) { if(_shader == shader) return true;
settings.synchronize = value.get<bool>(); OpenGL::shader(_shader = shader);
if(wglcontext) { if(!_shader) OpenGL::filter = _smooth ? GL_LINEAR : GL_NEAREST;
init(); return true;
OpenGL::shader(settings.shader); }
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
}
}
}
if(name == Video::Filter && value.is<uint>()) { auto clear() -> void {
settings.filter = value.get<uint>(); if(!ready()) return;
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; OpenGL::clear();
return true; SwapBuffers(_display);
}
if(name == Video::Shader && value.is<string>()) {
settings.shader = value.get<string>();
OpenGL::shader(settings.shader);
if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST;
return true;
}
return false;
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!ready()) return false;
OpenGL::size(width, height); OpenGL::size(width, height);
return OpenGL::lock(data, pitch); return OpenGL::lock(data, pitch);
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
} }
auto clear() -> void { auto output() -> void {
OpenGL::clear(); if(!ready()) return;
SwapBuffers(display); 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 { private:
RECT rc; auto initialize() -> bool {
GetClientRect(settings.handle, &rc); terminate();
outputWidth = rc.right - rc.left, outputHeight = rc.bottom - rc.top; if(!_context) return false;
OpenGL::refresh();
SwapBuffers(display);
}
auto init() -> bool { PIXELFORMATDESCRIPTOR descriptor = {};
GLuint pixel_format; descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR);
PIXELFORMATDESCRIPTOR pfd; descriptor.nVersion = 1;
memory::fill(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); descriptor.iPixelType = PFD_TYPE_RGBA;
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
display = GetDC(settings.handle); _display = GetDC((HWND)_context);
pixel_format = ChoosePixelFormat(display, &pfd); GLuint pixelFormat = ChoosePixelFormat(_display, &descriptor);
SetPixelFormat(display, pixel_format, &pfd); SetPixelFormat(_display, pixelFormat, &descriptor);
wglcontext = wglCreateContext(display); _wglContext = wglCreateContext(_display);
wglMakeCurrent(display, wglcontext); wglMakeCurrent(_display, _wglContext);
wglCreateContextAttribs = (HGLRC (APIENTRY*)(HDC, HGLRC, const int*))glGetProcAddress("wglCreateContextAttribsARB"); wglCreateContextAttribs = (HGLRC (APIENTRY*)(HDC, HGLRC, const int*))glGetProcAddress("wglCreateContextAttribsARB");
wglSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT"); wglSwapInterval = (BOOL (APIENTRY*)(int))glGetProcAddress("wglSwapIntervalEXT");
if(wglCreateContextAttribs) { if(wglCreateContextAttribs) {
int attributes[] = { int attributeList[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGL_CONTEXT_MINOR_VERSION_ARB, 2,
0 0
}; };
HGLRC context = wglCreateContextAttribs(display, 0, attributes); HGLRC context = wglCreateContextAttribs(_display, 0, attributeList);
if(context) { if(context) {
wglMakeCurrent(nullptr, nullptr); wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(wglcontext); wglDeleteContext(_wglContext);
wglMakeCurrent(display, wglcontext = context); wglMakeCurrent(_display, _wglContext = context);
} }
} }
if(wglSwapInterval) { if(wglSwapInterval) wglSwapInterval(_blocking);
wglSwapInterval(settings.synchronize); return _ready = OpenGL::initialize();
}
OpenGL::init();
return true;
} }
auto term() -> void { auto terminate() -> void {
OpenGL::term(); _ready = false;
OpenGL::terminate();
if(wglcontext) { if(_wglContext) {
wglDeleteContext(wglcontext); wglDeleteContext(_wglContext);
wglcontext = nullptr; _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;
}; };

View File

@ -29,7 +29,7 @@ struct VideoXShm : Video {
} }
auto clear() -> void { auto clear() -> void {
if(!_ready) return; if(!ready()) return;
auto dp = _inputBuffer; auto dp = _inputBuffer;
uint length = _inputWidth * _inputHeight; uint length = _inputWidth * _inputHeight;
while(length--) *dp++ = 255u << 24; while(length--) *dp++ = 255u << 24;
@ -37,6 +37,7 @@ struct VideoXShm : Video {
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!ready()) return false;
if(!_inputBuffer || _inputWidth != width || _inputHeight != height) { if(!_inputBuffer || _inputWidth != width || _inputHeight != height) {
if(_inputBuffer) delete[] _inputBuffer; if(_inputBuffer) delete[] _inputBuffer;
_inputWidth = width; _inputWidth = width;
@ -50,10 +51,11 @@ struct VideoXShm : Video {
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
} }
auto output() -> void { auto output() -> void {
if(!_ready) return; if(!ready()) return;
size(); size();
float xratio = (float)_inputWidth / (float)_outputWidth; float xratio = (float)_inputWidth / (float)_outputWidth;

View File

@ -37,6 +37,7 @@ struct VideoXVideo : Video {
} }
auto clear() -> void { auto clear() -> void {
if(!ready()) return;
memory::fill(_buffer, _bufferWidth * _bufferHeight * sizeof(uint32_t)); memory::fill(_buffer, _bufferWidth * _bufferHeight * sizeof(uint32_t));
//clear twice in case video is double buffered ... //clear twice in case video is double buffered ...
output(); output();
@ -44,21 +45,18 @@ struct VideoXVideo : Video {
} }
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
if(!_ready) return false; if(!ready()) return false;
if(width != _width || height != _height) resize(_width = width, _height = height);
if(width != _width || height != _height) {
resize(_width = width, _height = height);
}
pitch = _bufferWidth * 4; pitch = _bufferWidth * 4;
return data = _buffer; return data = _buffer;
} }
auto unlock() -> void { auto unlock() -> void {
if(!ready()) return;
} }
auto output() -> void { auto output() -> void {
if(!_ready) return; if(!ready()) return;
XWindowAttributes target; XWindowAttributes target;
XGetWindowAttributes(_display, _window, &target); XGetWindowAttributes(_display, _window, &target);
@ -93,6 +91,7 @@ struct VideoXVideo : Video {
true); true);
} }
private:
auto initialize() -> bool { auto initialize() -> bool {
terminate(); terminate();
if(!_context) return false; if(!_context) return false;