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

View file

@ -14,24 +14,60 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from PyQt5.QtCore import Qt, QMargins
from PyQt5.QtOpenGL import QGLWidget
from PyQt5.QtCore import Qt
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):
QGLWidget.__init__(self, parent)
self.setAttribute(Qt.WA_NativeWindow, True)
self.setContentsMargins(QMargins())
self.setFocusPolicy(Qt.StrongFocus)
self.parent = parent
QWindow.__init__(self, None)
def showEvent(self, event):
self.setFocus(True)
self.setSurfaceType(QWindow.OpenGLSurface)
self.ctx = QOpenGLContext()
def context(self):
return self.ctx
def resizeEvent(self, event):
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):
pass
def mouseDoubleClickEvent(self, event):
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 sys
from PyQt5.QtOpenGL import QGLContext
from PyQt5.QtGui import QKeySequence, QPixmap
from PyQt5.QtGui import QKeySequence, QPixmap, QOpenGLContext
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy, QDialog
from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot, QThread
from PyQt5.QtWidgets import QAction, QLabel, QFileDialog, QStackedWidget, QActionGroup, QSizePolicy, QWidget, QDialog
from PyQt5.QtCore import Qt, QTimer, QFileInfo, QEvent, QMargins, pyqtSignal, pyqtSlot
from m64py.core.defs import *
from m64py.frontend.dialogs import *
@ -35,7 +34,6 @@ from m64py.frontend.settings import Settings
from m64py.frontend.glwidget import GLWidget
from m64py.ui.mainwindow_ui import Ui_MainWindow
from m64py.frontend.recentfiles import RecentFiles
from m64py.frontend.keymap import QT2SDL2
class MainWindow(QMainWindow, Ui_MainWindow):
@ -50,7 +48,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
save_image = pyqtSignal(bool)
info_dialog = pyqtSignal(str)
archive_dialog = pyqtSignal(list)
vidext_init = pyqtSignal(QGLContext)
vidext_init = pyqtSignal(QOpenGLContext)
toggle_fs = pyqtSignal()
def __init__(self, optparse):
@ -59,6 +57,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.setupUi(self)
self.opts, self.args = optparse
self._initialized = False
self._initialize_attempt = 0
logview.setParent(self)
@ -78,7 +77,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.slots = {}
self.stack = None
self.glwidget = None
self.cheats = None
self.maximized = False
self.widgets_height = None
@ -125,36 +123,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.create_size_actions()
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):
window_width, window_height = size
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):
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.settings.qset.setValue("size", (window_width, window_height - self.widgets_height))
self.resize(window_width, window_height)
@ -234,15 +200,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.stack.setPalette(palette)
self.stack.setAutoFillBackground(True)
self.glwidget = GLWidget(self)
self.worker.video.set_widget(self, self.glwidget)
self.setCentralWidget(self.stack)
glwidget = GLWidget(self)
self.worker.video.set_widget(self, glwidget)
self.stack.addWidget(View(self))
self.stack.addWidget(self.glwidget)
self.stack.addWidget(QWidget.createWindowContainer(glwidget, self))
self.stack.setCurrentIndex(0)
self.setCentralWidget(self.stack)
def create_state_slots(self):
"""Creates state slot actions."""
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))
def on_vidext_init(self, context):
context.doneCurrent()
context.create()
context.moveToThread(self.worker)
self._initialized = True
def on_toggle_fs(self):
if self.isFullScreen():
@ -386,6 +355,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.update_status("ROM closed.")
del self.cheats
self.cheats = None
self._initialized = False
@pyqtSlot()
def on_actionManually_triggered(self):