summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2021-02-11 00:11:02 +0100
committerMichael Lutz <michi@icosahedron.de>2021-02-22 22:16:07 +0100
commit7af1fd3ffbe37c231c53b0d4f667e65e0a7a1d01 (patch)
tree9f8cc218daf9ac92812292d423fdfd8d8390e267
parentb4a3bc1ffe5e1692ab5a53ac936ac67b256be621 (diff)
downloadopenttd-7af1fd3ffbe37c231c53b0d4f667e65e0a7a1d01.tar.xz
Add: [OSX] OpenGL video driver.
-rw-r--r--src/video/cocoa/CMakeLists.txt6
-rw-r--r--src/video/cocoa/cocoa_ogl.h59
-rw-r--r--src/video/cocoa/cocoa_ogl.mm317
-rw-r--r--src/video/cocoa/cocoa_wnd.h2
4 files changed, 384 insertions, 0 deletions
diff --git a/src/video/cocoa/CMakeLists.txt b/src/video/cocoa/CMakeLists.txt
index 968a98a91..092237603 100644
--- a/src/video/cocoa/CMakeLists.txt
+++ b/src/video/cocoa/CMakeLists.txt
@@ -6,3 +6,9 @@ add_files(
cocoa_wnd.mm
CONDITION APPLE
)
+
+add_files(
+ cocoa_ogl.h
+ cocoa_ogl.mm
+ CONDITION APPLE AND OPENGL_FOUND
+)
diff --git a/src/video/cocoa/cocoa_ogl.h b/src/video/cocoa/cocoa_ogl.h
new file mode 100644
index 000000000..be5d73113
--- /dev/null
+++ b/src/video/cocoa/cocoa_ogl.h
@@ -0,0 +1,59 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file cocoa_ogl.h The Cocoa OpenGL video driver. */
+
+#ifndef VIDEO_COCOA_OGL_H
+#define VIDEO_COCOA_OGL_H
+
+#include "cocoa_v.h"
+
+@class OTTD_OpenGLView;
+
+class VideoDriver_CocoaOpenGL : public VideoDriver_Cocoa {
+ CGLContextObj gl_context;
+
+ uint8 *anim_buffer; ///< Animation buffer from OpenGL back-end.
+
+ const char *AllocateContext(bool allow_software);
+
+public:
+ VideoDriver_CocoaOpenGL() : gl_context(nullptr), anim_buffer(nullptr) {}
+
+ const char *Start(const StringList &param) override;
+ void Stop() override;
+
+ bool HasEfficient8Bpp() const override { return true; }
+
+ bool UseSystemCursor() override { return true; }
+
+ void ClearSystemSprites() override;
+
+ bool HasAnimBuffer() override { return true; }
+ uint8 *GetAnimBuffer() override { return this->anim_buffer; }
+
+ /** Return driver name */
+ const char *GetName() const override { return "cocoa-opengl"; }
+
+ void AllocateBackingStore(bool force = false) override;
+
+protected:
+ void Paint() override;
+
+ void *GetVideoPointer() override;
+ void ReleaseVideoPointer() override;
+
+ NSView* AllocateDrawView() override;
+};
+
+class FVideoDriver_CocoaOpenGL : public DriverFactoryBase {
+public:
+ FVideoDriver_CocoaOpenGL() : DriverFactoryBase(Driver::DT_VIDEO, 9, "cocoa-opengl", "Cocoa OpenGL Video Driver") {}
+ Driver *CreateInstance() const override { return new VideoDriver_CocoaOpenGL(); }
+};
+
+#endif /* VIDEO_COCOA_OGL_H */
diff --git a/src/video/cocoa/cocoa_ogl.mm b/src/video/cocoa/cocoa_ogl.mm
new file mode 100644
index 000000000..915f43779
--- /dev/null
+++ b/src/video/cocoa/cocoa_ogl.mm
@@ -0,0 +1,317 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file cocoa_ogl.mm Code related to the cocoa OpengL video driver. */
+
+#ifdef WITH_COCOA
+
+#include "../../stdafx.h"
+#include "../../os/macosx/macos.h"
+
+#define Rect OTTDRect
+#define Point OTTDPoint
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+#undef Rect
+#undef Point
+
+#include "../../openttd.h"
+#include "../../debug.h"
+#include "../../core/geometry_func.hpp"
+#include "../../core/math_func.hpp"
+#include "../../core/mem_func.hpp"
+#include "cocoa_ogl.h"
+#include "cocoa_wnd.h"
+#include "../../blitter/factory.hpp"
+#include "../../gfx_func.h"
+#include "../../framerate_type.h"
+#include "../opengl.h"
+
+#import <dlfcn.h>
+#import <OpenGL/OpenGL.h>
+#import <OpenGL/gl3.h>
+
+
+/**
+ * Important notice regarding all modifications!!!!!!!
+ * There are certain limitations because the file is objective C++.
+ * gdb has limitations.
+ * C++ and objective C code can't be joined in all cases (classes stuff).
+ * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
+ */
+
+/** Platform-specific callback to get an OpenGL funtion pointer. */
+static OGLProc GetOGLProcAddressCallback(const char *proc)
+{
+ static void *dl = nullptr;
+
+ if (dl == nullptr) {
+ dl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY);
+ }
+
+ return reinterpret_cast<OGLProc>(dlsym(dl, proc));
+}
+
+@interface OTTD_CGLLayer : CAOpenGLLayer {
+@private
+ CGLContextObj _context;
+}
+
+@property (class) bool allowSoftware;
++ (CGLPixelFormatObj)defaultPixelFormat;
+
+- (instancetype)initWithContext:(CGLContextObj)context;
+@end
+
+@implementation OTTD_CGLLayer
+
+static bool _allowSoftware;
++ (bool)allowSoftware
+{
+ return _allowSoftware;
+}
++ (void)setAllowSoftware:(bool)newVal
+{
+ _allowSoftware = newVal;
+}
+
+- (instancetype)initWithContext:(CGLContextObj)context
+{
+ if (self = [ super init ]) {
+ self->_context = context;
+
+ self.opaque = YES;
+ self.magnificationFilter = kCAFilterNearest;
+ }
+ return self;
+}
+
++ (CGLPixelFormatObj)defaultPixelFormat
+{
+ CGLPixelFormatAttribute attribs[] = {
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core,
+ kCGLPFAColorSize, (CGLPixelFormatAttribute)24,
+ kCGLPFAAlphaSize, (CGLPixelFormatAttribute)0,
+ kCGLPFADepthSize, (CGLPixelFormatAttribute)0,
+ kCGLPFADoubleBuffer,
+ kCGLPFAAllowOfflineRenderers,
+ kCGLPFASupportsAutomaticGraphicsSwitching,
+ kCGLPFANoRecovery,
+ _allowSoftware ? (CGLPixelFormatAttribute)0 : kCGLPFAAccelerated,
+ (CGLPixelFormatAttribute)0
+ };
+
+ CGLPixelFormatObj pxfmt = nullptr;
+ GLint numPixelFormats;
+ CGLChoosePixelFormat(attribs, &pxfmt, &numPixelFormats);
+
+ return pxfmt;
+}
+
+- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
+{
+ return [ OTTD_CGLLayer defaultPixelFormat ];
+}
+
+- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pf
+{
+ CGLContextObj ctx;
+ CGLCreateContext(pf, self->_context, &ctx);
+
+ /* Set context state that is not shared. */
+ CGLSetCurrentContext(ctx);
+ OpenGLBackend::Get()->PrepareContext();
+
+ return ctx;
+}
+
+- (void)drawInCGLContext:(CGLContextObj)ctx pixelFormat:(CGLPixelFormatObj)pf forLayerTime:(CFTimeInterval)t displayTime:(nullable const CVTimeStamp *)ts
+{
+ CGLSetCurrentContext(ctx);
+
+ OpenGLBackend::Get()->Paint();
+ if (_cursor.in_window) OpenGLBackend::Get()->DrawMouseCursor();
+
+ [ super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts ];
+}
+@end
+
+@interface OTTD_CGLLayerView : NSView
+- (instancetype)initWithFrame:(NSRect)frameRect context:(CGLContextObj)context;
+@end
+
+@implementation OTTD_CGLLayerView
+
+- (instancetype)initWithFrame:(NSRect)frameRect context:(CGLContextObj)context
+{
+ if (self = [ super initWithFrame:frameRect ]) {
+ /* We manage our content updates ourselves. */
+ self.wantsBestResolutionOpenGLSurface = _allow_hidpi_window ? YES : NO;
+ self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
+
+ /* Create backing layer. */
+ CALayer *l = [ [ OTTD_CGLLayer alloc ] initWithContext:context ];
+ self.layer = l;
+ self.wantsLayer = YES;
+ [ l release ];
+ }
+ return self;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return NO;
+}
+
+- (BOOL)isOpaque
+{
+ return YES;
+}
+
+- (void)viewDidChangeBackingProperties
+{
+ self.layer.contentsScale = _allow_hidpi_window && [ self.window respondsToSelector:@selector(backingScaleFactor) ] ? [ self.window backingScaleFactor ] : 1.0f;
+}
+@end
+
+
+static FVideoDriver_CocoaOpenGL iFVideoDriver_CocoaOpenGL;
+
+
+const char *VideoDriver_CocoaOpenGL::Start(const StringList &param)
+{
+ const char *err = this->Initialize();
+ if (err != nullptr) return err;
+
+ int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
+ if (bpp != 8 && bpp != 32) {
+ this->Stop();
+ return "The cocoa OpenGL subdriver only supports 8 and 32 bpp.";
+ }
+
+ /* Try to allocate GL context. */
+ err = this->AllocateContext(GetDriverParamBool(param, "software"));
+ if (err != nullptr) {
+ this->Stop();
+ return err;
+ }
+
+ bool fullscreen = _fullscreen;
+ if (!this->MakeWindow(_cur_resolution.width, _cur_resolution.height)) {
+ this->Stop();
+ return "Could not create window";
+ }
+
+ this->AllocateBackingStore(true);
+
+ if (fullscreen) this->ToggleFullscreen(fullscreen);
+
+ this->GameSizeChanged();
+ this->UpdateVideoModes();
+ MarkWholeScreenDirty();
+
+ return nullptr;
+
+}
+
+void VideoDriver_CocoaOpenGL::Stop()
+{
+ this->VideoDriver_Cocoa::Stop();
+
+ CGLSetCurrentContext(this->gl_context);
+ OpenGLBackend::Destroy();
+ CGLReleaseContext(this->gl_context);
+}
+
+void VideoDriver_CocoaOpenGL::ClearSystemSprites()
+{
+ CGLSetCurrentContext(this->gl_context);
+ OpenGLBackend::Get()->ClearCursorCache();
+}
+
+const char *VideoDriver_CocoaOpenGL::AllocateContext(bool allow_software)
+{
+ [ OTTD_CGLLayer setAllowSoftware:allow_software ];
+
+ CGLPixelFormatObj pxfmt = [ OTTD_CGLLayer defaultPixelFormat ];
+ if (pxfmt == nullptr) return "No suitable pixel format found";
+
+ CGLCreateContext(pxfmt, nullptr, &this->gl_context);
+ CGLDestroyPixelFormat(pxfmt);
+
+ if (this->gl_context == nullptr) return "Can't create a rendering context";
+
+ CGLSetCurrentContext(this->gl_context);
+
+ return OpenGLBackend::Create(&GetOGLProcAddressCallback);
+}
+
+NSView *VideoDriver_CocoaOpenGL::AllocateDrawView()
+{
+ return [ [ OTTD_CGLLayerView alloc ] initWithFrame:this->cocoaview.bounds context:this->gl_context ];
+}
+
+/** Resize the window. */
+void VideoDriver_CocoaOpenGL::AllocateBackingStore(bool force)
+{
+ if (this->window == nil || this->setup) return;
+
+ if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer();
+
+ CGLSetCurrentContext(this->gl_context);
+ NSRect frame = [ this->cocoaview getRealRect:[ this->cocoaview frame ] ];
+ OpenGLBackend::Get()->Resize(frame.size.width, frame.size.height, force);
+ _screen.dst_ptr = this->GetVideoPointer();
+ this->dirty_rect = {};
+
+ /* Redraw screen */
+ this->GameSizeChanged();
+}
+
+void *VideoDriver_CocoaOpenGL::GetVideoPointer()
+{
+ CGLSetCurrentContext(this->gl_context);
+ if (BlitterFactory::GetCurrentBlitter()->NeedsAnimationBuffer()) {
+ this->anim_buffer = OpenGLBackend::Get()->GetAnimBuffer();
+ }
+ return OpenGLBackend::Get()->GetVideoBuffer();
+}
+
+void VideoDriver_CocoaOpenGL::ReleaseVideoPointer()
+{
+ CGLSetCurrentContext(this->gl_context);
+
+ if (this->anim_buffer != nullptr) OpenGLBackend::Get()->ReleaseAnimBuffer(this->dirty_rect);
+ OpenGLBackend::Get()->ReleaseVideoBuffer(this->dirty_rect);
+ this->dirty_rect = {};
+ _screen.dst_ptr = nullptr;
+ this->anim_buffer = nullptr;
+}
+
+void VideoDriver_CocoaOpenGL::Paint()
+{
+ PerformanceMeasurer framerate(PFE_VIDEO);
+
+ if (_cur_palette.count_dirty != 0) {
+ Blitter *blitter = BlitterFactory::GetCurrentBlitter();
+
+ /* Always push a changed palette to OpenGL. */
+ CGLSetCurrentContext(this->gl_context);
+ OpenGLBackend::Get()->UpdatePalette(_cur_palette.palette, _cur_palette.first_dirty, _cur_palette.count_dirty);
+ if (blitter->UsePaletteAnimation() == Blitter::PALETTE_ANIMATION_BLITTER) {
+ blitter->PaletteAnimate(_cur_palette);
+ }
+
+ _cur_palette.count_dirty = 0;
+ }
+
+ [ CATransaction begin ];
+ [ this->cocoaview.subviews[0].layer setNeedsDisplay ];
+ [ CATransaction commit ];
+}
+
+#endif /* WITH_COCOA */
diff --git a/src/video/cocoa/cocoa_wnd.h b/src/video/cocoa/cocoa_wnd.h
index 55f0561b7..f8b9385ac 100644
--- a/src/video/cocoa/cocoa_wnd.h
+++ b/src/video/cocoa/cocoa_wnd.h
@@ -55,6 +55,8 @@ extern NSString *OTTDMainLaunchGameEngine;
@end
+extern bool _allow_hidpi_window;
+
bool CocoaSetupApplication();
void CocoaExitApplication();