Changeset - r24888:87ba0f0b3e65
[Not reviewed]
master
0 4 0
Michael Lutz - 3 years ago 2021-01-16 15:43:16
michi@icosahedron.de
Codechange: [OpenGL] Use a pixel buffer object to store the video buffer.
4 files changed with 89 insertions and 35 deletions:
0 comments (0 inline, 0 general)
src/video/opengl.cpp
Show inline comments
 
@@ -261,15 +261,15 @@ OpenGLBackend::OpenGLBackend()
 
 */
 
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);
 
}
 

	
 
/**
 
 * Check for the needed OpenGL functionality and allocate all resources.
 
 * @return Error string or nullptr if successful.
 
 */
 
@@ -294,12 +294,14 @@ const char *OpenGLBackend::Init()
 
	if (!IsOpenGLVersionAtLeast(1, 3)) return "OpenGL version >= 1.3 required";
 
	/* Check for non-power-of-two texture support. */
 
	if (!IsOpenGLVersionAtLeast(2, 0) && !IsOpenGLExtensionSupported("GL_ARB_texture_non_power_of_two")) return "Non-power-of-two textures not supported";
 
	/* 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";
 

	
 
	/* Setup video buffer texture. */
 
	glGenTextures(1, &this->vid_texture);
 
@@ -309,12 +311,17 @@ const char *OpenGLBackend::Init()
 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
	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[] = {
 
		//  x     y    u    v
 
		{  1.f, -1.f, 1.f, 1.f },
 
		{  1.f,  1.f, 1.f, 0.f },
 
@@ -352,49 +359,70 @@ const char *OpenGLBackend::Init()
 
 * @param h New height of the window.
 
 * @param force Recreate resources even if size didn't change.
 
 * @param False if nothing had to be done, true otherwise.
 
 */
 
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<uint32>(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);
 
	glBindTexture(GL_TEXTURE_2D, 0);
 

	
 
	/* Set new viewport. */
 
	_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));
 
	}
 
}
 

	
src/video/opengl.h
Show inline comments
 
@@ -22,13 +22,13 @@ bool IsOpenGLVersionAtLeast(byte major, 
 

	
 
/** Platform-independent back-end class for OpenGL video drivers. */
 
class OpenGLBackend : public ZeroedMemoryAllocator {
 
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.
 

	
 
	OpenGLBackend();
 
	~OpenGLBackend();
 
@@ -42,16 +42,13 @@ public:
 
		return OpenGLBackend::instance;
 
	}
 
	static const char *Create(GetOGLProcAddressProc get_proc);
 
	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 */
src/video/win32_v.cpp
Show inline comments
 
@@ -981,16 +981,16 @@ void VideoDriver_Win32Base::MainLoop()
 
		delete this->draw_signal;
 

	
 
		this->draw_mutex = nullptr;
 
	}
 
}
 

	
 
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;
 
		_local_palette = _cur_palette;
 

	
 
		BlitterFactory::GetCurrentBlitter()->PostResize();
 
@@ -1090,18 +1090,26 @@ bool VideoDriver_Win32Base::LockVideoBuf
 
	if (this->buffer_locked) return false;
 
	this->buffer_locked = true;
 

	
 
	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;
 
}
 

	
 

	
 
static FVideoDriver_Win32GDI iFVideoDriver_Win32GDI;
 
@@ -1236,15 +1244,22 @@ void VideoDriver_Win32GDI::Paint()
 

	
 
		switch (blitter->UsePaletteAnimation()) {
 
			case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
 
				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;
 

	
 
			default:
 
				NOT_REACHED();
 
@@ -1333,13 +1348,13 @@ const char *VideoDriver_Win32OpenGL::Sta
 
	if (err != nullptr) {
 
		this->Stop();
 
		_cur_resolution = old_res;
 
		return err;
 
	}
 

	
 
	this->ClientSizeChanged(this->width, this->height);
 
	this->ClientSizeChanged(this->width, this->height, true);
 

	
 
	this->draw_threaded = false;
 
	MarkWholeScreenDirty();
 

	
 
	return nullptr;
 
}
 
@@ -1400,44 +1415,57 @@ const char *VideoDriver_Win32OpenGL::All
 

	
 
	return OpenGLBackend::Create(&GetOGLProcAddressCallback);
 
}
 

	
 
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;
 
}
 

	
 
bool VideoDriver_Win32OpenGL::AllocateBackingStore(int w, int h, bool force)
 
{
 
	if (!force && w == _screen.width && h == _screen.height) return false;
 

	
 
	this->width = w = std::max(w, 64);
 
	this->height = h = std::max(h, 64);
 

	
 
	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()
 
{
 
	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);
 

	
 
	if (IsEmptyRect(this->dirty_rect)) return;
 

	
 
@@ -1456,13 +1484,11 @@ void VideoDriver_Win32OpenGL::Paint()
 
			default:
 
				NOT_REACHED();
 
		}
 
		_cur_palette.count_dirty = 0;
 
	}
 

	
 
	OpenGLBackend::Get()->Paint(this->dirty_rect);
 
	OpenGLBackend::Get()->Paint();
 
	SwapBuffers(this->dc);
 

	
 
	this->dirty_rect = {};
 
}
 

	
 
#endif /* WITH_OPENGL */
src/video/win32_v.h
Show inline comments
 
@@ -60,20 +60,22 @@ protected:
 
	bool LockVideoBuffer() override;
 
	void UnlockVideoBuffer() override;
 
	void CheckPaletteAnim() override;
 

	
 
	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();
 
	/** (Re-)create the backing store. */
 
	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;
 

	
 
private:
 
	std::unique_lock<std::recursive_mutex> draw_lock;
 

	
 
@@ -146,12 +148,13 @@ protected:
 

	
 
	void Paint() override;
 
	void PaintThread() override {}
 

	
 
	bool AllocateBackingStore(int w, int h, bool force = false) override;
 
	void *GetVideoPointer() override;
 
	void ReleaseVideoPointer() override;
 
	void PaletteChanged(HWND hWnd) override {}
 

	
 
	const char *AllocateContext();
 
	void DestroyContext();
 
};
 

	
0 comments (0 inline, 0 general)