Use QOpenGLContext

This commit is contained in:
Milan Nikolic 2024-10-10 12:08:36 +02:00
parent 234b811bd2
commit 2a9bba7adb
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
3 changed files with 122 additions and 103 deletions

View file

@ -16,15 +16,8 @@
import ctypes import ctypes
try:
# nvidia hack
from OpenGL import GL
glimport = True
except ModuleNotFoundError:
glimport = False
from PyQt5.QtOpenGL import QGLFormat
from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QSurfaceFormat
from sdl2 import SDL_WasInit, SDL_InitSubSystem, SDL_QuitSubSystem, SDL_INIT_VIDEO from sdl2 import SDL_WasInit, SDL_InitSubSystem, SDL_QuitSubSystem, SDL_INIT_VIDEO
from sdl2 import SDL_GetNumDisplayModes, SDL_DisplayMode, SDL_GetDisplayMode from sdl2 import SDL_GetNumDisplayModes, SDL_DisplayMode, SDL_GetDisplayMode
@ -58,8 +51,6 @@ class Video:
self.widget = None self.widget = None
self.glformat = None self.glformat = None
self.glcontext = None self.glcontext = None
self.major = None
self.minor = None
def set_widget(self, parent, widget): def set_widget(self, parent, widget):
"""Sets GL widget.""" """Sets GL widget."""
@ -69,11 +60,16 @@ class Video:
def init(self): def init(self):
"""Initialize GL context.""" """Initialize GL context."""
if not self.glcontext: if not self.glcontext:
self.glformat = QGLFormat() self.glformat = QSurfaceFormat.defaultFormat()
self.glformat.setVersion(3, 3)
self.glformat.setOption(QSurfaceFormat.DeprecatedFunctions, 1)
self.glformat.setProfile(QSurfaceFormat.CompatibilityProfile)
self.glformat.setRenderableType(QSurfaceFormat.OpenGL)
self.glformat.setDepthBufferSize(24)
self.glformat.setSwapInterval(0)
self.glcontext = self.widget.context() self.glcontext = self.widget.context()
self.glcontext.setFormat(self.glformat) self.glcontext.setFormat(self.glformat)
self.glcontext.create()
self.parent.vidext_init.emit(self.glcontext)
return M64ERR_SUCCESS return M64ERR_SUCCESS
def quit(self): def quit(self):
@ -104,13 +100,18 @@ class Video:
def set_mode(self, width, height, bits, mode, flags): def set_mode(self, width, height, bits, mode, flags):
"""Creates a rendering window.""" """Creates a rendering window."""
self.widget.makeCurrent() self.parent.vidext_init.emit(self.glcontext)
if self.widget.isValid(): while not self.parent._initialized:
if glimport: continue
GL.glClearColor(0.0, 0.0, 0.0, 1.0)
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.glcontext.makeCurrent(self.widget)
self.widget.swapBuffers()
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) if self.glcontext.isValid():
# GL = self.glcontext.functions()
# GL.glClearColor(0.0, 0.0, 0.0, 1.0)
# GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
# self.glcontext.swapBuffers(self.glcontext.surface())
# GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
return M64ERR_SUCCESS return M64ERR_SUCCESS
else: else:
return M64ERR_SYSTEM_FAIL return M64ERR_SYSTEM_FAIL
@ -135,7 +136,7 @@ class Video:
def gl_get_proc(self, proc): def gl_get_proc(self, proc):
"""Used to get a pointer to """Used to get a pointer to
an OpenGL extension function.""" an OpenGL extension function."""
addr = self.glcontext.getProcAddress(proc.decode()) addr = self.glcontext.getProcAddress(proc)
if addr is not None: if addr is not None:
return addr.__int__() return addr.__int__()
else: else:
@ -144,18 +145,18 @@ class Video:
def gl_set_attr(self, attr, value): def gl_set_attr(self, attr, value):
"""Sets OpenGL attributes.""" """Sets OpenGL attributes."""
attr_map = { attr_map = {
M64P_GL_DOUBLEBUFFER: self.glformat.setDoubleBuffer, M64P_GL_DOUBLEBUFFER: self.set_doublebuffer,
M64P_GL_BUFFER_SIZE: None, M64P_GL_BUFFER_SIZE: self.set_buffer_size,
M64P_GL_DEPTH_SIZE: self.glformat.setDepthBufferSize, M64P_GL_DEPTH_SIZE: self.glformat.setDepthBufferSize,
M64P_GL_RED_SIZE: self.glformat.setRedBufferSize, M64P_GL_RED_SIZE: self.glformat.setRedBufferSize,
M64P_GL_GREEN_SIZE: self.glformat.setGreenBufferSize, M64P_GL_GREEN_SIZE: self.glformat.setGreenBufferSize,
M64P_GL_BLUE_SIZE: self.glformat.setBlueBufferSize, M64P_GL_BLUE_SIZE: self.glformat.setBlueBufferSize,
M64P_GL_ALPHA_SIZE: self.glformat.setAlphaBufferSize, M64P_GL_ALPHA_SIZE: self.glformat.setAlphaBufferSize,
M64P_GL_SWAP_CONTROL: self.glformat.setSwapInterval, M64P_GL_SWAP_CONTROL: self.glformat.setSwapInterval,
M64P_GL_MULTISAMPLEBUFFERS: self.glformat.setSampleBuffers, M64P_GL_MULTISAMPLEBUFFERS: None,
M64P_GL_MULTISAMPLESAMPLES: self.glformat.setSamples, M64P_GL_MULTISAMPLESAMPLES: self.glformat.setSamples,
M64P_GL_CONTEXT_MAJOR_VERSION: self.set_major, M64P_GL_CONTEXT_MAJOR_VERSION: self.glformat.setMajorVersion,
M64P_GL_CONTEXT_MINOR_VERSION: self.set_minor, M64P_GL_CONTEXT_MINOR_VERSION: self.glformat.setMinorVersion,
M64P_GL_CONTEXT_PROFILE_MASK: self.set_profile M64P_GL_CONTEXT_PROFILE_MASK: self.set_profile
} }
@ -163,24 +164,20 @@ class Video:
if set_attr: if set_attr:
set_attr(value) set_attr(value)
if attr == M64P_GL_CONTEXT_MAJOR_VERSION or attr == M64P_GL_CONTEXT_MINOR_VERSION:
if self.major and self.minor:
self.glformat.setVersion(self.major, self.minor)
return M64ERR_SUCCESS return M64ERR_SUCCESS
def gl_get_attr(self, attr, value): def gl_get_attr(self, attr, value):
"""Gets OpenGL attributes.""" """Gets OpenGL attributes."""
attr_map = { attr_map = {
M64P_GL_DOUBLEBUFFER: self.glformat.doubleBuffer, M64P_GL_DOUBLEBUFFER: self.get_doublebuffer,
M64P_GL_BUFFER_SIZE: None, M64P_GL_BUFFER_SIZE: self.get_buffer_size,
M64P_GL_DEPTH_SIZE: self.glformat.depthBufferSize, M64P_GL_DEPTH_SIZE: self.glformat.depthBufferSize,
M64P_GL_RED_SIZE: self.glformat.redBufferSize, M64P_GL_RED_SIZE: self.glformat.redBufferSize,
M64P_GL_GREEN_SIZE: self.glformat.greenBufferSize, M64P_GL_GREEN_SIZE: self.glformat.greenBufferSize,
M64P_GL_BLUE_SIZE: self.glformat.blueBufferSize, M64P_GL_BLUE_SIZE: self.glformat.blueBufferSize,
M64P_GL_ALPHA_SIZE: self.glformat.alphaBufferSize, M64P_GL_ALPHA_SIZE: self.glformat.alphaBufferSize,
M64P_GL_SWAP_CONTROL: self.glformat.swapInterval, M64P_GL_SWAP_CONTROL: self.glformat.swapInterval,
M64P_GL_MULTISAMPLEBUFFERS: self.glformat.sampleBuffers, M64P_GL_MULTISAMPLEBUFFERS: None,
M64P_GL_MULTISAMPLESAMPLES: self.glformat.samples, M64P_GL_MULTISAMPLESAMPLES: self.glformat.samples,
M64P_GL_CONTEXT_MAJOR_VERSION: self.glformat.majorVersion, M64P_GL_CONTEXT_MAJOR_VERSION: self.glformat.majorVersion,
M64P_GL_CONTEXT_MINOR_VERSION: self.glformat.minorVersion, M64P_GL_CONTEXT_MINOR_VERSION: self.glformat.minorVersion,
@ -199,8 +196,8 @@ class Video:
def gl_swap_buf(self): def gl_swap_buf(self):
"""Swaps the front/back buffers after """Swaps the front/back buffers after
rendering an output video frame. """ rendering an output video frame. """
if self.widget.isValid(): if self.glcontext.isValid():
self.widget.swapBuffers() self.glcontext.swapBuffers(self.glcontext.surface())
return M64ERR_SUCCESS return M64ERR_SUCCESS
def resize_window(self, width, height): def resize_window(self, width, height):
@ -210,7 +207,7 @@ class Video:
def gl_get_default_framebuffer(self): def gl_get_default_framebuffer(self):
"""Gets default framebuffer.""" """Gets default framebuffer."""
return 0 return self.glcontext.defaultFramebufferObject()
def init_with_render_mode(self, mode): def init_with_render_mode(self, mode):
return self.init() return self.init()
@ -221,29 +218,45 @@ class Video:
def vk_get_instance_extensions(self, a, b): def vk_get_instance_extensions(self, a, b):
return M64ERR_SUCCESS return M64ERR_SUCCESS
def set_major(self, major):
self.major = major
def set_minor(self, minor):
self.minor = minor
def set_profile(self, value): def set_profile(self, value):
if value == M64P_GL_CONTEXT_PROFILE_CORE: if value == M64P_GL_CONTEXT_PROFILE_CORE:
self.glformat.setProfile(QGLFormat.CoreProfile) self.glformat.setProfile(QSurfaceFormat.CoreProfile)
elif value == M64P_GL_CONTEXT_PROFILE_COMPATIBILITY: elif value == M64P_GL_CONTEXT_PROFILE_COMPATIBILITY:
self.glformat.setProfile(QGLFormat.CompatibilityProfile) self.glformat.setProfile(QSurfaceFormat.CompatibilityProfile)
else: else:
self.glformat.setProfile(QGLFormat.CompatibilityProfile) self.glformat.setProfile(QSurfaceFormat.CompatibilityProfile)
def get_profile(self): def get_profile(self):
profile = self.glformat.profile() profile = self.glformat.profile()
if profile == QGLFormat.CoreProfile: if profile == QSurfaceFormat.CoreProfile:
return M64P_GL_CONTEXT_PROFILE_CORE return M64P_GL_CONTEXT_PROFILE_CORE
elif profile == QGLFormat.CompatibilityProfile: elif profile == QSurfaceFormat.CompatibilityProfile:
return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY
else: else:
return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY return M64P_GL_CONTEXT_PROFILE_COMPATIBILITY
def set_doublebuffer(self, value):
if value == 1:
self.glformat.setSwapBehavior(QSurfaceFormat.DoubleBuffer)
elif value == 0:
self.glformat.setSwapBehavior(QSurfaceFormat.SingleBuffer)
def get_doublebuffer(self):
if self.glformat.swapBehavior() == QSurfaceFormat.DoubleBuffer:
return 1
return 0
def set_buffer_size(self, value):
val = int(value/4)
self.glformat.setAlphaBufferSize(val)
self.glformat.setRedBufferSize(val)
self.glformat.setGreenBufferSize(val)
self.glformat.setBlueBufferSize(val)
def get_buffer_size(self):
return (self.glformat.alphaBufferSize() + self.glformat.redBufferSize() +
self.glformat.greenBufferSize() + self.glformat.blueBufferSize())
video = Video() video = Video()
vidext = M64pVideoExtensionFunctions() vidext = M64pVideoExtensionFunctions()
vidext.Functions = 17 vidext.Functions = 17

View file

@ -14,24 +14,60 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from PyQt5.QtCore import Qt, QMargins from PyQt5.QtCore import Qt
from PyQt5.QtOpenGL import QGLWidget from PyQt5.QtGui import QWindow, QOpenGLContext
from m64py.core.defs import *
from m64py.frontend.keymap import QT2SDL2
class GLWidget(QGLWidget): class GLWidget(QWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
QGLWidget.__init__(self, parent) self.parent = parent
self.setAttribute(Qt.WA_NativeWindow, True) QWindow.__init__(self, None)
self.setContentsMargins(QMargins())
self.setFocusPolicy(Qt.StrongFocus)
def showEvent(self, event): self.setSurfaceType(QWindow.OpenGLSurface)
self.setFocus(True) self.ctx = QOpenGLContext()
def context(self):
return self.ctx
def resizeEvent(self, event): def resizeEvent(self, event):
size = event.size() size = event.size()
self.resize(size.width(), size.height()) width, height = int(size.width() * self.devicePixelRatio()), int(size.height() * self.devicePixelRatio())
self.resize(width, height)
def paintEvent(self, event): def mouseDoubleClickEvent(self, event):
pass self.parent.toggle_fs.emit()
def keyPressEvent(self, event):
if self.parent.worker.state != M64EMU_RUNNING:
return
key = event.key()
modifiers = event.modifiers()
if modifiers & Qt.AltModifier and (key == Qt.Key_Enter or key == Qt.Key_Return):
self.parent.toggle_fs.emit()
elif key == Qt.Key_F3:
self.parent.worker.save_title()
elif key == Qt.Key_F4:
self.parent.worker.save_snapshot()
else:
try:
sdl_key = QT2SDL2[key]
self.parent.worker.send_sdl_keydown(sdl_key)
except KeyError:
pass
def keyReleaseEvent(self, event):
if self.parent.worker.state != M64EMU_RUNNING:
return
key = event.key()
try:
sdl_key = QT2SDL2[key]
self.parent.worker.send_sdl_keyup(sdl_key)
except KeyError:
pass

View file

@ -17,11 +17,10 @@
import os import os
import sys import sys
from PyQt5.QtOpenGL import QGLContext from PyQt5.QtGui import QKeySequence, QPixmap, QOpenGLContext
from PyQt5.QtGui import QKeySequence, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy, QDialog from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy, QWidget, QDialog
from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot, QThread from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot
from m64py.core.defs import * from m64py.core.defs import *
from m64py.frontend.dialogs import * from m64py.frontend.dialogs import *
@ -35,7 +34,6 @@ from m64py.frontend.settings import Settings
from m64py.frontend.glwidget import GLWidget from m64py.frontend.glwidget import GLWidget
from m64py.ui.mainwindow_ui import Ui_MainWindow from m64py.ui.mainwindow_ui import Ui_MainWindow
from m64py.frontend.recentfiles import RecentFiles from m64py.frontend.recentfiles import RecentFiles
from m64py.frontend.keymap import QT2SDL2
class MainWindow(QMainWindow, Ui_MainWindow): class MainWindow(QMainWindow, Ui_MainWindow):
@ -50,7 +48,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
save_image = pyqtSignal(bool) save_image = pyqtSignal(bool)
info_dialog = pyqtSignal(str) info_dialog = pyqtSignal(str)
archive_dialog = pyqtSignal(list) archive_dialog = pyqtSignal(list)
vidext_init = pyqtSignal(QGLContext) vidext_init = pyqtSignal(QOpenGLContext)
toggle_fs = pyqtSignal() toggle_fs = pyqtSignal()
def __init__(self, optparse): def __init__(self, optparse):
@ -59,6 +57,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.setupUi(self) self.setupUi(self)
self.opts, self.args = optparse self.opts, self.args = optparse
self._initialized = False
self._initialize_attempt = 0 self._initialize_attempt = 0
logview.setParent(self) logview.setParent(self)
@ -78,7 +77,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.slots = {} self.slots = {}
self.stack = None self.stack = None
self.glwidget = None
self.cheats = None self.cheats = None
self.maximized = False self.maximized = False
self.widgets_height = None self.widgets_height = None
@ -125,36 +123,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.create_size_actions() self.create_size_actions()
self.center_widget() self.center_widget()
def mouseDoubleClickEvent(self, event):
self.toggle_fs.emit()
def keyPressEvent(self, event):
if self.worker.state == M64EMU_RUNNING:
key = event.key()
modifiers = event.modifiers()
if modifiers & Qt.AltModifier and \
(key == Qt.Key_Enter or key == Qt.Key_Return):
self.toggle_fs.emit()
elif key == Qt.Key_F3:
self.worker.save_title()
elif key == Qt.Key_F4:
self.worker.save_snapshot()
else:
try:
sdl_key = QT2SDL2[key]
self.worker.send_sdl_keydown(sdl_key)
except KeyError:
pass
def keyReleaseEvent(self, event):
if self.worker.state == M64EMU_RUNNING:
key = event.key()
try:
sdl_key = QT2SDL2[key]
self.worker.send_sdl_keyup(sdl_key)
except KeyError:
pass
def window_size_triggered(self, size): def window_size_triggered(self, size):
window_width, window_height = size window_width, window_height = size
if self.vidext and self.worker.core.get_handle(): if self.vidext and self.worker.core.get_handle():
@ -173,8 +141,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if self.worker.state in (M64EMU_RUNNING, M64EMU_PAUSED): if self.worker.state in (M64EMU_RUNNING, M64EMU_PAUSED):
self.worker.core_state_set(M64CORE_VIDEO_SIZE, video_size) self.worker.core_state_set(M64CORE_VIDEO_SIZE, video_size)
self.glwidget.move(int((window_width - game_width) / 2), 0)
self.set_sizes((window_width, window_height - self.widgets_height)) self.set_sizes((window_width, window_height - self.widgets_height))
self.settings.qset.setValue("size", (window_width, window_height - self.widgets_height)) self.settings.qset.setValue("size", (window_width, window_height - self.widgets_height))
self.resize(window_width, window_height) self.resize(window_width, window_height)
@ -234,15 +200,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.stack.setPalette(palette) self.stack.setPalette(palette)
self.stack.setAutoFillBackground(True) self.stack.setAutoFillBackground(True)
self.glwidget = GLWidget(self) glwidget = GLWidget(self)
self.worker.video.set_widget(self, glwidget)
self.worker.video.set_widget(self, self.glwidget)
self.setCentralWidget(self.stack)
self.stack.addWidget(View(self)) self.stack.addWidget(View(self))
self.stack.addWidget(self.glwidget) self.stack.addWidget(QWidget.createWindowContainer(glwidget, self))
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
self.setCentralWidget(self.stack)
def create_state_slots(self): def create_state_slots(self):
"""Creates state slot actions.""" """Creates state slot actions."""
group = QActionGroup(self) group = QActionGroup(self)
@ -273,7 +239,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
action.triggered.connect(lambda t, wi=w, he=h: self.resize(wi, he)) action.triggered.connect(lambda t, wi=w, he=h: self.resize(wi, he))
def on_vidext_init(self, context): def on_vidext_init(self, context):
context.doneCurrent()
context.create()
context.moveToThread(self.worker) context.moveToThread(self.worker)
self._initialized = True
def on_toggle_fs(self): def on_toggle_fs(self):
if self.isFullScreen(): if self.isFullScreen():
@ -386,6 +355,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.update_status("ROM closed.") self.update_status("ROM closed.")
del self.cheats del self.cheats
self.cheats = None self.cheats = None
self._initialized = False
@pyqtSlot() @pyqtSlot()
def on_actionManually_triggered(self): def on_actionManually_triggered(self):