Files
projectm/src/libprojectM/Renderer/CopyTexture.cpp
Kai Blaschke e66b110665 Add texture burn-in support
Also use shader cache in texture copier and a transformation matrix for flipping instead of flags.
2025-12-07 17:23:00 +01:00

332 lines
8.8 KiB
C++

#include "CopyTexture.hpp"
namespace libprojectM {
namespace Renderer {
#ifdef USE_GLES
static constexpr char ShaderVersion[] = "#version 300 es\n\n";
#else
static constexpr char ShaderVersion[] = "#version 330\n\n";
#endif
static constexpr char CopyTextureVertexShader[] = R"(
precision mediump float;
layout(location = 0) in vec2 position;
layout(location = 2) in vec2 tex_coord;
out vec2 fragment_tex_coord;
uniform mat4 vertex_transformation;
void main() {
gl_Position = vec4(position, 0.0, 1.0) * vertex_transformation;
fragment_tex_coord = tex_coord;
}
)";
static constexpr char CopyTextureFragmentShader[] = R"(
precision mediump float;
in vec2 fragment_tex_coord;
uniform sampler2D texture_sampler;
out vec4 color;
void main(){
color = texture(texture_sampler, fragment_tex_coord);
}
)";
CopyTexture::CopyTexture()
: m_mesh(VertexBufferUsage::StaticDraw, false, true)
{
m_framebuffer.CreateColorAttachment(0, 0);
m_mesh.SetRenderPrimitiveType(Mesh::PrimitiveType::TriangleStrip);
m_mesh.SetVertexCount(4);
m_mesh.Vertices().Set({{-1.0, 1.0},
{1.0, 1.0},
{-1.0, -1.0},
{1.0, -1.0}});
m_mesh.UVs().Set({{0.0, 1.0},
{1.0, 1.0},
{0.0, 0.0},
{1.0, 0.0}});
m_mesh.Indices().Set({0, 1, 2, 3});
m_mesh.Update();
}
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr)
{
return;
}
// Just bind the texture and draw it to the currently bound buffer.
originalTexture->Bind(0);
Copy(shaderCache, flipVertical, flipHorizontal);
}
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
const std::shared_ptr<class Texture>& targetTexture,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr ||
originalTexture->Empty() ||
(targetTexture != nullptr && targetTexture->Empty()) ||
originalTexture == targetTexture)
{
return;
}
if (targetTexture == nullptr)
{
UpdateTextureSize(originalTexture->Width(), originalTexture->Height());
}
else
{
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 unflipped texture
originalTexture->Bind(0);
if (targetTexture)
{
internalTexture = m_framebuffer.GetColorAttachmentTexture(0, 0);
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
}
Copy(shaderCache, flipVertical, flipHorizontal);
// Rebind our internal texture.
if (targetTexture)
{
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(internalTexture);
}
Framebuffer::Unbind();
}
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
Framebuffer& framebuffer, int framebufferIndex,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr //
|| originalTexture->Empty() //
|| framebuffer.GetColorAttachmentTexture(framebufferIndex, 0) == nullptr //
|| framebuffer.GetColorAttachmentTexture(framebufferIndex, 0)->Empty())
{
return;
}
UpdateTextureSize(framebuffer.Width(), framebuffer.Height());
if (m_width == 0 || m_height == 0)
{
return;
}
m_framebuffer.Bind(0);
// Draw from unflipped texture
originalTexture->Bind(0);
Copy(shaderCache, flipVertical, flipHorizontal);
// Swap texture attachments
auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0);
framebuffer.RemoveColorAttachment(framebufferIndex, 0);
framebuffer.SetAttachment(framebufferIndex, 0, m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0));
m_framebuffer.RemoveColorAttachment(0, 0);
m_framebuffer.SetAttachment(0, 0, tempAttachment);
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>
{
return m_framebuffer.GetColorAttachmentTexture(0, 0);
}
void CopyTexture::UpdateTextureSize(int width, int height)
{
if (m_width == width &&
m_height == height)
{
return;
}
m_width = width;
m_height = height;
m_framebuffer.SetSize(m_width, m_height);
}
void CopyTexture::Copy(ShaderCache& shaderCache,
bool flipVertical, bool flipHorizontal)
{
glm::mat4x4 flipMatrix(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_mesh.Draw();
glBindTexture(GL_TEXTURE_2D, 0);
Mesh::Unbind();
Sampler::Unbind(0);
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 libprojectM