cocoa: support windowless offscreen rendering on macOS

Fixes: #183
This commit is contained in:
rdb 2017-12-20 14:17:53 +01:00
parent f82a940878
commit ef7f856c46
13 changed files with 352 additions and 157 deletions

View File

@ -0,0 +1,12 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file cocoaGraphicsBuffer.I
* @author rdb
* @date 2017-12-19
*/

View File

@ -0,0 +1,61 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file cocoaGraphicsBuffer.h
* @author rdb
* @date 2017-12-19
*/
#ifndef COCOAGRAPHICSBUFFER_H
#define COCOAGRAPHICSBUFFER_H
#include "pandabase.h"
#include "glgsg.h"
/**
* This is a light wrapper around GLGraphicsBuffer (ie. FBOs) to interface
* with Cocoa contexts, so that it can be used without a host window.
*/
class CocoaGraphicsBuffer : public GLGraphicsBuffer {
public:
CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
const string &name,
const FrameBufferProperties &fb_prop,
const WindowProperties &win_prop,
int flags,
GraphicsStateGuardian *gsg,
GraphicsOutput *host);
virtual bool begin_frame(FrameMode mode, Thread *current_thread);
virtual void end_frame(FrameMode mode, Thread *current_thread);
protected:
virtual void close_buffer();
virtual bool open_buffer();
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
GLGraphicsBuffer::init_type();
register_type(_type_handle, "CocoaGraphicsBuffer",
GLGraphicsBuffer::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "cocoaGraphicsBuffer.I"
#endif

View File

@ -0,0 +1,165 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file cocoaGraphicsBuffer.mm
* @author rdb
* @date 2017-12-19
*/
#include "cocoaGraphicsBuffer.h"
#include "cocoaGraphicsStateGuardian.h"
#include "config_cocoadisplay.h"
#include "cocoaGraphicsPipe.h"
#import <OpenGL/OpenGL.h>
TypeHandle CocoaGraphicsBuffer::_type_handle;
/**
*
*/
CocoaGraphicsBuffer::
CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
const string &name,
const FrameBufferProperties &fb_prop,
const WindowProperties &win_prop,
int flags,
GraphicsStateGuardian *gsg,
GraphicsOutput *host) : // Ignore the host.
GLGraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, nullptr)
{
}
/**
* This function will be called within the draw thread before beginning
* rendering for a given frame. It should do whatever setup is required, and
* return true if the frame should be rendered, or false if it should be
* skipped.
*/
bool CocoaGraphicsBuffer::
begin_frame(FrameMode mode, Thread *current_thread) {
if (_gsg == nullptr) {
return false;
}
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_R(cocoagsg, _gsg, false);
nassertr(cocoagsg->_context != nil, false);
// Lock the context and make it current.
{
PStatTimer timer(_make_current_pcollector, current_thread);
cocoagsg->lock_context();
[cocoagsg->_context makeCurrentContext];
}
return GLGraphicsBuffer::begin_frame(mode, current_thread);
}
/**
* This function will be called within the draw thread after rendering is
* completed for a given frame. It should do whatever finalization is
* required.
*/
void CocoaGraphicsBuffer::
end_frame(FrameMode mode, Thread *current_thread) {
nassertv(_gsg != nullptr);
GLGraphicsBuffer::end_frame(mode, current_thread);
// Release the context.
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_V(cocoagsg, _gsg);
cocoagsg->unlock_context();
}
/**
* Opens the buffer right now. Called from the window thread. Returns true
* if the buffer is successfully opened, or false if there was a problem.
*/
bool CocoaGraphicsBuffer::
open_buffer() {
CocoaGraphicsPipe *cocoa_pipe;
DCAST_INTO_R(cocoa_pipe, _pipe, false);
// GSG CreationInitialization
CocoaGraphicsStateGuardian *cocoagsg;
if (_gsg == nullptr) {
// There is no old gsg. Create a new one.
cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, nullptr);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false);
_gsg = cocoagsg;
} else {
// If the old gsg has the wrong pixel format, create a new one that shares
// with the old gsg.
DCAST_INTO_R(cocoagsg, _gsg, false);
if (!cocoagsg->get_fb_properties().subsumes(_fb_properties)) {
cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, cocoagsg);
cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false);
_gsg = cocoagsg;
}
}
FrameBufferProperties desired_props(_fb_properties);
// Lock the context, so we can safely operate on it.
cocoagsg->lock_context();
// Make the context current and initialize what we need.
[cocoagsg->_context makeCurrentContext];
[cocoagsg->_context update];
cocoagsg->reset_if_new();
// These properties are determined by choose_pixel_format.
_fb_properties.set_force_hardware(cocoagsg->_fbprops.get_force_hardware());
_fb_properties.set_force_software(cocoagsg->_fbprops.get_force_software());
bool success = GLGraphicsBuffer::open_buffer();
if (success) {
rebuild_bitplanes();
if (_needs_rebuild) {
// If it still needs rebuild, then something must have gone wrong.
success = false;
}
}
if (success && !_fb_properties.verify_hardware_software
(desired_props, cocoagsg->get_gl_renderer())) {
GLGraphicsBuffer::close_buffer();
success = false;
}
// Release the context.
cocoagsg->unlock_context();
if (!success) {
return false;
}
return true;
}
/**
* Closes the buffer right now. Called from the window thread.
*/
void CocoaGraphicsBuffer::
close_buffer() {
if (_gsg != nullptr) {
CocoaGraphicsStateGuardian *cocoagsg;
cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
if (cocoagsg != nullptr && cocoagsg->_context != nil) {
cocoagsg->lock_context();
GLGraphicsBuffer::close_buffer();
cocoagsg->unlock_context();
}
_gsg.clear();
} else {
GLGraphicsBuffer::close_buffer();
}
}

