Clean and optimized (Pixel buffer Object and thead) Screenshot.

This commit is contained in:
brunoherbelin
2020-07-11 01:07:21 +02:00
parent 41ec7a035b
commit 4dbaf3c4f5
6 changed files with 105 additions and 70 deletions

View File

@@ -148,7 +148,7 @@ message(STATUS "Including 'STB Nothings' from https://github.com/nothings/stb --
# DIRENT
#
if(WIN32)
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext)
set(DIRENT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/Dirent/include)
message(STATUS "Including 'Dirent' from https://github.com/tronkko/dirent -- ${DIRENT_INCLUDE_DIR}.")
endif( WIN32 )

View File

@@ -93,7 +93,6 @@ protected:
GeometryView geometry_;
LayerView layer_;
TransitionView transition_;
// class SessionSource *transition_source_;
gint64 update_time_;
float dt_;

View File

@@ -228,7 +228,7 @@ void Rendering::draw()
// perform screenshot if requested
if (request_screenshot_) {
// glfwMakeContextCurrent(main_window_);
screenshot_.CreateFromCaptureGL(0, 0, main_.width(), main_.height());
screenshot_.captureGL(0, 0, main_.width(), main_.height());
request_screenshot_ = false;
}
@@ -353,8 +353,7 @@ Screenshot *Rendering::currentScreenshot()
}
void Rendering::requestScreenshot()
{
screenshot_.Clear();
{
request_screenshot_ = true;
}

View File

@@ -2,6 +2,8 @@
#include <memory.h>
#include <assert.h>
#include <thread>
#include <atomic>
#include <glad/glad.h>
@@ -9,63 +11,84 @@
#include <stb_image.h>
#include <stb_image_write.h>
Screenshot::Screenshot()
{
Width = Height = 0;
Data = nullptr;
Pbo = 0;
Pbo_size = 0;
Pbo_full = false;
}
Screenshot::~Screenshot()
{
Clear();
if (Data) free(Data);
}
bool Screenshot::IsFull()
bool Screenshot::isFull()
{
return Data != nullptr;
return Pbo_full;
}
void Screenshot::Clear()
void Screenshot::captureGL(int x, int y, int w, int h)
{
if (IsFull())
free(Data);
Data = nullptr;
}
Width = w - x;
Height = h - y;
unsigned int size = Width * Height * 4;
void Screenshot::CreateEmpty(int w, int h)
{
Clear();
Width = w;
Height = h;
Data = (unsigned int*) malloc(Width * Height * 4 * sizeof(unsigned int));
memset(Data, 0, Width * Height * 4);
}
// create BPO
if (Pbo == 0)
glGenBuffers(1, &Pbo);
void Screenshot::CreateFromCaptureGL(int x, int y, int w, int h)
{
Clear();
Width = w;
Height = h;
Data = (unsigned int*) malloc(Width * Height * 4);
// bind
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
// actual capture of frame buffer
// init
if (Pbo_size != size) {
Pbo_size = size;
if (Data) free(Data);
Data = (unsigned char*) malloc(Pbo_size);
glBufferData(GL_PIXEL_PACK_BUFFER, Pbo_size, NULL, GL_STREAM_READ);
}
// screenshot to PBO (fast)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, Data);
glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 0);
Pbo_full = true;
// make it usable
RemoveAlpha();
FlipVertical();
// done
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
void Screenshot::SaveFile(const char* filename)
void Screenshot::save(std::string filename)
{
if (Data)
stbi_write_png(filename, Width, Height, 4, Data, Width * 4);
// is there something to save?
if (Pbo && Pbo_size > 0 && Pbo_full) {
// bind buffer
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
// get pixels (quite fast)
unsigned char* ptr = (unsigned char*) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (NULL != ptr) {
memmove(Data, ptr, Pbo_size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
// initiate saving in thread (slow)
std::thread(storeToFile, this, filename).detach();
// ready for next
Pbo_full = false;
}
}
void Screenshot::RemoveAlpha()
{
unsigned int* p = Data;
unsigned int* p = (unsigned int*)Data;
int n = Width * Height;
while (n-- > 0)
{
@@ -74,21 +97,6 @@ void Screenshot::RemoveAlpha()
}
}
void Screenshot::BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const
{
const Screenshot* src = this;
assert(dst != src);
assert(dst != NULL);
assert(src_x >= 0 && src_y >= 0);
assert(src_x + w <= src->Width);
assert(src_y + h <= src->Height);
assert(dst_x >= 0 && dst_y >= 0);
assert(dst_x + w <= dst->Width);
assert(dst_y + h <= dst->Height);
for (int y = 0; y < h; y++)
memcpy(dst->Data + dst_x + (dst_y + y) * dst->Width, src->Data + src_x + (src_y + y) * src->Width, w * 4);
}
void Screenshot::FlipVertical()
{
int comp = 4;
@@ -107,3 +115,21 @@ void Screenshot::FlipVertical()
delete[] line_tmp;
}
// Thread to perform slow operation of saving to file
void Screenshot::storeToFile(Screenshot *s, std::string filename)
{
static std::atomic<bool> ScreenshotSavePending_ = false;
// only one save at a time
if (ScreenshotSavePending_)
return;
ScreenshotSavePending_ = true;
// got data to save ?
if (s && s->Data) {
// make it usable
s->RemoveAlpha();
s->FlipVertical();
// save file
stbi_write_png(filename.c_str(), s->Width, s->Height, 4, s->Data, s->Width * 4);
}
ScreenshotSavePending_ = false;
}

View File

@@ -1,26 +1,31 @@
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
#include <string>
class Screenshot
{
int Width, Height;
unsigned int * Data;
unsigned char * Data;
unsigned int Pbo;
unsigned int Pbo_size;
bool Pbo_full;
void RemoveAlpha();
void FlipVertical();
static void storeToFile(Screenshot *s, std::string filename);
public:
Screenshot();
~Screenshot();
bool IsFull();
void Clear();
void CreateEmpty(int w, int h);
void CreateFromCaptureGL(int x, int y, int w, int h);
void RemoveAlpha();
void FlipVertical();
void SaveFile(const char* filename);
void BlitTo(Screenshot* dst, int src_x, int src_y, int dst_x, int dst_y, int w, int h) const;
// Quick usage :
// 1) Capture screenshot
void captureGL(int x, int y, int w, int h);
// 2) if it is full after capture
bool isFull();
// 3) then you can save to file
void save(std::string filename);
};
#endif // SCREENSHOT_H

View File

@@ -299,8 +299,8 @@ void UserInterface::handleKeyboard()
Mixer::manager().setView(View::GEOMETRY);
else if (ImGui::IsKeyPressed( GLFW_KEY_F3 ))
Mixer::manager().setView(View::LAYER);
else if (ImGui::IsKeyPressed( GLFW_KEY_F4 )) // TODO REMOVE (DEBUG ONLY)
Mixer::manager().setView(View::TRANSITION);
// else if (ImGui::IsKeyPressed( GLFW_KEY_F4 )) // TODO REMOVE (DEBUG ONLY)
// Mixer::manager().setView(View::TRANSITION);
else if (ImGui::IsKeyPressed( GLFW_KEY_F11 ))
Rendering::manager().mainWindow().toggleFullscreen();
else if (ImGui::IsKeyPressed( GLFW_KEY_F12 ))
@@ -311,8 +311,14 @@ void UserInterface::handleKeyboard()
if (ImGui::IsKeyPressed( GLFW_KEY_BACKSPACE ) || ImGui::IsKeyPressed( GLFW_KEY_DELETE ))
Mixer::manager().deleteSelection();
// button esc to toggle fullscreen
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE ))
Rendering::manager().mainWindow().setFullscreen(nullptr);
else if (ImGui::IsKeyPressed( GLFW_KEY_ESCAPE )) {
// if (Rendering::manager().mainWindow().isFullscreen())
// Rendering::manager().mainWindow().setFullscreen(nullptr);
// else if (!Mixer::selection().empty())
// Mixer::selection().clear();
// else
// Mixer::manager().setView(View::MIXING);
}
// button home to toggle menu
else if (ImGui::IsKeyPressed( GLFW_KEY_HOME ))
navigator.togglePannelMenu();
@@ -698,10 +704,9 @@ void UserInterface::handleScreenshot()
break;
case 3:
{
if ( Rendering::manager().currentScreenshot()->IsFull() ){
if ( Rendering::manager().currentScreenshot()->isFull() ){
std::string filename = SystemToolkit::full_filename( SystemToolkit::home_path(), SystemToolkit::date_time_string() + "_vmixcapture.png" );
Rendering::manager().currentScreenshot()->SaveFile( filename.c_str() );
Rendering::manager().currentScreenshot()->Clear();
Rendering::manager().currentScreenshot()->save( filename );
Log::Notify("Screenshot saved %s", filename.c_str() );
}
screenshot_step = 4;
@@ -1645,6 +1650,7 @@ void Navigator::RenderMainPannel()
bool fs = Rendering::manager().mainWindow().isFullscreen();
if ( ImGuiToolkit::IconToggle(3,15,2,15, &fs, tooltip ) ) {
Rendering::manager().mainWindow().toggleFullscreen();
ImGui::End();
return;
}