Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions plume_apple.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <atomic>
#include <memory>
#include <mutex>
#include "plume_render_interface_types.h"

Expand All @@ -20,13 +21,21 @@ namespace plume {
};

class CocoaWindow {
void* windowHandle;
CocoaWindowAttributes cachedAttributes;
std::atomic<int> cachedRefreshRate;
mutable std::mutex attributesMutex;

void updateWindowAttributesInternal(bool forceSync = false);
void updateRefreshRateInternal(bool forceSync = false);
public:
// Updates queued to the main thread capture this state instead of `this`,
// so they stay valid if the wrapper is destroyed before they run.
struct SharedState {
void* windowHandle = nullptr;
CocoaWindowAttributes cachedAttributes = {0, 0, 0, 0};
std::atomic<int> cachedRefreshRate{0};
std::mutex attributesMutex;
};

private:
std::shared_ptr<SharedState> state;

void updateWindowAttributesInternal(bool forceSync = false) const;
void updateRefreshRateInternal(bool forceSync = false) const;
public:
CocoaWindow(void* window);
~CocoaWindow();
Expand Down
101 changes: 47 additions & 54 deletions plume_apple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID) {
}
IOObjectRelease(entry); // Release the entry if we couldn't get parent
}

return RenderDeviceVendor::UNKNOWN;
}

Expand All @@ -56,24 +56,35 @@ CGFloat getScaleFactor(NSWindow *nsWindow) {

// MARK: - CocoaWindow

CocoaWindow::CocoaWindow(void* window)
: windowHandle(window), cachedRefreshRate(0) {
cachedAttributes = {0, 0, 0, 0};
// Main thread only. Asynchronous callers pass shared state and an NSWindow
// captured by the block, never `this`: a swapchain can destroy its wrapper
// before a queued update runs.
static void updateAttributes(CocoaWindow::SharedState &state, NSWindow *nsWindow) {
NSRect contentFrame = [[nsWindow contentView] frame];
CGFloat scaleFactor = getScaleFactor(nsWindow);

std::lock_guard<std::mutex> lock(state.attributesMutex);
state.cachedAttributes.x = (int)round(contentFrame.origin.x);
state.cachedAttributes.y = (int)round(contentFrame.origin.y);
state.cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor);
state.cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor);
}

