# HG changeset patch # User Michael Lutz # Date 2021-01-16 15:43:16 # Node ID 87ba0f0b3e6532481567186edc380f43c5d26570 # Parent e68d7e73076cabc9e23e25014b655c0718ea00e2 Codechange: [OpenGL] Use a pixel buffer object to store the video buffer. diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp --- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -264,9 +264,9 @@ OpenGLBackend::~OpenGLBackend() if (_glDeleteVertexArrays != nullptr) _glDeleteVertexArrays(1, &this->vao_quad); if (_glDeleteBuffers != nullptr) { _glDeleteBuffers(1, &this->vbo_quad); + _glDeleteBuffers(1, &this->vid_pbo); } glDeleteTextures(1, &this->vid_texture); - free(this->vid_buffer); } /** @@ -297,6 +297,8 @@ const char *OpenGLBackend::Init() /* Check for vertex buffer objects. */ if (!IsOpenGLVersionAtLeast(1, 5) && !IsOpenGLExtensionSupported("ARB_vertex_buffer_object")) return "Vertex buffer objects not supported"; if (!BindVBOExtension()) return "Failed to bind VBO extension functions"; + /* Check for pixel buffer objects. */ + if (!IsOpenGLVersionAtLeast(2, 1) && !IsOpenGLExtensionSupported("GL_ARB_pixel_buffer_object")) return "Pixel buffer objects not supported"; /* Check for vertex array objects. */ if (!IsOpenGLVersionAtLeast(3, 0) && (!IsOpenGLExtensionSupported("GL_ARB_vertex_array_object") || !IsOpenGLExtensionSupported("GL_APPLE_vertex_array_object"))) return "Vertex array objects not supported"; if (!BindVBAExtension()) return "Failed to bind VBA extension functions"; @@ -312,6 +314,11 @@ const char *OpenGLBackend::Init() glBindTexture(GL_TEXTURE_2D, 0); if (glGetError() != GL_NO_ERROR) return "Can't generate video buffer texture"; + /* Create pixel buffer object as video buffer storage. */ + _glGenBuffers(1, &this->vid_pbo); + _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo); + if (glGetError() != GL_NO_ERROR) return "Can't allocate pixel buffer for video buffer"; + /* Prime vertex buffer with a full-screen quad and store * the corresponding state in a vertex array object. */ static const Simple2DVertex vert_array[] = { @@ -355,13 +362,14 @@ const char *OpenGLBackend::Init() */ bool OpenGLBackend::Resize(int w, int h, bool force) { - if (!force && _screen.width == w && _screen.height == h && this->vid_buffer != nullptr) return false; + if (!force && _screen.width == w && _screen.height == h) return false; glViewport(0, 0, w, h); - /* Re-allocate video buffer texture. */ - free(this->vid_buffer); - this->vid_buffer = CallocT(w * h); // 32bpp + /* Re-allocate video buffer texture and backing store. */ + _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo); + _glBufferData(GL_PIXEL_UNPACK_BUFFER, w * h * 4, nullptr, GL_DYNAMIC_READ); // Buffer content has to persist from frame to frame and is read back by the blitter, which means a READ usage hint. + _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, this->vid_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); @@ -371,30 +379,50 @@ bool OpenGLBackend::Resize(int w, int h, _screen.height = h; _screen.width = w; _screen.pitch = w; - _screen.dst_ptr = this->GetVideoBuffer(); + _screen.dst_ptr = nullptr; return true; } /** * Render video buffer to the screen. - * @param update_rect Rectangle encompassing the dirty region of the video buffer. */ -void OpenGLBackend::Paint(Rect update_rect) +void OpenGLBackend::Paint() { - assert(this->vid_buffer != nullptr); - glClear(GL_COLOR_BUFFER_BIT); - /* Update changed rect of the video buffer texture. */ + /* Blit video buffer to screen. */ glBindTexture(GL_TEXTURE_2D, this->vid_texture); - if (!IsEmptyRect(update_rect)) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, _screen.pitch); - glTexSubImage2D(GL_TEXTURE_2D, 0, update_rect.left, update_rect.top, update_rect.right - update_rect.left, update_rect.bottom - update_rect.top, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (uint32 *)this->vid_buffer + update_rect.top * _screen.pitch + update_rect.left); - } - - /* Blit video buffer to screen. */ _glBindVertexArray(this->vao_quad); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } +/** + * Get a pointer to the memory for the video driver to draw to. + * @return Pointer to draw on. + */ +void *OpenGLBackend::GetVideoBuffer() +{ + _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo); + return _glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE); +} + +/** + * Update video buffer texture after the video buffer was filled. + * @param update_rect Rectangle encompassing the dirty region of the video buffer. + */ +void OpenGLBackend::ReleaseVideoBuffer(const Rect &update_rect) +{ + assert(this->vid_pbo != 0); + + _glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo); + _glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + /* Update changed rect of the video buffer texture. */ + if (!IsEmptyRect(update_rect)) { + glBindTexture(GL_TEXTURE_2D, this->vid_texture); + glPixelStorei(GL_UNPACK_ROW_LENGTH, _screen.pitch); + glTexSubImage2D(GL_TEXTURE_2D, 0, update_rect.left, update_rect.top, update_rect.right - update_rect.left, update_rect.bottom - update_rect.top, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (GLvoid *)(size_t)(update_rect.top * _screen.pitch * 4 + update_rect.left * 4)); + } +} + diff --git a/src/video/opengl.h b/src/video/opengl.h --- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -25,7 +25,7 @@ class OpenGLBackend : public ZeroedMemor private: static OpenGLBackend *instance; ///< Singleton instance pointer. - void *vid_buffer; ///< Pointer to the memory used for the video driver to draw to. + GLuint vid_pbo; ///< Pixel buffer object storing the memory used for the video driver to draw to. GLuint vid_texture; ///< Texture handle for the video buffer texture. GLuint vao_quad; ///< Vertex array object storing the rendering state for the fullscreen quad. GLuint vbo_quad; ///< Vertex buffer with a fullscreen quad. @@ -45,13 +45,10 @@ public: static void Destroy(); bool Resize(int w, int h, bool force = false); - void Paint(Rect update_rect); + void Paint(); - /** - * Get a pointer to the memory for the video driver to draw to. - * @return Pointer to draw on. - */ - void *GetVideoBuffer() { return this->vid_buffer; } + void *GetVideoBuffer(); + void ReleaseVideoBuffer(const Rect &update_rect); }; #endif /* VIDEO_OPENGL_H */ diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -984,10 +984,10 @@ void VideoDriver_Win32Base::MainLoop() } } -void VideoDriver_Win32Base::ClientSizeChanged(int w, int h) +void VideoDriver_Win32Base::ClientSizeChanged(int w, int h, bool force) { /* Allocate backing store of the new size. */ - if (this->AllocateBackingStore(w, h)) { + if (this->AllocateBackingStore(w, h, force)) { /* Mark all palette colours dirty. */ _cur_palette.first_dirty = 0; _cur_palette.count_dirty = 256; @@ -1093,12 +1093,20 @@ bool VideoDriver_Win32Base::LockVideoBuf if (this->draw_threaded) this->draw_lock.lock(); _screen.dst_ptr = this->GetVideoPointer(); + assert(_screen.dst_ptr != nullptr); return true; } void VideoDriver_Win32Base::UnlockVideoBuffer() { + assert(_screen.dst_ptr != nullptr); + if (_screen.dst_ptr != nullptr) { + /* Hand video buffer back to the drawing backend. */ + this->ReleaseVideoPointer(); + _screen.dst_ptr = nullptr; + } + if (this->draw_threaded) this->draw_lock.unlock(); this->buffer_locked = false; } @@ -1239,9 +1247,16 @@ void VideoDriver_Win32GDI::Paint() this->UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty); break; - case Blitter::PALETTE_ANIMATION_BLITTER: + case Blitter::PALETTE_ANIMATION_BLITTER: { + bool need_buf = _screen.dst_ptr == nullptr; + if (need_buf) _screen.dst_ptr = this->GetVideoPointer(); blitter->PaletteAnimate(_local_palette); + if (need_buf) { + this->ReleaseVideoPointer(); + _screen.dst_ptr = nullptr; + } break; + } case Blitter::PALETTE_ANIMATION_NONE: break; @@ -1336,7 +1351,7 @@ const char *VideoDriver_Win32OpenGL::Sta return err; } - this->ClientSizeChanged(this->width, this->height); + this->ClientSizeChanged(this->width, this->height, true); this->draw_threaded = false; MarkWholeScreenDirty(); @@ -1403,17 +1418,18 @@ const char *VideoDriver_Win32OpenGL::All bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen) { + if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer(); this->DestroyContext(); bool res = this->VideoDriver_Win32Base::ToggleFullscreen(full_screen); res &= this->AllocateContext() == nullptr; - this->ClientSizeChanged(this->width, this->height); + this->ClientSizeChanged(this->width, this->height, true); return res; } bool VideoDriver_Win32OpenGL::AfterBlitterChange() { assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0); - this->ClientSizeChanged(this->width, this->height); + this->ClientSizeChanged(this->width, this->height, true); return true; } @@ -1426,8 +1442,13 @@ bool VideoDriver_Win32OpenGL::AllocateBa if (this->gl_rc == nullptr) return false; + if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer(); + this->dirty_rect = {}; - return OpenGLBackend::Get()->Resize(w, h, force); + bool res = OpenGLBackend::Get()->Resize(w, h, force); + _screen.dst_ptr = this->GetVideoPointer(); + + return res; } void *VideoDriver_Win32OpenGL::GetVideoPointer() @@ -1435,6 +1456,13 @@ void *VideoDriver_Win32OpenGL::GetVideoP return OpenGLBackend::Get()->GetVideoBuffer(); } +void VideoDriver_Win32OpenGL::ReleaseVideoPointer() +{ + OpenGLBackend::Get()->ReleaseVideoBuffer(this->dirty_rect); + this->dirty_rect = {}; + _screen.dst_ptr = nullptr; +} + void VideoDriver_Win32OpenGL::Paint() { PerformanceMeasurer framerate(PFE_VIDEO); @@ -1459,10 +1487,8 @@ void VideoDriver_Win32OpenGL::Paint() _cur_palette.count_dirty = 0; } - OpenGLBackend::Get()->Paint(this->dirty_rect); + OpenGLBackend::Get()->Paint(); SwapBuffers(this->dc); - - this->dirty_rect = {}; } #endif /* WITH_OPENGL */ diff --git a/src/video/win32_v.h b/src/video/win32_v.h --- a/src/video/win32_v.h +++ b/src/video/win32_v.h @@ -63,7 +63,7 @@ protected: void Initialize(); bool MakeWindow(bool full_screen); - void ClientSizeChanged(int w, int h); + void ClientSizeChanged(int w, int h, bool force = false); /** Get screen depth to use for fullscreen mode. */ virtual uint8 GetFullscreenBpp(); @@ -71,6 +71,8 @@ protected: virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0; /** Get a pointer to the video buffer. */ virtual void *GetVideoPointer() = 0; + /** Hand video buffer back to the painting backend. */ + virtual void ReleaseVideoPointer() {} /** Palette of the window has changed. */ virtual void PaletteChanged(HWND hWnd) = 0; @@ -149,6 +151,7 @@ protected: bool AllocateBackingStore(int w, int h, bool force = false) override; void *GetVideoPointer() override; + void ReleaseVideoPointer() override; void PaletteChanged(HWND hWnd) override {} const char *AllocateContext();