Add texture burn-in support

Also use shader cache in texture copier and a transformation matrix for flipping instead of flags.
This commit is contained in:
Kai Blaschke
2025-02-27 19:32:47 +01:00
parent af479c01d6
commit e66b110665
6 changed files with 232 additions and 40 deletions

View File

@ -278,6 +278,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
endif() endif()
message(STATUS " Use system GLM: ${ENABLE_SYSTEM_GLM}") message(STATUS " Use system GLM: ${ENABLE_SYSTEM_GLM}")
message(STATUS " Use system projectM-eval: ${ENABLE_SYSTEM_PROJECTM_EVAL}") message(STATUS " Use system projectM-eval: ${ENABLE_SYSTEM_PROJECTM_EVAL}")
if(ENABLE_SYSTEM_PROJECTM_EVAL)
message(STATUS " projectM-eval version: ${projectM-Eval_VERSION}")
endif()
message(STATUS " Link UI with shared lib: ${ENABLE_SHARED_LINKING}") message(STATUS " Link UI with shared lib: ${ENABLE_SHARED_LINKING}")
message(STATUS "") message(STATUS "")
message(STATUS "Targets and applications:") message(STATUS "Targets and applications:")

View File

@ -105,7 +105,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
} }
// y-flip the previous frame and assign the flipped texture as "main" // y-flip the previous frame and assign the flipped texture as "main"
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), nullptr, true, false); m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), nullptr, true, false);
m_state.mainTexture = m_flipTexture.Texture(); m_state.mainTexture = m_flipTexture.Texture();
// We now draw to the current framebuffer. // We now draw to the current framebuffer.
@ -146,7 +146,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
m_border.Draw(m_perFrameContext); m_border.Draw(m_perFrameContext);
// y-flip the image for final compositing again // y-flip the image for final compositing again
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0), nullptr, true, false); m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0), nullptr, true, false);
m_state.mainTexture = m_flipTexture.Texture(); m_state.mainTexture = m_flipTexture.Texture();
// We no longer need the previous frame image, use it to render the final composite. // We no longer need the previous frame image, use it to render the final composite.
@ -158,7 +158,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
if (!m_finalComposite.HasCompositeShader()) if (!m_finalComposite.HasCompositeShader())
{ {
// Flip texture again in "previous" framebuffer as old-school effects are still upside down. // Flip texture again in "previous" framebuffer as old-school effects are still upside down.
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), m_framebuffer, m_previousFrameBuffer, true, false); m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), m_framebuffer, m_previousFrameBuffer, true, false);
} }
// Swap framebuffer IDs for the next frame. // Swap framebuffer IDs for the next frame.
@ -178,7 +178,7 @@ void MilkdropPreset::DrawInitialImage(const std::shared_ptr<Renderer::Texture>&
m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY); m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY);
// Render to previous framebuffer, as this is the image used to draw the next frame on. // Render to previous framebuffer, as this is the image used to draw the next frame on.
m_flipTexture.Draw(image, m_framebuffer, m_previousFrameBuffer); m_flipTexture.Draw(*renderContext.shaderCache, image, m_framebuffer, m_previousFrameBuffer);
} }
void MilkdropPreset::BindFramebuffer() void MilkdropPreset::BindFramebuffer()

View File

