Skip to content

Commit

Permalink
Initial Display Helper Work
Browse files Browse the repository at this point in the history
  • Loading branch information
colincornaby committed Feb 20, 2024
1 parent 131b106 commit c6b8a3f
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Sources/Plasma/Apps/plClient/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ if(WIN32)
elseif(APPLE)
list(APPEND plClient_SOURCES
Mac-Cocoa/main.mm
Mac-Cocoa/plMacDisplayHelper.mm
Mac-Cocoa/NSString+StringTheory.mm
Mac-Cocoa/PLSKeyboardEventMonitor.mm
Mac-Cocoa/PLSView.mm
Expand All @@ -102,6 +103,7 @@ elseif(APPLE)
)
list(APPEND plClient_HEADERS
Mac-Cocoa/NSString+StringTheory.h
Mac-Cocoa/plMacDisplayHelper.h
Mac-Cocoa/PLSKeyboardEventMonitor.h
Mac-Cocoa/PLSView.h
Mac-Cocoa/PLSLoginWindowController.h
Expand Down
8 changes: 8 additions & 0 deletions Sources/Plasma/Apps/plClient/Mac-Cocoa/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
#include "pfGLPipeline/plGLPipeline.h"
#endif
#include "plInputCore/plInputDevice.h"
#include "plMacDisplayHelper.h"
#ifdef PLASMA_PIPELINE_METAL
#include "pfMetalPipeline/plMetalPipeline.h"
#endif
Expand Down Expand Up @@ -105,6 +106,7 @@ @interface AppDelegate : NSWindowController <NSApplicationDelegate,
@public
plClientLoader gClient;
dispatch_source_t _displaySource;
std::shared_ptr<plMacDisplayHelper> _displayHelper;
}

@property(retain) PLSKeyboardEventMonitor* eventMonitor;
Expand Down Expand Up @@ -196,6 +198,9 @@ - (id)init
window.contentView = view;
[window setDelegate:self];

_displayHelper = std::make_shared<plMacDisplayHelper>();
_displayHelper->SetCurrentScreen([window screen]);

gClient.SetClientWindow((__bridge void *)view.layer);
gClient.SetClientDisplay((hsWindowHndl)NULL);