View File

@ -18,11 +18,3 @@ INLINE CGDirectDisplayID CocoaGraphicsPipe::
get_display_id() const {
return _display;
}
/**
* Returns the Cocoa NSScreen pointer associated with this graphics pipe.
*/
INLINE NSScreen *CocoaGraphicsPipe::
get_nsscreen() const {
return _screen;
}

View File

@ -35,13 +35,10 @@ class FrameBufferProperties;
*/
class CocoaGraphicsPipe : public GraphicsPipe {
public:
CocoaGraphicsPipe();
CocoaGraphicsPipe(CGDirectDisplayID display);
CocoaGraphicsPipe(NSScreen *screen);
CocoaGraphicsPipe(CGDirectDisplayID display = CGMainDisplayID());
virtual ~CocoaGraphicsPipe();
INLINE CGDirectDisplayID get_display_id() const;
INLINE NSScreen *get_nsscreen() const;
virtual string get_interface_name() const;
static PT(GraphicsPipe) pipe_constructor();
@ -64,11 +61,8 @@ protected:
private:
void load_display_information();
// _display and _screen refer to the same thing, NSScreen being the tiny
// Cocoa wrapper around the Quartz display ID. NSScreen isn't generally
// useful, but we need it when creating the window.
// This is the Quartz display identifier.
CGDirectDisplayID _display;
NSScreen *_screen;
friend class CocoaGraphicsWindow;

View File

@ -12,7 +12,7 @@
*/
#include "cocoaGraphicsPipe.h"
// #include "cocoaGraphicsBuffer.h"
#include "cocoaGraphicsBuffer.h"
#include "cocoaGraphicsWindow.h"
#include "cocoaGraphicsStateGuardian.h"
#include "cocoaPandaApp.h"
@ -30,104 +30,32 @@
TypeHandle CocoaGraphicsPipe::_type_handle;
static void init_app() {
if (NSApp == nil) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[CocoaPandaApp sharedApplication];
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
#endif
[NSApp finishLaunching];
[NSApp activateIgnoringOtherApps:YES];
// Put Cocoa into thread-safe mode by spawning a thread which immediately
// exits.
NSThread* thread = [[NSThread alloc] init];
[thread start];
[thread autorelease];
}
}
/**
* Uses the main screen (the one the user is most likely to be working in at
* the moment).
* Takes a CoreGraphics display ID, which defaults to the main display.
*/
CocoaGraphicsPipe::
CocoaGraphicsPipe() {
CocoaGraphicsPipe(CGDirectDisplayID display) : _display(display) {
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
_is_valid = true;
init_app();
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
_screen = [NSScreen mainScreen];
NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"];
_display = (CGDirectDisplayID) [num longValue];
// Put Cocoa into thread-safe mode by spawning a thread which immediately
// exits.
NSThread* thread = [[NSThread alloc] init];
[thread start];
[thread autorelease];
// We used to also obtain the corresponding NSScreen here, but this causes
// the application icon to start bouncing, which may be undesirable for
// apps that will never open a window.
_display_width = CGDisplayPixelsWide(_display);
_display_height = CGDisplayPixelsHigh(_display);
load_display_information();
cocoadisplay_cat.debug()
<< "Creating CocoaGraphicsPipe for main screen "
<< _screen << " with display ID " << _display << "\n";
}
/**
* Takes a CoreGraphics display ID.
*/
CocoaGraphicsPipe::
CocoaGraphicsPipe(CGDirectDisplayID display) {
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
_is_valid = true;
_display = display;
init_app();
// Iterate over the screens to find the one with our display ID.
NSEnumerator *e = [[NSScreen screens] objectEnumerator];
while (NSScreen *screen = (NSScreen *) [e nextObject]) {
NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"];
if (display == (CGDirectDisplayID) [num longValue]) {
_screen = screen;
break;
}
}
_display_width = CGDisplayPixelsWide(_display);
_display_height = CGDisplayPixelsHigh(_display);
load_display_information();
cocoadisplay_cat.debug()
<< "Creating CocoaGraphicsPipe for screen "
<< _screen << " with display ID " << _display << "\n";
}
/**
* Takes an NSScreen pointer.
*/
CocoaGraphicsPipe::
CocoaGraphicsPipe(NSScreen *screen) {
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
_is_valid = true;
init_app();
if (screen == nil) {
_screen = [NSScreen mainScreen];
} else {
_screen = screen;
}
NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"];
_display = (CGDirectDisplayID) [num longValue];
_display_width = CGDisplayPixelsWide(_display);
_display_height = CGDisplayPixelsHigh(_display);
load_display_information();
cocoadisplay_cat.debug()
<< "Creating CocoaGraphicsPipe for screen "
<< _screen << " with display ID " << _display << "\n";
<< "Creating CocoaGraphicsPipe for display ID " << _display << "\n";
}
/**
@ -308,10 +236,12 @@ make_output(const string &name,
flags, gsg, host);
}
// Second thing to try: a GLGraphicsBuffer
// Second thing to try: a GLGraphicsBuffer. This requires a context, so if
// we don't have a host window, we instead create a CocoaGraphicsBuffer,
// which wraps around GLGraphicsBuffer and manages a context.
if (retry == 1) {
if (!gl_support_fbo || host == NULL ||
if (!gl_support_fbo ||
(flags & (BF_require_parasite | BF_require_window)) != 0) {
return NULL;
}
@ -334,33 +264,14 @@ make_output(const string &name,
precertify = true;
}
}
return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}
/*
// Third thing to try: a CocoaGraphicsBuffer
if (retry == 2) {
if (((flags&BF_require_parasite)!=0)||
((flags&BF_require_window)!=0)||
((flags&BF_resizeable)!=0)||
((flags&BF_size_track_host)!=0)||
((flags&BF_can_bind_layered)!=0)) {
return NULL;
if (host != NULL) {
return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
} else {
return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}
if (!support_rtt) {
if (((flags&BF_rtt_cumulative)!=0)||
((flags&BF_can_bind_every)!=0)) {
// If we require Render-to-Texture, but can't be sure we support it,
// bail.
return NULL;
}
}
return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop,
flags, gsg, host);
}
*/
// Nothing else left to try.
return NULL;

View File

@ -19,3 +19,21 @@ INLINE const FrameBufferProperties &CocoaGraphicsStateGuardian::
get_fb_properties() const {
return _fbprops;
}
/**
* Locks the context.
*/
INLINE void CocoaGraphicsStateGuardian::
lock_context() {
nassertv(_context != nil);
CGLLockContext((CGLContextObj) [_context CGLContextObj]);
}
/**
* Unlocks the context.
*/
INLINE void CocoaGraphicsStateGuardian::
unlock_context() {
nassertv(_context != nil);
CGLUnlockContext((CGLContextObj) [_context CGLContextObj]);
}

View File

@ -19,6 +19,7 @@
#include "glgsg.h"
#import <AppKit/NSOpenGL.h>
#import <OpenGL/OpenGL.h>
/**
* A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific
@ -38,6 +39,9 @@ public:
virtual ~CocoaGraphicsStateGuardian();
INLINE void lock_context();
INLINE void unlock_context();
NSOpenGLContext *_share_context;
NSOpenGLContext *_context;
FrameBufferProperties _fbprops;

View File

@ -65,6 +65,16 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
_fullscreen_mode = NULL;
_windowed_mode = NULL;
// Now that we know for sure we want a window, we can create the Cocoa app.
// This will cause the application icon to appear and start bouncing.
if (NSApp == nil) {
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
#endif
[NSApp finishLaunching];
[NSApp activateIgnoringOtherApps:YES];
}
GraphicsWindowInputDevice device =
GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
add_input_device(device);
@ -144,7 +154,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
nassertr(_view != nil, false);
// Place a lock on the context.
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->lock_context();
// Set the drawable.
if (_properties.get_fullscreen()) {
@ -210,7 +220,7 @@ end_frame(FrameMode mode, Thread *current_thread) {
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_V(cocoagsg, _gsg);
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->unlock_context();
if (mode == FM_render) {
// end_render_texture();
@ -239,7 +249,7 @@ end_flip() {
CocoaGraphicsStateGuardian *cocoagsg;
DCAST_INTO_V(cocoagsg, _gsg);
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->lock_context();
// Swap the front and back buffer.
[cocoagsg->_context flushBuffer];
@ -247,7 +257,7 @@ end_flip() {
// Flush the window
[[_view window] flushWindow];
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->unlock_context();
}
GraphicsWindow::end_flip();
}
@ -399,6 +409,16 @@ open_window() {
}
}
// Iterate over the screens to find the one with our display ID.
NSScreen *screen;
NSEnumerator *e = [[NSScreen screens] objectEnumerator];
while (screen = (NSScreen *) [e nextObject]) {
NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"];
if (cocoa_pipe->_display == (CGDirectDisplayID) [num longValue]) {
break;
}
}
// Center the window if coordinates were set to -1 or -2 TODO: perhaps in
// future, in the case of -1, it should use the origin used in a previous
// run of Panda
@ -406,7 +426,7 @@ open_window() {
if (parent_nsview != NULL) {
container = [parent_nsview bounds];
} else {
container = [cocoa_pipe->_screen frame];
container = [screen frame];
container.origin = NSMakePoint(0, 0);
}
int x = _properties.get_x_origin();
@ -453,7 +473,7 @@ open_window() {
_window = [[CocoaPandaWindow alloc]
initWithContentRect: rect
styleMask:windowStyle
screen:cocoa_pipe->_screen
screen:screen
window:this];
if (_window == nil) {
@ -464,7 +484,7 @@ open_window() {
}
// Lock the context, so we can safely operate on it.
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->lock_context();
// Create the NSView to render to.
NSRect rect = NSMakeRect(0, 0, _properties.get_x_size(), _properties.get_y_size());
@ -588,7 +608,7 @@ open_window() {
cocoagsg->reset_if_new();
// Release the context.
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->unlock_context();
if (!cocoagsg->is_valid()) {
close_window();
@ -637,9 +657,9 @@ close_window() {
cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
if (cocoagsg != NULL && cocoagsg->_context != nil) {
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->lock_context();
[cocoagsg->_context clearDrawable];
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->unlock_context();
}
_gsg.clear();
}
@ -1429,9 +1449,9 @@ handle_close_event() {
cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
if (cocoagsg != NULL && cocoagsg->_context != nil) {
CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->lock_context();
[cocoagsg->_context clearDrawable];
CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
cocoagsg->unlock_context();
}
_gsg.clear();
}

View File

@ -12,6 +12,7 @@
*/
#include "config_cocoadisplay.h"
#include "cocoaGraphicsBuffer.h"
#include "cocoaGraphicsPipe.h"
#include "cocoaGraphicsStateGuardian.h"
#include "cocoaGraphicsWindow.h"
@ -40,6 +41,7 @@ init_libcocoadisplay() {
}
initialized = true;
CocoaGraphicsBuffer::init_type();
CocoaGraphicsPipe::init_type();
CocoaGraphicsStateGuardian::init_type();
CocoaGraphicsWindow::init_type();

View File

@ -1,4 +1,5 @@
#include "config_cocoadisplay.mm"
#include "cocoaGraphicsBuffer.mm"
#include "cocoaGraphicsPipe.mm"
#include "cocoaGraphicsStateGuardian.mm"
#include "cocoaGraphicsWindow.mm"

View File

@ -213,12 +213,20 @@ begin_frame(FrameMode mode, Thread *current_thread) {
return false;
}
if (!_host->begin_frame(FM_parasite, current_thread)) {
if (GLCAT.is_debug()) {
GLCAT.debug()
<< get_name() << "'s host is not ready\n";
if (_host != nullptr) {
if (!_host->begin_frame(FM_parasite, current_thread)) {
if (GLCAT.is_debug()) {
GLCAT.debug()
<< get_name() << "'s host is not ready\n";
}
return false;
}
} else {
// We don't have a host window, which is possible for CocoaGraphicsBuffer.
_gsg->set_current_properties(&get_fb_properties());
if (!_gsg->begin_frame(current_thread)) {
return false;
}
return false;
}
// Figure out the desired size of the buffer.
@ -235,7 +243,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
}
}
if (_creation_flags & GraphicsPipe::BF_size_track_host) {
if (_host->get_size() != _size) {
if (_host != nullptr && _host->get_size() != _size) {
// We also need to rebuild if we need to change size.
_needs_rebuild = true;
}
@ -356,7 +364,7 @@ rebuild_bitplanes() {
// Calculate bitplane size. This can be larger than the buffer.
if (_creation_flags & GraphicsPipe::BF_size_track_host) {
if (_host->get_size() != _size) {
if (_host != nullptr && _host->get_size() != _size) {
set_size_and_recalc(_host->get_x_size(),
_host->get_y_size());
}
@ -1253,7 +1261,11 @@ end_frame(FrameMode mode, Thread *current_thread) {
generate_mipmaps();
}
_host->end_frame(FM_parasite, current_thread);
if (_host != nullptr) {
_host->end_frame(FM_parasite, current_thread);
} else {
glgsg->end_frame(current_thread);
}
if (mode == FM_render) {
trigger_flip();
@ -1315,8 +1327,11 @@ bool CLP(GraphicsBuffer)::
open_buffer() {
report_my_gl_errors();
// Double check that we have a host
nassertr(_host != 0, false);
// Double check that we have a valid gsg
nassertr(_gsg != nullptr, false);
if (!_gsg->is_valid()) {
return false;
}
// Count total color buffers.
int totalcolor =
@ -1491,8 +1506,10 @@ open_buffer() {
_fb_properties.set_back_buffers(0);
_fb_properties.set_indexed_color(0);
_fb_properties.set_rgb_color(1);
_fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware());
_fb_properties.set_force_software(_host->get_fb_properties().get_force_software());
if (_host != nullptr) {
_fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware());
_fb_properties.set_force_software(_host->get_fb_properties().get_force_software());
}
_is_valid = true;
_needs_rebuild = true;
@ -1508,7 +1525,7 @@ open_buffer() {
*/
GraphicsOutput *CLP(GraphicsBuffer)::
get_host() {
return _host;
return (_host != nullptr) ? _host : this;
}
/**
@ -1699,7 +1716,7 @@ report_my_errors(int line, const char *file) {
*/
void CLP(GraphicsBuffer)::
check_host_valid() {
if ((_host == 0)||(!_host->is_valid())) {
if (_host != nullptr && !_host->is_valid()) {
_rb_data_size_bytes = 0;
if (_rb_context != NULL) {
// We must delete this object first, because when the GSG destructs, so

View File

@ -86,8 +86,6 @@ protected:
void report_my_errors(int line, const char *file);
private:
void bind_slot(int layer, bool rb_resize, Texture **attach,
RenderTexturePlane plane, GLenum attachpoint);
void bind_slot_multisample(bool rb_resize, Texture **attach,