Files
test/intern/ghost/intern/GHOST_ContextCGL.mm
Clément Foucault cdb8a8929c GHOST: Delete Mac OpenGL support
The maximum OpenGL versions supported on mac
doesn't meet the minimum required version (>=4.3) anymore.

This removes all the OpenGL paths in GHOST
Cocoa backend and from the drop down menu in
the user preferences.

Pull Request: https://projects.blender.org/blender/blender/pulls/110185
2023-07-19 14:16:03 +02:00

403 lines
12 KiB
Plaintext

/* SPDX-FileCopyrightText: 2013 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
*
* Definition of GHOST_ContextCGL class.
*/
/* Don't generate OpenGL deprecation warning. This is a known thing, and is not something easily
* solvable in a short term. */
#ifdef __clang__
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "GHOST_ContextCGL.hh"
#include <Cocoa/Cocoa.h>
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include <epoxy/gl.h>
#include <cassert>
#include <vector>
static void ghost_fatal_error_dialog(const char *msg)
{
@autoreleasepool {
NSString *message = [NSString stringWithFormat:@"Error opening window:\n%s", msg];
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"Quit"];
[alert setMessageText:@"Blender"];
[alert setInformativeText:message];
[alert setAlertStyle:NSAlertStyleCritical];
[alert runModal];
}
exit(1);
}
NSOpenGLContext *GHOST_ContextCGL::s_sharedOpenGLContext = nil;
MTLCommandQueue *GHOST_ContextCGL::s_sharedMetalCommandQueue = nil;
int GHOST_ContextCGL::s_sharedCount = 0;
GHOST_ContextCGL::GHOST_ContextCGL(bool stereoVisual,
NSView *metalView,
CAMetalLayer *metalLayer,
int debug)
: GHOST_Context(stereoVisual),
m_metalView(metalView),
m_metalLayer(metalLayer),
m_metalRenderPipeline(nil),
m_debug(debug)
{
/* Initialize Metal Swap-chain. */
current_swapchain_index = 0;
for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
m_defaultFramebufferMetalTexture[i].texture = nil;
m_defaultFramebufferMetalTexture[i].index = i;
}
if (m_metalView) {
m_ownsMetalDevice = false;
metalInit();
}
else {
/* Prepare offscreen GHOST Context Metal device. */
id<MTLDevice> metalDevice = MTLCreateSystemDefaultDevice();
if (m_debug) {
printf("Selected Metal Device: %s\n", [metalDevice.name UTF8String]);
}
m_ownsMetalDevice = true;
if (metalDevice) {
m_metalLayer = [[CAMetalLayer alloc] init];
[m_metalLayer setEdgeAntialiasingMask:0];
[m_metalLayer setMasksToBounds:NO];
[m_metalLayer setOpaque:YES];
[m_metalLayer setFramebufferOnly:YES];
[m_metalLayer setPresentsWithTransaction:NO];
[m_metalLayer removeAllAnimations];
[m_metalLayer setDevice:metalDevice];
m_metalLayer.allowsNextDrawableTimeout = NO;
metalInit();
}
else {
ghost_fatal_error_dialog(
"[ERROR] Failed to create Metal device for offscreen GHOST Context.\n");
}
}
/* Initialize swap-interval. */
mtl_SwapInterval = 60;
}
GHOST_ContextCGL::~GHOST_ContextCGL()
{
metalFree();
if (m_ownsMetalDevice) {
if (m_metalLayer) {
[m_metalLayer release];
m_metalLayer = nil;
}
}
}
GHOST_TSuccess GHOST_ContextCGL::swapBuffers()
{
if (m_metalView) {
metalSwapBuffers();
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::setSwapInterval(int interval)
{
mtl_SwapInterval = interval;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::getSwapInterval(int &intervalOut)
{
intervalOut = mtl_SwapInterval;
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::activateDrawingContext()
{
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::releaseDrawingContext()
{
return GHOST_kSuccess;
}
unsigned int GHOST_ContextCGL::getDefaultFramebuffer()
{
/* NOTE(Metal): This is not valid. */
return 0;
}
GHOST_TSuccess GHOST_ContextCGL::updateDrawingContext()
{
if (m_metalView) {
metalUpdateFramebuffer();
return GHOST_kSuccess;
}
return GHOST_kFailure;
}
id<MTLTexture> GHOST_ContextCGL::metalOverlayTexture()
{
/* Increment Swap-chain - Only needed if context is requesting a new texture */
current_swapchain_index = (current_swapchain_index + 1) % METAL_SWAPCHAIN_SIZE;
/* Ensure backing texture is ready for current swapchain index */
updateDrawingContext();
/* Return texture. */
return m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
}
MTLCommandQueue *GHOST_ContextCGL::metalCommandQueue()
{
return s_sharedMetalCommandQueue;
}
MTLDevice *GHOST_ContextCGL::metalDevice()
{
id<MTLDevice> device = m_metalLayer.device;
return (MTLDevice *)device;
}
void GHOST_ContextCGL::metalRegisterPresentCallback(void (*callback)(
MTLRenderPassDescriptor *, id<MTLRenderPipelineState>, id<MTLTexture>, id<CAMetalDrawable>))
{
this->contextPresentCallback = callback;
}
GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
{
@autoreleasepool {
if (m_metalView) {
metalInitFramebuffer();
}
}
return GHOST_kSuccess;
}
GHOST_TSuccess GHOST_ContextCGL::releaseNativeHandles()
{
m_metalView = nil;
return GHOST_kSuccess;
}
static const MTLPixelFormat METAL_FRAMEBUFFERPIXEL_FORMAT = MTLPixelFormatBGRA8Unorm;
void GHOST_ContextCGL::metalInit()
{
@autoreleasepool {
id<MTLDevice> device = m_metalLayer.device;
/* Create a command queue for blit/present operation. Note: All context should share a single
* command queue to ensure correct ordering of work submitted from multiple contexts. */
if (s_sharedMetalCommandQueue == nil) {
s_sharedMetalCommandQueue = (MTLCommandQueue *)[device
newCommandQueueWithMaxCommandBufferCount:GHOST_ContextCGL::max_command_buffer_count];
}
/* Ensure active GHOSTContext retains a reference to the shared context. */
[s_sharedMetalCommandQueue retain];
/* Create shaders for blit operation. */
NSString *source = @R"msl(
using namespace metal;
struct Vertex {
float4 position [[position]];
float2 texCoord [[attribute(0)]];
};
vertex Vertex vertex_shader(uint v_id [[vertex_id]]) {
Vertex vtx;
vtx.position.x = float(v_id & 1) * 4.0 - 1.0;
vtx.position.y = float(v_id >> 1) * 4.0 - 1.0;
vtx.position.z = 0.0;
vtx.position.w = 1.0;
vtx.texCoord = vtx.position.xy * 0.5 + 0.5;
return vtx;
}
constexpr sampler s {};
fragment float4 fragment_shader(Vertex v [[stage_in]],
texture2d<float> t [[texture(0)]]) {
/* Final blit should ensure alpha is 1.0. This resolves
* rendering artifacts for blitting of final backbuffer. */
float4 out_tex = t.sample(s, v.texCoord);
out_tex.a = 1.0;
return out_tex;
}
)msl";
MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
options.languageVersion = MTLLanguageVersion1_1;
NSError *error = nil;
id<MTLLibrary> library = [device newLibraryWithSource:source options:options error:&error];
if (error) {
ghost_fatal_error_dialog(
"GHOST_ContextCGL::metalInit: newLibraryWithSource:options:error: failed!");
}
/* Create a render pipeline for blit operation. */
MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"];
desc.vertexFunction = [library newFunctionWithName:@"vertex_shader"];
/* Ensure library is released. */
[library autorelease];
[desc.colorAttachments objectAtIndexedSubscript:0].pixelFormat = METAL_FRAMEBUFFERPIXEL_FORMAT;
m_metalRenderPipeline = (MTLRenderPipelineState *)[device
newRenderPipelineStateWithDescriptor:desc
error:&error];
if (error) {
ghost_fatal_error_dialog(
"GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed!");
}
/* Create a render pipeline to composite things rendered with Metal on top
* of the frame-buffer contents. Uses the same vertex and fragment shader
* as the blit above, but with alpha blending enabled. */
desc.label = @"Metal Overlay";
desc.colorAttachments[0].blendingEnabled = YES;
desc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
desc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
if (error) {
ghost_fatal_error_dialog(
"GHOST_ContextCGL::metalInit: newRenderPipelineStateWithDescriptor:error: failed (when "
"creating the Metal overlay pipeline)!");
}
}
}
void GHOST_ContextCGL::metalFree()
{
if (m_metalRenderPipeline) {
[m_metalRenderPipeline release];
}
for (int i = 0; i < METAL_SWAPCHAIN_SIZE; i++) {
if (m_defaultFramebufferMetalTexture[i].texture) {
[m_defaultFramebufferMetalTexture[i].texture release];
}
}
}
void GHOST_ContextCGL::metalInitFramebuffer()
{
updateDrawingContext();
}
void GHOST_ContextCGL::metalUpdateFramebuffer()
{
NSRect bounds = [m_metalView bounds];
NSSize backingSize = [m_metalView convertSizeToBacking:bounds.size];
size_t width = (size_t)backingSize.width;
size_t height = (size_t)backingSize.height;
if (m_defaultFramebufferMetalTexture[current_swapchain_index].texture &&
m_defaultFramebufferMetalTexture[current_swapchain_index].texture.width == width &&
m_defaultFramebufferMetalTexture[current_swapchain_index].texture.height == height)
{
return;
}
/* Free old texture */
[m_defaultFramebufferMetalTexture[current_swapchain_index].texture release];
id<MTLDevice> device = m_metalLayer.device;
MTLTextureDescriptor *overlayDesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA16Float
width:width
height:height
mipmapped:NO];
overlayDesc.storageMode = MTLStorageModePrivate;
overlayDesc.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
id<MTLTexture> overlayTex = [device newTextureWithDescriptor:overlayDesc];
if (!overlayTex) {
ghost_fatal_error_dialog(
"GHOST_ContextCGL::metalUpdateFramebuffer: failed to create Metal overlay texture!");
}
else {
overlayTex.label = [NSString
stringWithFormat:@"Metal Overlay for GHOST Context %p", this]; //@"";
// NSLog(@"Created new Metal Overlay (backbuffer) for context %p\n", this);
}
m_defaultFramebufferMetalTexture[current_swapchain_index].texture =
overlayTex; //[(MTLTexture *)overlayTex retain];
/* Clear texture on create */
id<MTLCommandBuffer> cmdBuffer = [s_sharedMetalCommandQueue commandBuffer];
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
{
auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
attachment.texture = m_defaultFramebufferMetalTexture[current_swapchain_index].texture;
attachment.loadAction = MTLLoadActionClear;
attachment.clearColor = MTLClearColorMake(0.294, 0.294, 0.294, 1.000);
attachment.storeAction = MTLStoreActionStore;
}
{
id<MTLRenderCommandEncoder> enc = [cmdBuffer
renderCommandEncoderWithDescriptor:passDescriptor];
[enc endEncoding];
}
[cmdBuffer commit];
[m_metalLayer setDrawableSize:CGSizeMake((CGFloat)width, (CGFloat)height)];
}
void GHOST_ContextCGL::metalSwapBuffers()
{
@autoreleasepool {
updateDrawingContext();
id<CAMetalDrawable> drawable = [m_metalLayer nextDrawable];
if (!drawable) {
return;
}
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
{
auto attachment = [passDescriptor.colorAttachments objectAtIndexedSubscript:0];
attachment.texture = drawable.texture;
attachment.loadAction = MTLLoadActionClear;
attachment.clearColor = MTLClearColorMake(1.0, 0.294, 0.294, 1.000);
attachment.storeAction = MTLStoreActionStore;
}
assert(contextPresentCallback);
assert(m_defaultFramebufferMetalTexture[current_swapchain_index].texture != nil);
(*contextPresentCallback)(passDescriptor,
(id<MTLRenderPipelineState>)m_metalRenderPipeline,
m_defaultFramebufferMetalTexture[current_swapchain_index].texture,
drawable);
}
}