-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try moving Metal Layer handling to the pipeline #1554
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,6 @@ | |
*==LICENSE==*/ | ||
|
||
#import "PLSView.h" | ||
#include <Metal/Metal.h> | ||
#include <QuartzCore/QuartzCore.h> | ||
#include "plMessage/plInputEventMsg.h" | ||
|
||
|
@@ -63,39 +62,16 @@ | |
@interface PLSView () | ||
|
||
@property NSTrackingArea* mouseTrackingArea; | ||
#if PLASMA_PIPELINE_METAL | ||
@property(weak) CAMetalLayer* metalLayer; | ||
#endif | ||
|
||
@end | ||
|
||
@implementation PLSView | ||
|
||
// MARK: View setup | ||
- (id)initWithFrame:(NSRect)frameRect | ||
{ | ||
self = [super initWithFrame:frameRect]; | ||
#if PLASMA_PIPELINE_METAL | ||
CAMetalLayer* layer = [CAMetalLayer layer]; | ||
layer.contentsScale = [[NSScreen mainScreen] backingScaleFactor]; | ||
layer.maximumDrawableCount = 3; | ||
layer.pixelFormat = MTLPixelFormatBGR10A2Unorm; | ||
self.layer = self.metalLayer = layer; | ||
#endif | ||
self.layer.backgroundColor = NSColor.blackColor.CGColor; | ||
return self; | ||
} | ||
|
||
- (BOOL)acceptsFirstResponder | ||
{ | ||
return YES; | ||
} | ||
|
||
- (BOOL)wantsLayer | ||
{ | ||
return YES; | ||
} | ||
|
||
// MARK: Left mouse button | ||
- (void)mouseDown:(NSEvent*)event | ||
{ | ||
|
@@ -275,9 +251,12 @@ - (void)resizeDrawable:(CGFloat)scaleFactor | |
return; | ||
} | ||
|
||
#if PLASMA_PIPELINE_METAL | ||
_metalLayer.drawableSize = newSize; | ||
#endif | ||
self.layer.contentsScale = scaleFactor; | ||
|
||
if ([self.layer isKindOfClass:[CAMetalLayer class]]) { | ||
((CAMetalLayer*)self.layer).drawableSize = newSize; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to do this with NSInvocation would probably work but that looks awful to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NSInvocation is typically on the developer do-not-use list (Swift does not support it in any form) - so NSInvocation should be avoided. I think the way it's phrased is fine. drawableSize exists on all versions of CAMetalLayer AFAIK so the CAMetalLayer check is sufficient. |
||
} | ||
|
||
[self.delegate renderView:self | ||
didChangeOutputSize:newSize | ||
scale:scaleFactor]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,9 +42,6 @@ | |
|
||
// System Frameworks | ||
#import <Cocoa/Cocoa.h> | ||
#ifdef PLASMA_PIPELINE_GL | ||
#import <OpenGL/gl.h> | ||
#endif | ||
#ifdef PLASMA_PIPELINE_METAL | ||
#import <Metal/Metal.h> | ||
#endif | ||
|
@@ -82,18 +79,12 @@ | |
#include "plNetGameLib/plNetGameLib.h" | ||
#include "plProduct.h" | ||
|
||
// Until a pipeline is integrated with macOS, need to import the | ||
// abstract definition. | ||
#include "plPipeline/pl3DPipeline.h" | ||
|
||
void PumpMessageQueueProc(); | ||
|
||
extern bool gDataServerLocal; | ||
extern bool gPythonLocal; | ||
extern bool gSDLLocal; | ||
|
||
bool NeedsResolutionUpdate = false; | ||
|
||
std::vector<ST::string> args; | ||
|
||
@interface AppDelegate : NSWindowController <NSApplicationDelegate, | ||
|
@@ -111,7 +102,6 @@ @interface AppDelegate : NSWindowController <NSApplicationDelegate, | |
@property CVDisplayLinkRef displayLink; | ||
@property dispatch_queue_t renderQueue; | ||
@property CALayer* renderLayer; | ||
@property(weak) PLSView* plsView; | ||
@property PLSPatcherWindowController* patcherWindow; | ||
@property NSModalSession currentModalSession; | ||
@property PLSPatcher* patcher; | ||
|
@@ -191,16 +181,18 @@ - (id)init | |
defer:NO]; | ||
window.backgroundColor = NSColor.blackColor; | ||
|
||
PLSView* view = [[PLSView alloc] init]; | ||
self.plsView = view; | ||
window.contentView = view; | ||
[window setDelegate:self]; | ||
|
||
gClient.SetClientWindow((__bridge void *)view.layer); | ||
PLSView* plView = [[PLSView alloc] init]; | ||
window.contentView = plView; | ||
|
||
gClient.SetClientWindow((__bridge void *)plView.layer); | ||
gClient.SetClientDisplay((hsWindowHndl)NULL); | ||
|
||
self = [super initWithWindow:window]; | ||
self.window.acceptsMouseMovedEvents = YES; | ||
|
||
plView.delegate = self; | ||
[window setDelegate:self]; | ||
|
||
return self; | ||
} | ||
|
||
|
@@ -209,7 +201,7 @@ - (id)init | |
- (void)startRunLoop | ||
{ | ||
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:@"PlasmaEventMode"]; | ||
[self.plsView setBoundsSize:self.plsView.bounds.size]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Odd. But I think maybe this line was here to get the view to just enter setBoundsSize and do setup. |
||
[self.window.contentView setBoundsSize:self.window.contentView.bounds.size]; | ||
auto* msg = new plDisplayScaleChangedMsg(self.window.backingScaleFactor); | ||
msg->Send(); | ||
|
||
|
@@ -457,20 +449,27 @@ - (void)startClient | |
{ | ||
PF_CONSOLE_INITIALIZE(Audio) | ||
|
||
self.plsView.delegate = self; | ||
self.renderLayer = gClient->GetPipeline()->GetRenderLayer(); | ||
_renderLayer.backgroundColor = NSColor.blackColor.CGColor; | ||
|
||
self.window.contentView.layer = _renderLayer; | ||
self.window.contentView.wantsLayer = YES; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how I feel. PLSView was meant to be self contained and wrap all details about the layer. Mostly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's some nuance here that I don't entirely understand about layer-hosted views and layer-backed views and the order of setting |
||
// Create a window: | ||
|
||
// Window controller | ||
[self.window setContentSize:NSMakeSize(800, 600)]; | ||
[self.window center]; | ||
[self.window makeKeyAndOrderFront:self]; | ||
self.renderLayer = self.window.contentView.layer; | ||
|
||
[self.renderLayer addObserver:self | ||
forKeyPath:@"device" | ||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | ||
context:DeviceDidChangeContext]; | ||
|
||
|
||
#ifdef PLASMA_PIPELINE_METAL | ||
if ([_renderLayer respondsToSelector:@selector(device)]) { | ||
[_renderLayer addObserver:self | ||
forKeyPath:@"device" | ||
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial | ||
context:DeviceDidChangeContext]; | ||
} | ||
#endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good change. I think that this probably does extra checking. It could simply check the layer type and then check for device - in since CAMetalLayer already has device defined. The respondsToSelector would be unnecessary (wouldn't want some other device function floating in unintentionally in a different layer type.) The ifdef might be redundant in since it's a dynamic check, but we could skip the dynamic check if Metal wasn't present. Longer term - we need a common code path for GL and Metal to handle screen and device changes. I think screen right now is all CGDisplay based so that's agnostic to the underlying API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I can tell, the only thing this is currently being used for it to print the Metal device name in the window title for debugging purposes, so I'm hopeful it can be dropped entirely at some point in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strangely - this has been useful. On Intel MacBook Pros I've run into situations in which Plasma is running on the wrong GPU, so it's a nice sanity check. However it could be maybe re-implemented against the enumeration code that already exists in the engine and may already be storing the same data. There's another reason this is a helpful debug hint. Plasma may need to switch GPUs mid run (which can be supported with Plasma's GPU crash recovery mechanisms.) So double checking the GPU device would be helpful for proofing that out. But I haven't gotten eGPU ejection or Plasma-follows-your-current-displays-GPU yet. |
||
|
||
if (!gClient) { | ||
exit(0); | ||
} | ||
|
@@ -563,7 +562,11 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N | |
|
||
- (void)dealloc | ||
{ | ||
[_renderLayer removeObserver:self forKeyPath:@"device" context:DeviceDidChangeContext]; | ||
#ifdef PLASMA_PIPELINE_METAL | ||
if ([_renderLayer respondsToSelector:@selector(device)]) { | ||
[_renderLayer removeObserver:self forKeyPath:@"device" context:DeviceDidChangeContext]; | ||
} | ||
#endif | ||
} | ||
|
||
@end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4372,6 +4372,14 @@ uint32_t plMetalPipeline::IGetBufferFormatSize(uint8_t format) const | |
return size; | ||
} | ||
|
||
CALayer* plMetalPipeline::GetRenderLayer() const | ||
{ | ||
CA::MetalLayer* layer = CA::MetalLayer::layer(); | ||
layer->setPixelFormat(MTL::PixelFormatBGR10A2Unorm); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. contentsScale is independent from Metal so it probably should go into the client. contentsScale should apply to both GL and Metal evenly. Or whatever else comes along in the future. |
||
|
||
return reinterpret_cast<CALayer*>(layer); | ||
} | ||
|
||
void plMetalPipeline::plMetalPipelineCurrentState::Reset() | ||
{ | ||
fCurrentPipelineState = nullptr; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,11 @@ You can contact Cyan Worlds, Inc. by email [email protected] | |
#define DEFAULT_SHADOWS 0 | ||
#define DEFAULT_PLANARREFLECTIONS 0 | ||
|
||
#if __OBJC__ | ||
@class CALayer; | ||
#else | ||
class CALayer; | ||
#endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now I'm curious, but I bet I know the answer... Another reason CALayer was kept out of the renderer was to prevent Obj-C from creeping in. This forward declaration seems to be part of the abstraction against that. Metal can allocate a CALayer through Metal-cpp. Wouldn't this introduce Obj-C (or, I'm guessing Obj-C msg sending) into the GL renderer itself? One risk of putting it into the pipeline is that a lot of stuff includes the pipeline, so you risk Obj-C leakage. I'm wondering if maybe there needs to be a secondary class that just pairs pipelines with layers, and that way the layer integration and all the mess that goes with can live outside of the pipeline. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this is why I'm playing with subclassing CAOpenGLLayer entirely in C++ using objc runtime APIs (🙊), but in a class that's only included when compiling for Darwin. The alternative is that the client needs to have a CAOpenGLLayer subclass that knows internal state of the GL pipeline like the GL context object, and that feels like it is prone to breaking due to a fuzzy API boundary between the two (whereas CAOpenGLLayer's expected API is well understood) |
||
|
||
struct hsPoint3; | ||
struct hsVector3; | ||
|
@@ -354,7 +359,11 @@ class plPipeline : public plCreatable | |
plDisplayMode fDesktopParams; | ||
|
||
virtual size_t GetViewStackSize() const = 0; | ||
|
||
|
||
#ifdef HS_BUILD_FOR_MACOS | ||
virtual CALayer* GetRenderLayer() const { return nullptr; } | ||
#endif | ||
|
||
float fBackingScale = 1.0f; | ||
void SetBackingScale(float scale) { fBackingScale = scale; }; | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously we've talked about inspecting the screen bit depth and setting the layer pixel format based on that. I.E. don't set a 10 bit color depth if the screen isn't 10 bit. I don't know if that change is moving ahead - but moving that out of the client would complicate things. The answer might need to be some sort of engine level flag for 10 bit color. In theory - Direct3D 9 supports 10 bit color output as well.
I've also seen Apple just set this to 16 bit in their examples (which is what our fragment shaders output in Metal) and call it a day. But memory usage would be higher.