@ -29,8 +29,8 @@
#include <Renderer/CopyTexture.hpp> #include <Renderer/CopyTexture.hpp>
#include <Renderer/PresetTransition.hpp> #include <Renderer/PresetTransition.hpp>
#include <Renderer/TextureManager.hpp>
#include <Renderer/ShaderCache.hpp> #include <Renderer/ShaderCache.hpp>
#include <Renderer/TextureManager.hpp>
#include <Renderer/TransitionShaderManager.hpp> #include <Renderer/TransitionShaderManager.hpp>
#include <UserSprites/SpriteManager.hpp> #include <UserSprites/SpriteManager.hpp>
@ -177,11 +177,11 @@ void ProjectM::RenderFrame(uint32_t targetFramebufferObject /*= 0*/)
} }
else else
{ {
m_textureCopier->Draw(m_activePreset->OutputTexture(), false, false); m_textureCopier->Draw(*renderContext.shaderCache, m_activePreset->OutputTexture(), false, false);
} }
// Draw user sprites // Draw user sprites
m_spriteManager->Draw(audioData, renderContext, targetFramebufferObject, { m_activePreset, m_transitioningPreset }); m_spriteManager->Draw(audioData, renderContext, targetFramebufferObject, {m_activePreset, m_transitioningPreset});
m_frameCount++; m_frameCount++;
m_previousFrameVolume = audioData.vol; m_previousFrameVolume = audioData.vol;
@ -312,6 +312,23 @@ auto ProjectM::UserSpriteIdentifiers() const -> std::vector<uint32_t>
return m_spriteManager->ActiveSpriteIdentifiers(); return m_spriteManager->ActiveSpriteIdentifiers();
} }
void ProjectM::BurnInTexture(uint32_t openGlTextureId, int left, int top, int width, int height)
{
if (m_activePreset)
{
m_activePreset->BindFramebuffer();
m_textureCopier->Draw(*m_shaderCache, openGlTextureId, m_windowWidth, m_windowHeight, left, top, width, height);
}
if (m_transitioningPreset)
{
m_transitioningPreset->BindFramebuffer();
m_textureCopier->Draw(*m_shaderCache, openGlTextureId, m_windowWidth, m_windowHeight, left, top, width, height);
}
Renderer::Framebuffer::Unbind();
}
void ProjectM::SetPresetLocked(bool locked) void ProjectM::SetPresetLocked(bool locked)
{ {
// ToDo: Add a preset switch timer separate from the display timer and reset to 0 when // ToDo: Add a preset switch timer separate from the display timer and reset to 0 when

View File

@ -246,6 +246,16 @@ public:
*/ */
auto UserSpriteIdentifiers() const -> std::vector<uint32_t>; auto UserSpriteIdentifiers() const -> std::vector<uint32_t>;
/**
* @brief Draws the given texture on the active preset's main texture to get a "burn-in" effect.
* @param openGlTextureId The OpenGL texture to draw onto the active preset(s).
* @param left Left coordinate in pixels on the destination texture.
* @param top Top coordinate in pixels on the destination texture.
* @param width Width of the final image on the destination texture in pixels, can be negative to flip it horizontally.
* @param height Height of the final image on the destination texture in pixels, can be negative to flip it vertically.
*/
void BurnInTexture(uint32_t openGlTextureId, int left, int top, int width, int height);
private: private:
void Initialize(); void Initialize();

View File

@ -17,19 +17,11 @@ layout(location = 2) in vec2 tex_coord;
out vec2 fragment_tex_coord; out vec2 fragment_tex_coord;
uniform ivec2 flip; uniform mat4 vertex_transformation;
void main() { void main() {
gl_Position = vec4(position, 0.0, 1.0); gl_Position = vec4(position, 0.0, 1.0) * vertex_transformation;
fragment_tex_coord = tex_coord; fragment_tex_coord = tex_coord;
if (flip.x > 0)
{
fragment_tex_coord.s = 1.0 - fragment_tex_coord.s;
}
if (flip.y > 0)
{
fragment_tex_coord.t = 1.0 - fragment_tex_coord.t;
}
} }
)"; )";
@ -53,13 +45,6 @@ CopyTexture::CopyTexture()
{ {
m_framebuffer.CreateColorAttachment(0, 0); m_framebuffer.CreateColorAttachment(0, 0);
std::string vertexShader(ShaderVersion);
std::string fragmentShader(ShaderVersion);
vertexShader.append(CopyTextureVertexShader);
fragmentShader.append(CopyTextureFragmentShader);
m_shader.CompileProgram(vertexShader, fragmentShader);
m_mesh.SetRenderPrimitiveType(Mesh::PrimitiveType::TriangleStrip); m_mesh.SetRenderPrimitiveType(Mesh::PrimitiveType::TriangleStrip);
m_mesh.SetVertexCount(4); m_mesh.SetVertexCount(4);
@ -78,7 +63,9 @@ CopyTexture::CopyTexture()
m_mesh.Update(); m_mesh.Update();
} }
void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, bool flipVertical, bool flipHorizontal) void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
bool flipVertical, bool flipHorizontal)
{ {
if (originalTexture == nullptr) if (originalTexture == nullptr)
{ {
@ -87,10 +74,12 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, bo
// Just bind the texture and draw it to the currently bound buffer. // Just bind the texture and draw it to the currently bound buffer.
originalTexture->Bind(0); originalTexture->Bind(0);
Copy(flipVertical, flipHorizontal); Copy(shaderCache, flipVertical, flipHorizontal);
} }
void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, const std::shared_ptr<class Texture>& targetTexture, void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
const std::shared_ptr<class Texture>& targetTexture,
bool flipVertical, bool flipHorizontal) bool flipVertical, bool flipHorizontal)
{ {
if (originalTexture == nullptr || if (originalTexture == nullptr ||
@ -128,7 +117,7 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, co
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture); m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
} }
Copy(flipVertical, flipHorizontal); Copy(shaderCache, flipVertical, flipHorizontal);
// Rebind our internal texture. // Rebind our internal texture.
if (targetTexture) if (targetTexture)
@ -139,7 +128,9 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, co
Framebuffer::Unbind(); Framebuffer::Unbind();
} }
void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex, void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
Framebuffer& framebuffer, int framebufferIndex,
bool flipVertical, bool flipHorizontal) bool flipVertical, bool flipHorizontal)
{ {
if (originalTexture == nullptr // if (originalTexture == nullptr //
@ -162,7 +153,7 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Fr
// Draw from unflipped texture // Draw from unflipped texture
originalTexture->Bind(0); originalTexture->Bind(0);
Copy(flipVertical, flipHorizontal); Copy(shaderCache, flipVertical, flipHorizontal);
// Swap texture attachments // Swap texture attachments
auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0); auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0);
@ -174,6 +165,74 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Fr
Framebuffer::Unbind(); Framebuffer::Unbind();
} }
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<struct Texture>& originalTexture,
const std::shared_ptr<struct Texture>& targetTexture,
int left, int top, int width, int height)
{
if (originalTexture == nullptr ||
originalTexture->Empty() ||
targetTexture == nullptr ||
targetTexture->Empty() ||
originalTexture == targetTexture)
{
return;
}
UpdateTextureSize(targetTexture->Width(), targetTexture->Height());
if (m_width == 0 || m_height == 0)
{
return;
}
std::shared_ptr<class Texture> internalTexture;
m_framebuffer.Bind(0);
// Draw from original texture
originalTexture->Bind(0);
internalTexture = m_framebuffer.GetColorAttachmentTexture(0, 0);
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
Copy(shaderCache, left, top, width, height);
// Rebind our internal texture.
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(internalTexture);
Framebuffer::Unbind();
}
void CopyTexture::Draw(ShaderCache& shaderCache,
GLuint originalTexture,
int viewportWidth, int viewportHeight,
int left, int top, int width, int height)
{
if (originalTexture == 0)
{
return;
}
if (viewportWidth == 0 || viewportHeight == 0)
{
return;
}
int oldWidth = m_width;
int oldHeight = m_height;
m_width = viewportWidth;
m_height = viewportHeight;
// Draw from original texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, originalTexture);
Copy(shaderCache, left, top, width, height);
m_width = oldWidth;
m_height = oldHeight;
}
auto CopyTexture::Texture() -> std::shared_ptr<class Texture> auto CopyTexture::Texture() -> std::shared_ptr<class Texture>
{ {
return m_framebuffer.GetColorAttachmentTexture(0, 0); return m_framebuffer.GetColorAttachmentTexture(0, 0);
@ -193,11 +252,18 @@ void CopyTexture::UpdateTextureSize(int width, int height)
m_framebuffer.SetSize(m_width, m_height); m_framebuffer.SetSize(m_width, m_height);
} }
void CopyTexture::Copy(bool flipVertical, bool flipHorizontal) void CopyTexture::Copy(ShaderCache& shaderCache,
bool flipVertical, bool flipHorizontal)
{ {
m_shader.Bind(); glm::mat4x4 flipMatrix(1.0);
m_shader.SetUniformInt("texture_sampler", 0);
m_shader.SetUniformInt2("flip", {flipHorizontal ? 1 : 0, flipVertical ? 1 : 0}); flipMatrix[0][0] = flipHorizontal ? -1.0 : 1.0;
flipMatrix[1][1] = flipVertical ? -1.0 : 1.0;
std::shared_ptr<Shader> shader = BindShader(shaderCache);
shader->SetUniformInt("texture_sampler", 0);
shader->SetUniformMat4x4("vertex_transformation", flipMatrix);
m_sampler.Bind(0); m_sampler.Bind(0);
@ -209,5 +275,57 @@ void CopyTexture::Copy(bool flipVertical, bool flipHorizontal)
Shader::Unbind(); Shader::Unbind();
} }
void CopyTexture::Copy(ShaderCache& shaderCache,
int left, int top, int width, int height)
{
glm::mat4x4 translationMatrix(1.0);
translationMatrix[0][0] = static_cast<float>(width) / static_cast<float>(m_width);
translationMatrix[1][1] = static_cast<float>(height) / static_cast<float>(m_height);
translationMatrix[3][0] = static_cast<float>(left) / static_cast<float>(m_width);
translationMatrix[3][1] = static_cast<float>(top) / static_cast<float>(m_height);
std::shared_ptr<Shader> shader = BindShader(shaderCache);
shader->SetUniformInt("texture_sampler", 0);
shader->SetUniformMat4x4("vertex_transformation", translationMatrix);
m_sampler.Bind(0);
m_mesh.Draw();
Mesh::Unbind();
Sampler::Unbind(0);
Shader::Unbind();
}
std::shared_ptr<Shader> CopyTexture::BindShader(ShaderCache& shaderCache)
{
auto shader = m_shader.lock();
if (!shader)
{
shader = shaderCache.Get("copy_texture");
}
if (!shader)
{
std::string vertexShader(ShaderVersion);
std::string fragmentShader(ShaderVersion);
vertexShader.append(CopyTextureVertexShader);
fragmentShader.append(CopyTextureFragmentShader);
shader = std::make_shared<Shader>();
shader->CompileProgram(vertexShader, fragmentShader);
m_shader = shader;
shaderCache.Insert("copy_texture", shader);
}
shader->Bind();
return shader;
}
} // namespace Renderer } // namespace Renderer
} // namespace libprojectM } // namespace libprojectM

