From 85e190835ffb40b12e6e283c1db2b95e4457a97e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 30 Jan 2026 22:22:52 -0500 Subject: [PATCH] Use a `Texture`. --- Outputs/OpenGL/Primitives/Texture.cpp | 27 ++++++- Outputs/OpenGL/Primitives/Texture.hpp | 17 +++- Outputs/OpenGL/ScanTarget.cpp | 112 ++++++++++++-------------- Outputs/OpenGL/ScanTarget.hpp | 18 +++-- 4 files changed, 99 insertions(+), 75 deletions(-) diff --git a/Outputs/OpenGL/Primitives/Texture.cpp b/Outputs/OpenGL/Primitives/Texture.cpp index 3840b88ed..4ec64c434 100644 --- a/Outputs/OpenGL/Primitives/Texture.cpp +++ b/Outputs/OpenGL/Primitives/Texture.cpp @@ -10,7 +10,30 @@ using namespace Outputs::Display::OpenGL; +namespace { +constexpr GLint internal_format_for_depth(const std::size_t depth) { + switch(depth) { + default: return GL_FALSE; + case 1: return GL_R8UI; + case 2: return GL_RG8UI; + case 3: return GL_RGB8UI; + case 4: return GL_RGBA8UI; + } +} + +constexpr GLenum format_for_depth(const std::size_t depth) { + switch(depth) { + default: return GL_FALSE; + case 1: return GL_RED_INTEGER; + case 2: return GL_RG_INTEGER; + case 3: return GL_RGB_INTEGER; + case 4: return GL_RGBA_INTEGER; + } +} +} + Texture::Texture( + const size_t channels, const GLenum texture_unit, const GLint mag_filter, const GLint min_filter, @@ -29,11 +52,11 @@ Texture::Texture( glTexImage2D, GL_TEXTURE_2D, 0, - GL_RGBA, + internal_format_for_depth(channels), GLsizei(width_), GLsizei(height_), 0, - GL_RGBA, + format_for_depth(channels), GL_UNSIGNED_BYTE, nullptr ); diff --git a/Outputs/OpenGL/Primitives/Texture.hpp b/Outputs/OpenGL/Primitives/Texture.hpp index 45b313d03..3f4951da7 100644 --- a/Outputs/OpenGL/Primitives/Texture.hpp +++ b/Outputs/OpenGL/Primitives/Texture.hpp @@ -15,12 +15,19 @@ namespace Outputs::Display::OpenGL { /*! Holds a texture of size @c width and @c height, which is bound to @c texture_unit. - Textures are always four-channel with byte values per channel. Both clamp directions are set to `GL_CLAMP_TO_EDGE`. - The magnification and minification filters as specified are attached. + Textures are always a single byte per channel. Both clamp directions are set to `GL_CLAMP_TO_EDGE`. + The magnification and minification filters are as specified. */ class Texture { public: - Texture(GLenum texture_unit, GLint mag_filter, GLint min_filter, GLsizei width, GLsizei height); + Texture( + size_t channels, + GLenum texture_unit, + GLint mag_filter, + GLint min_filter, + GLsizei width, + GLsizei height + ); ~Texture(); Texture() = default; @@ -35,6 +42,10 @@ public: */ void bind(); + bool empty() const { + return texture_ == 0; + } + private: GLenum texture_unit_ = GL_TEXTURE0; GLuint texture_ = 0; diff --git a/Outputs/OpenGL/ScanTarget.cpp b/Outputs/OpenGL/ScanTarget.cpp index bfbfcba71..f7cda7c49 100644 --- a/Outputs/OpenGL/ScanTarget.cpp +++ b/Outputs/OpenGL/ScanTarget.cpp @@ -99,8 +99,6 @@ ScanTarget::ScanTarget(const API api, const GLuint target_framebuffer, const flo // and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client // write straight into it. - test_gl(glGenTextures, 1, &write_area_texture_name_); - test_gl(glBlendFunc, GL_SRC_ALPHA, GL_CONSTANT_COLOR); test_gl(glBlendColor, 0.4f, 0.4f, 0.4f, 1.0f); @@ -109,53 +107,52 @@ ScanTarget::ScanTarget(const API api, const GLuint target_framebuffer, const flo // TEST CODE. NOCOMMIT. - const auto buffer_width = FilterGenerator::SuggestedBufferWidth; - const float sample_multiplier = - FilterGenerator::suggested_sample_multiplier(227.5f, 1320); - - VertexArray va(scan_buffer_); - for(auto &pair: { - std::make_pair(InputDataType::Luminance1, DisplayType::CompositeColour), - std::make_pair(InputDataType::Luminance8, DisplayType::CompositeColour), - std::make_pair(InputDataType::PhaseLinkedLuminance8, DisplayType::CompositeColour), - - std::make_pair(InputDataType::Luminance8Phase8, DisplayType::SVideo), - std::make_pair(InputDataType::Luminance8Phase8, DisplayType::CompositeColour), - - std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::RGB), - std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::SVideo), - std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::CompositeColour), - - std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::RGB), - std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::SVideo), - std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::CompositeColour), - - std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::RGB), - std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::SVideo), - std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::CompositeColour), - - std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::RGB), - std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::SVideo), - std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::CompositeColour), - }) { - OpenGL::composition_shader( - api, - pair.first, - pair.second, - ColourSpace::YIQ, - sample_multiplier, - BufferingScanTarget::WriteAreaWidth, BufferingScanTarget::WriteAreaHeight, - buffer_width, 2048, // TODO: substitute real composition buffer sizes. - va, - GL_TEXTURE0 - ).bind(); - } +// const auto buffer_width = FilterGenerator::SuggestedBufferWidth; +// const float sample_multiplier = +// FilterGenerator::suggested_sample_multiplier(227.5f, 1320); +// +// VertexArray va(scan_buffer_); +// for(auto &pair: { +// std::make_pair(InputDataType::Luminance1, DisplayType::CompositeColour), +// std::make_pair(InputDataType::Luminance8, DisplayType::CompositeColour), +// std::make_pair(InputDataType::PhaseLinkedLuminance8, DisplayType::CompositeColour), +// +// std::make_pair(InputDataType::Luminance8Phase8, DisplayType::SVideo), +// std::make_pair(InputDataType::Luminance8Phase8, DisplayType::CompositeColour), +// +// std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::RGB), +// std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::SVideo), +// std::make_pair(InputDataType::Red1Green1Blue1, DisplayType::CompositeColour), +// +// std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::RGB), +// std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::SVideo), +// std::make_pair(InputDataType::Red2Green2Blue2, DisplayType::CompositeColour), +// +// std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::RGB), +// std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::SVideo), +// std::make_pair(InputDataType::Red4Green4Blue4, DisplayType::CompositeColour), +// +// std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::RGB), +// std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::SVideo), +// std::make_pair(InputDataType::Red8Green8Blue8, DisplayType::CompositeColour), +// }) { +// OpenGL::composition_shader( +// api, +// pair.first, +// pair.second, +// ColourSpace::YIQ, +// sample_multiplier, +// BufferingScanTarget::WriteAreaWidth, BufferingScanTarget::WriteAreaHeight, +// buffer_width, 2048, // TODO: substitute real composition buffer sizes. +// va, +// GL_TEXTURE0 +// ).bind(); +// } } ScanTarget::~ScanTarget() { perform([&] { glDeleteBuffers(1, &scan_buffer_name_); - glDeleteTextures(1, &write_area_texture_name_); glDeleteVertexArrays(1, &scan_vertex_array_); }); } @@ -352,28 +349,19 @@ void ScanTarget::update(int, int output_height) { // Submit texture. if(area.start.write_area_x != area.end.write_area_x || area.start.write_area_y != area.end.write_area_y) { - test_gl(glActiveTexture, SourceDataTextureUnit); - test_gl(glBindTexture, GL_TEXTURE_2D, write_area_texture_name_); - // Create storage for the texture if it doesn't yet exist; this was deferred until here // because the pixel format wasn't initially known. - if(!texture_exists_) { - test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - test_gl(glTexImage2D, - GL_TEXTURE_2D, - 0, - internalFormatForDepth(write_area_data_size()), + if(source_texture_.empty()) { + source_texture_ = Texture( + write_area_data_size(), + SourceDataTextureUnit, + GL_NEAREST, + GL_NEAREST, WriteAreaWidth, - WriteAreaHeight, - 0, - formatForDepth(write_area_data_size()), - GL_UNSIGNED_BYTE, - nullptr); - texture_exists_ = true; + WriteAreaHeight + ); } + source_texture_.bind(); if(area.end.write_area_y >= area.start.write_area_y) { // Submit the direct region from the submit pointer to the read pointer. diff --git a/Outputs/OpenGL/ScanTarget.hpp b/Outputs/OpenGL/ScanTarget.hpp index 8de32346d..acdde69a7 100644 --- a/Outputs/OpenGL/ScanTarget.hpp +++ b/Outputs/OpenGL/ScanTarget.hpp @@ -15,6 +15,7 @@ #include "API.hpp" #include "OpenGL.hpp" #include "Primitives/Rectangle.hpp" +#include "Primitives/Texture.hpp" #include "Primitives/TextureTarget.hpp" #include "Primitives/VertexArray.hpp" @@ -94,9 +95,6 @@ private: GLuint scan_buffer_name_ = 0, scan_vertex_array_ = 0; GLuint line_buffer_name_ = 0, line_vertex_array_ = 0; - GLuint write_area_texture_name_ = 0; - bool texture_exists_ = false; - // Receives scan target modals. std::optional existing_modals_; void setup_pipeline(); @@ -122,11 +120,6 @@ private: std::unique_ptr output_shader_; std::unique_ptr qam_separation_shader_; - VertexArray scans_; - TextureTarget composition_buffer_; - Shader composition_shader_; - Shader copy_shader_; - /*! Produces a shader that composes fragment of the input stream to a single buffer, normalising the data into one of four forms: RGB, 8-bit luminance, @@ -160,6 +153,15 @@ private: std::array scan_buffer_; std::array line_buffer_; std::array line_metadata_buffer_; + + // + // NEW PIPELINE. Starts here. + // + Texture source_texture_; + VertexArray scans_; + TextureTarget composition_buffer_; + Shader composition_shader_; + Shader copy_shader_; }; }