if ([NSThread isMainThread]) {
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
NSRect contentFrame = [[nsWindow contentView] frame];
CGFloat scaleFactor = getScaleFactor(nsWindow);
static void updateRefreshRate(CocoaWindow::SharedState &state, NSWindow *nsWindow) {
NSScreen *screen = [nsWindow screen];
if (@available(macOS 12.0, *)) {
state.cachedRefreshRate.store((int)[screen maximumFramesPerSecond]);
}
}

cachedAttributes.x = (int)round(contentFrame.origin.x);
cachedAttributes.y = (int)round(contentFrame.origin.y);
cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor);
cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor);
CocoaWindow::CocoaWindow(void* window)
: state(std::make_shared<SharedState>()) {
state->windowHandle = window;

NSScreen *screen = [nsWindow screen];
if (@available(macOS 12.0, *)) {
cachedRefreshRate.store((int)[screen maximumFramesPerSecond]);
}
if ([NSThread isMainThread]) {
NSWindow *nsWindow = (__bridge NSWindow *)window;
updateAttributes(*state, nsWindow);
updateRefreshRate(*state, nsWindow);
} else {
updateWindowAttributesInternal(true);
updateRefreshRateInternal(true);
Expand All @@ -82,17 +93,11 @@ CGFloat getScaleFactor(NSWindow *nsWindow) {

CocoaWindow::~CocoaWindow() {}

void CocoaWindow::updateWindowAttributesInternal(bool forceSync) {
void CocoaWindow::updateWindowAttributesInternal(bool forceSync) const {
std::shared_ptr<SharedState> sharedState = state;
NSWindow *nsWindow = (__bridge NSWindow *)sharedState->windowHandle;
auto updateBlock = ^{
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
NSRect contentFrame = [[nsWindow contentView] frame];
CGFloat scaleFactor = getScaleFactor(nsWindow);

std::lock_guard<std::mutex> lock(attributesMutex);
cachedAttributes.x = (int)round(contentFrame.origin.x);
cachedAttributes.y = (int)round(contentFrame.origin.y);
cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor);
cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor);
updateAttributes(*sharedState, nsWindow);
};

if (forceSync) {
Expand All @@ -102,13 +107,11 @@ CGFloat getScaleFactor(NSWindow *nsWindow) {
}
}

void CocoaWindow::updateRefreshRateInternal(bool forceSync) {
void CocoaWindow::updateRefreshRateInternal(bool forceSync) const {
std::shared_ptr<SharedState> sharedState = state;
NSWindow *nsWindow = (__bridge NSWindow *)sharedState->windowHandle;
auto updateBlock = ^{
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
NSScreen *screen = [nsWindow screen];
if (@available(macOS 12.0, *)) {
cachedRefreshRate.store((int)[screen maximumFramesPerSecond]);
}
updateRefreshRate(*sharedState, nsWindow);
};

if (forceSync) {
Expand All @@ -120,57 +123,47 @@ CGFloat getScaleFactor(NSWindow *nsWindow) {

void CocoaWindow::getWindowAttributes(CocoaWindowAttributes* attributes) const {
if ([NSThread isMainThread]) {
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
NSRect contentFrame = [[nsWindow contentView] frame];
CGFloat scaleFactor = getScaleFactor(nsWindow);

{
std::lock_guard<std::mutex> lock(attributesMutex);
const_cast<CocoaWindow*>(this)->cachedAttributes.x = (int)round(contentFrame.origin.x);
const_cast<CocoaWindow*>(this)->cachedAttributes.y = (int)round(contentFrame.origin.y);
const_cast<CocoaWindow*>(this)->cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor);
const_cast<CocoaWindow*>(this)->cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor);
updateAttributes(*state, (__bridge NSWindow *)state->windowHandle);

*attributes = cachedAttributes;
}
std::lock_guard<std::mutex> lock(state->attributesMutex);
*attributes = state->cachedAttributes;
} else {
{
std::lock_guard<std::mutex> lock(attributesMutex);
*attributes = cachedAttributes;
std::lock_guard<std::mutex> lock(state->attributesMutex);
*attributes = state->cachedAttributes;
}

const_cast<CocoaWindow*>(this)->updateWindowAttributesInternal(false);
updateWindowAttributesInternal(false);
}
}

int CocoaWindow::getRefreshRate() const {
if ([NSThread isMainThread]) {
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
NSWindow *nsWindow = (__bridge NSWindow *)state->windowHandle;
NSScreen *screen = [nsWindow screen];

if (@available(macOS 12.0, *)) {
int freshRate = (int)[screen maximumFramesPerSecond];
const_cast<CocoaWindow*>(this)->cachedRefreshRate.store(freshRate);
state->cachedRefreshRate.store(freshRate);
return freshRate;
}

return cachedRefreshRate.load();
return state->cachedRefreshRate.load();
} else {
int rate = cachedRefreshRate.load();
int rate = state->cachedRefreshRate.load();

const_cast<CocoaWindow*>(this)->updateRefreshRateInternal(false);
updateRefreshRateInternal(false);

return rate;
}
}

void CocoaWindow::toggleFullscreen() {
NSWindow *nsWindow = (__bridge NSWindow *)state->windowHandle;
if ([NSThread isMainThread]) {
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
[nsWindow toggleFullScreen:NULL];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
NSWindow *nsWindow = (__bridge NSWindow *)windowHandle;
[nsWindow toggleFullScreen:NULL];
});
}
Expand Down