View File

@ -2,7 +2,7 @@
#include "Renderer/Framebuffer.hpp" #include "Renderer/Framebuffer.hpp"
#include "Renderer/Mesh.hpp" #include "Renderer/Mesh.hpp"
#include "Renderer/Shader.hpp" #include "Renderer/ShaderCache.hpp"
namespace libprojectM { namespace libprojectM {
namespace Renderer { namespace Renderer {
@ -20,37 +20,76 @@ public:
/** /**
* @brief Copies the original texture into the currently bound framebuffer. * @brief Copies the original texture into the currently bound framebuffer.
* @param shaderCache The global shader cache instance.
* @param originalTexture The texture to be copied. * @param originalTexture The texture to be copied.
* @param flipVertical Flip image on the y-axis when copying. * @param flipVertical Flip image on the y-axis when copying.
* @param flipHorizontal Flip image on the x-axis when copying. * @param flipHorizontal Flip image on the x-axis when copying.
*/ */
void Draw(const std::shared_ptr<class Texture>& originalTexture, void Draw(ShaderCache& shaderCache,
const std::shared_ptr<Texture>& originalTexture,
bool flipVertical = false, bool flipHorizontal = false); bool flipVertical = false, bool flipHorizontal = false);
/** /**
* @brief Copies the original texture either into the object's internal framebuffer or a given target texture. * @brief Copies the original texture either into the object's internal framebuffer or a given target texture.
* The original and target textures must not be the same. * The original and target textures must not be the same.
* @param shaderCache The global shader cache instance.
* @param originalTexture The texture to be copied. * @param originalTexture The texture to be copied.
* @param targetTexture Optional target texture to draw onto. * @param targetTexture Optional target texture to draw onto.
* @param flipVertical Flip image on the y-axis when copying. * @param flipVertical Flip image on the y-axis when copying.
* @param flipHorizontal Flip image on the x-axis when copying. * @param flipHorizontal Flip image on the x-axis when copying.
*/ */
void Draw(const std::shared_ptr<class Texture>& originalTexture, const std::shared_ptr<class Texture>& targetTexture = {}, void Draw(ShaderCache& shaderCache,
const std::shared_ptr<Texture>& originalTexture,
const std::shared_ptr<Texture>& targetTexture = {},
bool flipVertical = false, bool flipHorizontal = false); bool flipVertical = false, bool flipHorizontal = false);
/** /**
* @brief Copies the texture bound the given framebuffer's first color attachment. * @brief Copies the texture bound the given framebuffer's first color attachment.
* This is done by drawing into a second framebuffer, then swapping the textures, so the original texture * This is done by drawing into a second framebuffer, then swapping the textures, so the original texture
* can be the current color attachment of targetFramebuffer. * can be the current color attachment of targetFramebuffer.
* @param shaderCache The global shader cache instance.
* @param originalTexture The texture to be copied. * @param originalTexture The texture to be copied.
* @param targetFramebuffer Optional target texture to draw onto. * @param targetFramebuffer Optional target texture to draw onto.
* @param framebufferIndex The index of the framebuffer to use. * @param framebufferIndex The index of the framebuffer to use.
* @param flipVertical Flip image on the y-axis when copying. * @param flipVertical Flip image on the y-axis when copying.
* @param flipHorizontal Flip image on the x-axis when copying. * @param flipHorizontal Flip image on the x-axis when copying.
*/ */
void Draw(const std::shared_ptr<class Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex, void Draw(ShaderCache& shaderCache,
const std::shared_ptr<Texture>& originalTexture,
Framebuffer& framebuffer, int framebufferIndex,
bool flipVertical = false, bool flipHorizontal = false); bool flipVertical = false, bool flipHorizontal = false);
/**
* @brief Draws the original texture onto the specified target texture, using the provided screen coordinates to position it.
* @param shaderCache The global shader cache instance.
* @param originalTexture The texture to be copied.
* @param targetTexture The target texture to draw onto.
* @param left Left offset on the target texture in screen coordinates.
* @param top Top offset on the target texture in screen coordinates.
* @param width Width on the target texture in screen coordinates. Use a negative value to flip vertically.
* @param height Height on the target texture in screen coordinates. Use a negative value to flip horizontally.
*/
void Draw(ShaderCache& shaderCache,
const std::shared_ptr<Texture>& originalTexture,
const std::shared_ptr<Texture>& targetTexture,
int left, int top, int width, int height);
/**
* @brief Draws a raw GL texture into the currently bound framebuffer, using the provided screen coordinates to position it.
* @param shaderCache The global shader cache instance.
* @param originalTexture The texture ID to be copied.
* @param viewportWidth The target surface width.
* @param viewportHeight The target surface height.
* @param left Left offset on the target texture in screen coordinates.
* @param top Top offset on the target texture in screen coordinates.
* @param width Width on the target texture in screen coordinates. Use a negative value to flip vertically.
* @param height Height on the target texture in screen coordinates. Use a negative value to flip horizontally.
*/
void Draw(ShaderCache& shaderCache,
GLuint originalTexture,
int viewportWidth, int viewportHeight,
int left, int top, int width, int height);
/** /**
* @brief Returns the flipped texture. * @brief Returns the flipped texture.
* *
@ -64,15 +103,20 @@ private:
*/ */
void UpdateTextureSize(int width, int height); void UpdateTextureSize(int width, int height);
void Copy(bool flipVertical, bool flipHorizontal); void Copy(ShaderCache& shaderCache,
bool flipVertical, bool flipHorizontal);
void Copy(ShaderCache& shaderCache,
int left, int top, int width, int height);
Mesh m_mesh; Mesh m_mesh;
Shader m_shader; //!< Simple textured shader std::weak_ptr<Shader> m_shader; //!< Simple textured shader
Framebuffer m_framebuffer{1}; //!< Framebuffer for drawing the flipped texture Framebuffer m_framebuffer{1}; //!< Framebuffer for drawing the flipped texture
Sampler m_sampler{GL_CLAMP_TO_EDGE, GL_NEAREST}; //!< Texture sampler settings Sampler m_sampler{GL_CLAMP_TO_EDGE, GL_NEAREST}; //!< Texture sampler settings
int m_width{}; //!< Last known framebuffer/texture width int m_width{}; //!< Last known framebuffer/texture width
int m_height{}; //!< Last known framebuffer/texture height int m_height{}; //!< Last known framebuffer/texture height
std::shared_ptr<Shader> BindShader(ShaderCache& shaderCache);
}; };
} // namespace Renderer } // namespace Renderer