mirror of https://github.com/OpenTTD/OpenTTD
Codechange: [OpenGL] Use a pixel buffer object to store the video buffer.
parent
5af0cfd902
commit
a990c497b5
|
@ -264,9 +264,9 @@ OpenGLBackend::~OpenGLBackend()
|
||||||
if (_glDeleteVertexArrays != nullptr) _glDeleteVertexArrays(1, &this->vao_quad);
|
if (_glDeleteVertexArrays != nullptr) _glDeleteVertexArrays(1, &this->vao_quad);
|
||||||
if (_glDeleteBuffers != nullptr) {
|
if (_glDeleteBuffers != nullptr) {
|
||||||
_glDeleteBuffers(1, &this->vbo_quad);
|
_glDeleteBuffers(1, &this->vbo_quad);
|
||||||
|
_glDeleteBuffers(1, &this->vid_pbo);
|
||||||
}
|
}
|
||||||
glDeleteTextures(1, &this->vid_texture);
|
glDeleteTextures(1, &this->vid_texture);
|
||||||
free(this->vid_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,6 +297,8 @@ const char *OpenGLBackend::Init()
|
||||||
/* Check for vertex buffer objects. */
|
/* Check for vertex buffer objects. */
|
||||||
if (!IsOpenGLVersionAtLeast(1, 5) && !IsOpenGLExtensionSupported("ARB_vertex_buffer_object")) return "Vertex buffer objects not supported";
|
if (!IsOpenGLVersionAtLeast(1, 5) && !IsOpenGLExtensionSupported("ARB_vertex_buffer_object")) return "Vertex buffer objects not supported";
|
||||||
if (!BindVBOExtension()) return "Failed to bind VBO extension functions";
|
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. */
|
/* 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 (!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";
|
if (!BindVBAExtension()) return "Failed to bind VBA extension functions";
|
||||||
|
@ -312,6 +314,11 @@ const char *OpenGLBackend::Init()
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
if (glGetError() != GL_NO_ERROR) return "Can't generate video buffer texture";
|
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
|
/* Prime vertex buffer with a full-screen quad and store
|
||||||
* the corresponding state in a vertex array object. */
|
* the corresponding state in a vertex array object. */
|
||||||
static const Simple2DVertex vert_array[] = {
|
static const Simple2DVertex vert_array[] = {
|
||||||
|
@ -355,13 +362,14 @@ const char *OpenGLBackend::Init()
|
||||||
*/
|
*/
|
||||||
bool OpenGLBackend::Resize(int w, int h, bool force)
|
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);
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
/* Re-allocate video buffer texture. */
|
/* Re-allocate video buffer texture and backing store. */
|
||||||
free(this->vid_buffer);
|
_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo);
|
||||||
this->vid_buffer = CallocT<uint32>(w * h); // 32bpp
|
_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);
|
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);
|
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, bool force)
|
||||||
_screen.height = h;
|
_screen.height = h;
|
||||||
_screen.width = w;
|
_screen.width = w;
|
||||||
_screen.pitch = w;
|
_screen.pitch = w;
|
||||||
_screen.dst_ptr = this->GetVideoBuffer();
|
_screen.dst_ptr = nullptr;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render video buffer to the screen.
|
* 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);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
/* Update changed rect of the video buffer texture. */
|
|
||||||
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. */
|
/* Blit video buffer to screen. */
|
||||||
|
glBindTexture(GL_TEXTURE_2D, this->vid_texture);
|
||||||
_glBindVertexArray(this->vao_quad);
|
_glBindVertexArray(this->vao_quad);
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ class OpenGLBackend : public ZeroedMemoryAllocator {
|
||||||
private:
|
private:
|
||||||
static OpenGLBackend *instance; ///< Singleton instance pointer.
|
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 vid_texture; ///< Texture handle for the video buffer texture.
|
||||||
GLuint vao_quad; ///< Vertex array object storing the rendering state for the fullscreen quad.
|
GLuint vao_quad; ///< Vertex array object storing the rendering state for the fullscreen quad.
|
||||||
GLuint vbo_quad; ///< Vertex buffer with a fullscreen quad.
|
GLuint vbo_quad; ///< Vertex buffer with a fullscreen quad.
|
||||||
|
@ -45,13 +45,10 @@ public:
|
||||||
static void Destroy();
|
static void Destroy();
|
||||||
|
|
||||||
bool Resize(int w, int h, bool force = false);
|
bool Resize(int w, int h, bool force = false);
|
||||||
void Paint(Rect update_rect);
|
void Paint();
|
||||||
|
|
||||||
/**
|
void *GetVideoBuffer();
|
||||||
* Get a pointer to the memory for the video driver to draw to.
|
void ReleaseVideoBuffer(const Rect &update_rect);
|
||||||
* @return Pointer to draw on.
|
|
||||||
*/
|
|
||||||
void *GetVideoBuffer() { return this->vid_buffer; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* VIDEO_OPENGL_H */
|
#endif /* VIDEO_OPENGL_H */
|
||||||
|
|
|
@ -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. */
|
/* Allocate backing store of the new size. */
|
||||||
if (this->AllocateBackingStore(w, h)) {
|
if (this->AllocateBackingStore(w, h, force)) {
|
||||||
/* Mark all palette colours dirty. */
|
/* Mark all palette colours dirty. */
|
||||||
_cur_palette.first_dirty = 0;
|
_cur_palette.first_dirty = 0;
|
||||||
_cur_palette.count_dirty = 256;
|
_cur_palette.count_dirty = 256;
|
||||||
|
@ -1093,12 +1093,20 @@ bool VideoDriver_Win32Base::LockVideoBuffer()
|
||||||
if (this->draw_threaded) this->draw_lock.lock();
|
if (this->draw_threaded) this->draw_lock.lock();
|
||||||
|
|
||||||
_screen.dst_ptr = this->GetVideoPointer();
|
_screen.dst_ptr = this->GetVideoPointer();
|
||||||
|
assert(_screen.dst_ptr != nullptr);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoDriver_Win32Base::UnlockVideoBuffer()
|
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();
|
if (this->draw_threaded) this->draw_lock.unlock();
|
||||||
this->buffer_locked = false;
|
this->buffer_locked = false;
|
||||||
}
|
}
|
||||||
|
@ -1239,9 +1247,16 @@ void VideoDriver_Win32GDI::Paint()
|
||||||
this->UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
this->UpdatePalette(dc2, _local_palette.first_dirty, _local_palette.count_dirty);
|
||||||
break;
|
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);
|
blitter->PaletteAnimate(_local_palette);
|
||||||
|
if (need_buf) {
|
||||||
|
this->ReleaseVideoPointer();
|
||||||
|
_screen.dst_ptr = nullptr;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Blitter::PALETTE_ANIMATION_NONE:
|
case Blitter::PALETTE_ANIMATION_NONE:
|
||||||
break;
|
break;
|
||||||
|
@ -1336,7 +1351,7 @@ const char *VideoDriver_Win32OpenGL::Start(const StringList ¶m)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->ClientSizeChanged(this->width, this->height);
|
this->ClientSizeChanged(this->width, this->height, true);
|
||||||
|
|
||||||
this->draw_threaded = false;
|
this->draw_threaded = false;
|
||||||
MarkWholeScreenDirty();
|
MarkWholeScreenDirty();
|
||||||
|
@ -1403,17 +1418,18 @@ const char *VideoDriver_Win32OpenGL::AllocateContext()
|
||||||
|
|
||||||
bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen)
|
bool VideoDriver_Win32OpenGL::ToggleFullscreen(bool full_screen)
|
||||||
{
|
{
|
||||||
|
if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer();
|
||||||
this->DestroyContext();
|
this->DestroyContext();
|
||||||
bool res = this->VideoDriver_Win32Base::ToggleFullscreen(full_screen);
|
bool res = this->VideoDriver_Win32Base::ToggleFullscreen(full_screen);
|
||||||
res &= this->AllocateContext() == nullptr;
|
res &= this->AllocateContext() == nullptr;
|
||||||
this->ClientSizeChanged(this->width, this->height);
|
this->ClientSizeChanged(this->width, this->height, true);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoDriver_Win32OpenGL::AfterBlitterChange()
|
bool VideoDriver_Win32OpenGL::AfterBlitterChange()
|
||||||
{
|
{
|
||||||
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
|
assert(BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 0);
|
||||||
this->ClientSizeChanged(this->width, this->height);
|
this->ClientSizeChanged(this->width, this->height, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1426,8 +1442,13 @@ bool VideoDriver_Win32OpenGL::AllocateBackingStore(int w, int h, bool force)
|
||||||
|
|
||||||
if (this->gl_rc == nullptr) return false;
|
if (this->gl_rc == nullptr) return false;
|
||||||
|
|
||||||
|
if (_screen.dst_ptr != nullptr) this->ReleaseVideoPointer();
|
||||||
|
|
||||||
this->dirty_rect = {};
|
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()
|
void *VideoDriver_Win32OpenGL::GetVideoPointer()
|
||||||
|
@ -1435,6 +1456,13 @@ void *VideoDriver_Win32OpenGL::GetVideoPointer()
|
||||||
return OpenGLBackend::Get()->GetVideoBuffer();
|
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()
|
void VideoDriver_Win32OpenGL::Paint()
|
||||||
{
|
{
|
||||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||||
|
@ -1459,10 +1487,8 @@ void VideoDriver_Win32OpenGL::Paint()
|
||||||
_cur_palette.count_dirty = 0;
|
_cur_palette.count_dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenGLBackend::Get()->Paint(this->dirty_rect);
|
OpenGLBackend::Get()->Paint();
|
||||||
SwapBuffers(this->dc);
|
SwapBuffers(this->dc);
|
||||||
|
|
||||||
this->dirty_rect = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* WITH_OPENGL */
|
#endif /* WITH_OPENGL */
|
||||||
|
|
|
@ -63,7 +63,7 @@ protected:
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
bool MakeWindow(bool full_screen);
|
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. */
|
/** Get screen depth to use for fullscreen mode. */
|
||||||
virtual uint8 GetFullscreenBpp();
|
virtual uint8 GetFullscreenBpp();
|
||||||
|
@ -71,6 +71,8 @@ protected:
|
||||||
virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0;
|
virtual bool AllocateBackingStore(int w, int h, bool force = false) = 0;
|
||||||
/** Get a pointer to the video buffer. */
|
/** Get a pointer to the video buffer. */
|
||||||
virtual void *GetVideoPointer() = 0;
|
virtual void *GetVideoPointer() = 0;
|
||||||
|
/** Hand video buffer back to the painting backend. */
|
||||||
|
virtual void ReleaseVideoPointer() {}
|
||||||
/** Palette of the window has changed. */
|
/** Palette of the window has changed. */
|
||||||
virtual void PaletteChanged(HWND hWnd) = 0;
|
virtual void PaletteChanged(HWND hWnd) = 0;
|
||||||
|
|
||||||
|
@ -149,6 +151,7 @@ protected:
|
||||||
|
|
||||||
bool AllocateBackingStore(int w, int h, bool force = false) override;
|
bool AllocateBackingStore(int w, int h, bool force = false) override;
|
||||||
void *GetVideoPointer() override;
|
void *GetVideoPointer() override;
|
||||||
|
void ReleaseVideoPointer() override;
|
||||||
void PaletteChanged(HWND hWnd) override {}
|
void PaletteChanged(HWND hWnd) override {}
|
||||||
|
|
||||||
const char *AllocateContext();
|
const char *AllocateContext();
|
||||||
|
|
Loading…
Reference in New Issue