Let developers provide an optional filename for dumping main texture contents.

This commit is contained in:
Kai Blaschke
2023-01-25 14:15:58 +01:00
parent 45d05b71d0
commit 2b41fa256a
6 changed files with 64 additions and 39 deletions

View File

@ -32,13 +32,23 @@ extern "C" {
#endif
/**
* @brief Writes a .bmp framedump after rendering the next main texture, before shaders are applied.
* @brief Writes a .bmp main texture dump after rendering the next main texture, before shaders are applied.
*
* The image is written to the current working directory and is named "frame_texture_contents-[date].bmp".
* If no file name is given, the image is written to the current working directory
* and will be named named "frame_texture_contents-YYYY-mm-dd-HH:MM:SS-frame.bmp".
*
* Note this is the main texture contents, not the final rendering result. If the active preset
* uses a composite shader, the dumped image will not have it applied. The main texture is what is
* passed over to the next frame, the composite shader is only applied to the display framebuffer
* after updating the main texture.
*
* To capture the actual output, dump the contents of the main framebuffer after calling
* @a projectm_render_frame() on the application side.
*
* @param instance The projectM instance handle.
* @param output_file The filename to write the dump to or NULL.
*/
PROJECTM_EXPORT void projectm_write_debug_image_on_next_frame(projectm_handle instance);
PROJECTM_EXPORT void projectm_write_debug_image_on_next_frame(projectm_handle instance, const char* output_file);
#ifdef __cplusplus
} // extern "C"

View File

@ -115,8 +115,9 @@ void ProjectM::ResetTextures()
m_renderer->ResetTextures();
}
void ProjectM::DumpDebugImageOnNextFrame()
void ProjectM::DumpDebugImageOnNextFrame(const std::string& outputFile)
{
m_renderer->frameDumpOutputFile = outputFile;
m_renderer->writeNextFrameToFile = true;
}

View File

@ -208,7 +208,7 @@ public:
*
* The main texture is dumped after render pass 1, e.g. before shaders are applied.
*/
void DumpDebugImageOnNextFrame();
void DumpDebugImageOnNextFrame(const std::string& outputFile);
private:
void EvaluateSecondPreset();

View File

@ -388,9 +388,15 @@ auto projectm_pcm_add_uint8(projectm_handle instance, const uint8_t* samples, un
PcmAdd(instance, samples, count, channels);
}
auto projectm_write_debug_image_on_next_frame(projectm_handle instance) -> void
auto projectm_write_debug_image_on_next_frame(projectm_handle instance, const char* output_file) -> void
{
auto* projectMInstance = handle_to_instance(instance);
projectMInstance->DumpDebugImageOnNextFrame();
std::string outputFile;
if (output_file)
{
outputFile = output_file;
}
projectMInstance->DumpDebugImageOnNextFrame(outputFile);
}

View File

@ -204,11 +204,13 @@ void Renderer::RenderTouch(const Pipeline& pipeline, const PipelineContext& pipe
void Renderer::FinishPass1()
{
m_textureManager->updateMainTexture();
if(writeNextFrameToFile) {
debugWriteMainTextureToFile();
writeNextFrameToFile = false;
}
if (writeNextFrameToFile)
{
debugWriteMainTextureToFile();
writeNextFrameToFile = false;
frameDumpOutputFile = "";
}
}
void Renderer::Pass2(const Pipeline& pipeline, const PipelineContext& pipelineContext)
@ -530,36 +532,41 @@ void Renderer::touchDestroyAll()
m_waveformList.clear();
}
void Renderer::debugWriteMainTextureToFile() const {
GLuint fbo;
auto mainTexture = m_textureManager->getMainTexture();
void Renderer::debugWriteMainTextureToFile() const
{
std::string outputFile = frameDumpOutputFile;
if (outputFile.empty())
{
static constexpr char prefix[] = "frame_texture_contents-";
static constexpr auto prefixLen = sizeof(prefix) - 1;
constexpr auto fileNameMaxLength = 150;
constexpr auto fileExtensionLength = 4;
std::vector<char> fileNameBuffer;
fileNameBuffer.resize(fileNameMaxLength);
std::memcpy(fileNameBuffer.data(), prefix, prefixLen);
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
const auto bytesWritten = std::strftime(fileNameBuffer.data() + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm);
const auto offset = prefixLen + bytesWritten;
const auto spaceLeft = fileNameMaxLength - offset;
std::snprintf(fileNameBuffer.data() + offset, spaceLeft - fileExtensionLength, "-%d.bmp", totalframes);
outputFile = std::string(fileNameBuffer.data());
}
const auto safeWriteFile = [](const auto frameNumber, auto data, const auto width, const auto height) {
static const std::string prefix{"frame_texture_contents-"};
const auto prefixLen = prefix.size();
constexpr auto fileNameMaxLength = 150;
constexpr auto fileExtensionLength = 4;
char fileNameBuffer[fileNameMaxLength];
std::memcpy(fileNameBuffer, prefix.data(), prefixLen);
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
const auto bytesWritten = std::strftime(fileNameBuffer + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm);
const auto offset = prefixLen + bytesWritten;
const auto spaceLeft = fileNameMaxLength - offset;
std::snprintf(fileNameBuffer + offset, spaceLeft - fileExtensionLength, "%d.bmp", frameNumber);
stbi_write_bmp( fileNameBuffer, width, height, 4, data);
};
GLuint fbo;
auto mainTexture = m_textureManager->getMainTexture();
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0);
auto dataSize = mainTexture->width * mainTexture->height * 3;
GLubyte* pixels = new GLubyte[dataSize];
glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0);
auto dataSize = mainTexture->width * mainTexture->height * 4;
GLubyte* pixels = new GLubyte[dataSize];
glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &fbo);
safeWriteFile(totalframes, pixels, mainTexture->width, mainTexture->height);
delete[] pixels;
stbi_write_bmp(outputFile.c_str(), mainTexture->width, mainTexture->height, 3, pixels);
delete[] pixels;
}
void Renderer::UpdateContext(PipelineContext& context)

View File

@ -104,6 +104,7 @@ public:
bool correction{ true };
bool writeNextFrameToFile{ false };
std::string frameDumpOutputFile;
milliseconds lastTimeFPS{ nowMilliseconds() };
milliseconds currentTimeFPS{ nowMilliseconds() };