Expand Down Expand Up @@ -231,6 +236,7 @@ - (void)startRunLoop
object:self.window
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification* _Nonnull note) {
_displayHelper->SetCurrentScreen(self.window.screen);
// if we change displays, setup a new draw loop. The new display might
// have a different or variable refresh rate.
[self setupRunLoop];
Expand Down Expand Up @@ -390,6 +396,8 @@ - (void)initializeClient
while (!gClient.IsInited()) {
[NSRunLoop.mainRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

gClient->GetPipeline()->SetDisplayHelper(_displayHelper);

if (!gClient || gClient->GetDone()) {
[NSApp terminate:self];
Expand Down
77 changes: 77 additions & 0 deletions Sources/Plasma/Apps/plClient/Mac-Cocoa/plMacDisplayHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program 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, either version 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/

#ifndef plMacDisplayHelper_hpp
#define plMacDisplayHelper_hpp

#include <stdio.h>
// Currently requires Metal to query attached GPU capabilities
// Capability check will also work for GL - but will need something
// different for older GPUs.
#include <AppKit/AppKit.h>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.h>

#include "plPipeline.h"

class plMacDisplayHelper: public plDisplayHelper
{
public:
CGDirectDisplayID CurrentDisplay() { return fCurrentDisplay; }
// we need NSScreen to query for non rectangular screen geometry
void SetCurrentScreen(NSScreen* screen);

plDisplayMode DefaultDisplayMode() override { return fDefaultDisplayMode; };
void GetSupportedDisplayModes(std::vector<plDisplayMode> *res, int ColorDepth = 32) override;

MTL::Device* RenderDevice() { return fRenderDevice; }

plMacDisplayHelper();
~plMacDisplayHelper();
private:
CGDirectDisplayID fCurrentDisplay;
MTL::Device* fRenderDevice = nullptr;
plDisplayMode fDefaultDisplayMode;
std::vector<plDisplayMode> fDisplayModes;
};

#endif /* plMacDisplayHelper_hpp */
149 changes: 149 additions & 0 deletions Sources/Plasma/Apps/plClient/Mac-Cocoa/plMacDisplayHelper.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program 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, either version 3 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/

#include "plMacDisplayHelper.h"

// CGDirectDisplayCopyCurrentMetalDevice only declared in Metal.h?
#include <Metal/Metal.h>

void plMacDisplayHelper::SetCurrentScreen(NSScreen* screen)
{
fCurrentDisplay = [screen.deviceDescription[@"NSScreenNumber"] intValue];

// Calculate the region actually available for full screen
NSRect currentResolution = [screen frame];
if (@available(macOS 12.0, *)) {
NSEdgeInsets currentSafeAreaInsets = [screen safeAreaInsets];
// Sigh... Origin doesn't matter but lets do it for inspectability
currentResolution.origin.x += currentSafeAreaInsets.left;
currentResolution.origin.y += currentSafeAreaInsets.top;
currentResolution.size.width -= currentSafeAreaInsets.left + currentSafeAreaInsets.right;
currentResolution.size.height -= currentSafeAreaInsets.top + currentSafeAreaInsets.bottom;
}

float safeAspectRatio = currentResolution.size.width / currentResolution.size.height;

fDisplayModes.clear();

CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(fCurrentDisplay, nullptr);
for(int i=0; i< CFArrayGetCount(displayModes); i++)
{
// Now filter out the ones that are taller than the safe area aspect ratio
// This could break in interesting ways if Apple ships displays that have unsafe
// areas along the side - but will prevent us stripping any aspect ratios that don't
// match the display entirely.
// Asked for better guidance here from Apple - FB13375033
CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i);

float modeAspectRatio = float(CGDisplayModeGetWidth(mode)) / float(CGDisplayModeGetHeight(mode));
if(modeAspectRatio < safeAspectRatio)
{
continue;
}

// aspect ratio is good - add the mode to the list
plDisplayMode plasmaMode;
plasmaMode.ColorDepth = 32;
plasmaMode.Width = int(CGDisplayModeGetWidth(mode));
plasmaMode.Height = int(CGDisplayModeGetHeight(mode));

fDisplayModes.push_back(plasmaMode);
}
CFRelease(displayModes);

// we're going to look for a good default mode, but
// in case we somehow don't match at least pick one.

fDefaultDisplayMode = fDisplayModes.front();

fRenderDevice->release();
fRenderDevice = (__bridge MTL::Device *)(CGDirectDisplayCopyCurrentMetalDevice(fCurrentDisplay));

// now inspect the GPU and figure out a good default resolution
// This code is in Metal (for now) - but it should also work
// under OpenGL/Mac as long as the GPU also supports Metal.
if(fRenderDevice->supportsFamily(MTL::GPUFamilyMetal3))
{
// if it's a Metal 3 GPU - it should be good
// Pick the native display resolution
// (Re-picking the first one here for clarity)
fDefaultDisplayMode = fDisplayModes.front();
}
else
{
// time to go down the rabit hole
int maxWidth = INT_MAX;
if(fRenderDevice->lowPower())
{
// integrated - not Apple Silicon, we know it's not very good
maxWidth = 1080;
}
else if(fRenderDevice->recommendedMaxWorkingSetSize() < 4000000000)
{
// if it has less than around 4 gigs of VRAM it might still be performance
// limited
maxWidth = 1400;
}

for (auto & mode : fDisplayModes) {
if(mode.Width <= maxWidth) {
fDefaultDisplayMode = mode;
abort();
}
}
}
}

void plMacDisplayHelper::GetSupportedDisplayModes(std::vector<plDisplayMode> *res, int ColorDepth)
{
*res = fDisplayModes;
}

plMacDisplayHelper::plMacDisplayHelper()
{

};

plMacDisplayHelper::~plMacDisplayHelper()
{
fRenderDevice->release();
}
30 changes: 1 addition & 29 deletions Sources/Plasma/FeatureLib/pfMetalPipeline/plMetalPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,35 +970,7 @@ plMipmap* plMetalPipeline::ExtractMipMap(plRenderTarget* targ)

void plMetalPipeline::GetSupportedDisplayModes(std::vector<plDisplayMode>* res, int ColorDepth)
{
/*
There are decisions to make here.
Modern macOS does not support "display modes." You panel runs at native resolution at all times,
and you can over-render or under-render. But you never set the display mode of the panel, or get
the display mode of the panel. Most games have a "scale slider."
Note: There are legacy APIs for display modes for compatibility with older software. In since
we're here writing a new renderer, lets do things the right way. The display mode APIs also have
trouble with density. I.E. a 4k display might be reported as a 2k display if the window manager is
running in a higher DPI mode.
The basic approach should be to render at whatever the resolution of our output surface is. We're
mostly doing that now (aspect ratio doesn't adjust.)
Ideally we should support some sort of scaling/semi dynamic renderbuffer resolution thing. But don't
mess with the window servers framebuffer size. macOS has accelerated resolution scaling like consoles
do. Use that.
*/

std::vector<plDisplayMode> supported;
CA::MetalLayer* layer = fDevice.GetOutputLayer();
CGSize drawableSize = layer->drawableSize();
supported.emplace_back();
supported[0].Width = drawableSize.width;
supported[0].Height = drawableSize.height;
supported[0].ColorDepth = 32;

*res = supported;
fDisplayHelper->GetSupportedDisplayModes(res);
}

int plMetalPipeline::GetMaxAnisotropicSamples()
Expand Down
13 changes: 13 additions & 0 deletions Sources/Plasma/NucleusLib/inc/plPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ class plDisplayMode
int ColorDepth;
};

class plDisplayHelper
{
public:
virtual plDisplayMode DefaultDisplayMode() = 0;
virtual void GetSupportedDisplayModes(std::vector<plDisplayMode> *res, int ColorDepth = 32) = 0;

plDisplayHelper() = default;
virtual ~plDisplayHelper() = default;
};

class plPipeline : public plCreatable
{
public:
Expand Down Expand Up @@ -357,6 +367,9 @@ class plPipeline : public plCreatable

float fBackingScale = 1.0f;
void SetBackingScale(float scale) { fBackingScale = scale; };

std::shared_ptr<plDisplayHelper> fDisplayHelper;
void SetDisplayHelper(std::shared_ptr<plDisplayHelper> helper) { fDisplayHelper = helper; };
};

#endif // plPipeline_inc

0 comments on commit c6b8a3f

Please sign in to comment.