mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-27 16:31:31 +00:00
Pushed responsibility for submitting texture contents up to the texture builder, simplifying the interface.
This commit is contained in:
parent
5c5e44874f
commit
6ac20e0066
@ -13,30 +13,6 @@
|
|||||||
#include "../../../SignalProcessing/FIRFilter.hpp"
|
#include "../../../SignalProcessing/FIRFilter.hpp"
|
||||||
#include "Shaders/OutputShader.hpp"
|
#include "Shaders/OutputShader.hpp"
|
||||||
|
|
||||||
static const GLint internalFormatForDepth(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const GLenum formatForDepth(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Range {
|
struct Range {
|
||||||
GLsizei location, length;
|
GLsizei location, length;
|
||||||
};
|
};
|
||||||
@ -78,8 +54,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
|||||||
_rgb_shader(nullptr),
|
_rgb_shader(nullptr),
|
||||||
_last_output_width(0),
|
_last_output_width(0),
|
||||||
_last_output_height(0),
|
_last_output_height(0),
|
||||||
_fence(nullptr),
|
_fence(nullptr)
|
||||||
_texture_builder(new TextureBuilder(buffer_depth))
|
|
||||||
{
|
{
|
||||||
_output_buffer.data.resize(OutputVertexBufferDataSize);
|
_output_buffer.data.resize(OutputVertexBufferDataSize);
|
||||||
_source_buffer.data.resize(SourceVertexBufferDataSize);
|
_source_buffer.data.resize(SourceVertexBufferDataSize);
|
||||||
@ -94,14 +69,8 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
|||||||
filteredTexture.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit));
|
filteredTexture.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit));
|
||||||
|
|
||||||
// create the surce texture
|
// create the surce texture
|
||||||
glGenTextures(1, &textureName);
|
|
||||||
glActiveTexture(source_data_texture_unit);
|
glActiveTexture(source_data_texture_unit);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureName);
|
_texture_builder.reset(new TextureBuilder(buffer_depth));
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_texture_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_texture_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, nullptr);
|
|
||||||
|
|
||||||
// create the output vertex array
|
// create the output vertex array
|
||||||
glGenVertexArrays(1, &output_vertex_array);
|
glGenVertexArrays(1, &output_vertex_array);
|
||||||
@ -122,7 +91,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
|||||||
|
|
||||||
OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
OpenGLOutputBuilder::~OpenGLOutputBuilder()
|
||||||
{
|
{
|
||||||
glDeleteTextures(1, &textureName);
|
|
||||||
glDeleteBuffers(1, &output_array_buffer);
|
glDeleteBuffers(1, &output_array_buffer);
|
||||||
glDeleteBuffers(1, &source_array_buffer);
|
glDeleteBuffers(1, &source_array_buffer);
|
||||||
glDeleteVertexArrays(1, &output_vertex_array);
|
glDeleteVertexArrays(1, &output_vertex_array);
|
||||||
@ -190,16 +158,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
GLsizei submitted_source_data = submitArrayData(source_array_buffer, _source_buffer.data.data(), &_source_buffer.pointer);
|
GLsizei submitted_source_data = submitArrayData(source_array_buffer, _source_buffer.data.data(), &_source_buffer.pointer);
|
||||||
|
|
||||||
// upload new source pixels, if any
|
// upload new source pixels, if any
|
||||||
uint16_t completed_texture_y = _texture_builder->get_and_finalise_current_line();
|
|
||||||
if(completed_texture_y)
|
|
||||||
{
|
|
||||||
glActiveTexture(source_data_texture_unit);
|
glActiveTexture(source_data_texture_unit);
|
||||||
glTexSubImage2D( GL_TEXTURE_2D, 0,
|
_texture_builder->submit();
|
||||||
0, 0,
|
|
||||||
InputBufferBuilderWidth, completed_texture_y,
|
|
||||||
formatForDepth(_texture_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE,
|
|
||||||
_texture_builder->get_image_pointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
// buffer usage restart from 0 for the next time around
|
// buffer usage restart from 0 for the next time around
|
||||||
_composite_src_output_y = 0;
|
_composite_src_output_y = 0;
|
||||||
|
@ -80,7 +80,7 @@ class OpenGLOutputBuilder {
|
|||||||
|
|
||||||
unsigned int _last_output_width, _last_output_height;
|
unsigned int _last_output_width, _last_output_height;
|
||||||
|
|
||||||
GLuint textureName, shadowMaskTextureName;
|
GLuint shadowMaskTextureName;
|
||||||
|
|
||||||
GLuint defaultFramebuffer;
|
GLuint defaultFramebuffer;
|
||||||
|
|
||||||
|
@ -8,102 +8,134 @@
|
|||||||
|
|
||||||
#include "TextureBuilder.hpp"
|
#include "TextureBuilder.hpp"
|
||||||
#include "CRTOpenGL.hpp"
|
#include "CRTOpenGL.hpp"
|
||||||
|
#include "OpenGL.hpp"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
using namespace Outputs::CRT;
|
using namespace Outputs::CRT;
|
||||||
|
|
||||||
TextureBuilder::TextureBuilder(size_t bytes_per_pixel) :
|
static const GLint internalFormatForDepth(size_t depth)
|
||||||
_bytes_per_pixel(bytes_per_pixel),
|
|
||||||
_next_write_x_position(0),
|
|
||||||
_next_write_y_position(0)
|
|
||||||
{
|
{
|
||||||
_image.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GLenum formatForDepth(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureBuilder::TextureBuilder(size_t bytes_per_pixel) :
|
||||||
|
bytes_per_pixel_(bytes_per_pixel),
|
||||||
|
next_write_x_position_(0),
|
||||||
|
next_write_y_position_(0)
|
||||||
|
{
|
||||||
|
image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
|
||||||
|
glGenTextures(1, &texture_name_);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture_name_);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureBuilder::~TextureBuilder()
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &texture_name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *TextureBuilder::allocate_write_area(size_t required_length)
|
uint8_t *TextureBuilder::allocate_write_area(size_t required_length)
|
||||||
{
|
{
|
||||||
if(_next_write_y_position != InputBufferBuilderHeight)
|
if(next_write_y_position_ != InputBufferBuilderHeight)
|
||||||
{
|
{
|
||||||
_last_allocation_amount = required_length;
|
last_allocation_amount_ = required_length;
|
||||||
|
|
||||||
if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
|
if(next_write_x_position_ + required_length + 2 > InputBufferBuilderWidth)
|
||||||
{
|
{
|
||||||
_next_write_x_position = 0;
|
next_write_x_position_ = 0;
|
||||||
_next_write_y_position++;
|
next_write_y_position_++;
|
||||||
|
|
||||||
if(_next_write_y_position == InputBufferBuilderHeight)
|
if(next_write_y_position_ == InputBufferBuilderHeight)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_x_position = _next_write_x_position + 1;
|
write_x_position_ = next_write_x_position_ + 1;
|
||||||
_write_y_position = _next_write_y_position;
|
write_y_position_ = next_write_y_position_;
|
||||||
_write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position;
|
write_target_pointer_ = (write_y_position_ * InputBufferBuilderWidth) + write_x_position_;
|
||||||
_next_write_x_position += required_length + 2;
|
next_write_x_position_ += required_length + 2;
|
||||||
}
|
}
|
||||||
else return nullptr;
|
else return nullptr;
|
||||||
|
|
||||||
return &_image[_write_target_pointer * _bytes_per_pixel];
|
return &image_[write_target_pointer_ * bytes_per_pixel_];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextureBuilder::is_full()
|
bool TextureBuilder::is_full()
|
||||||
{
|
{
|
||||||
return (_next_write_y_position == InputBufferBuilderHeight);
|
return (next_write_y_position_ == InputBufferBuilderHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length)
|
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length)
|
||||||
{
|
{
|
||||||
if(_next_write_y_position == InputBufferBuilderHeight) return;
|
if(next_write_y_position_ == InputBufferBuilderHeight) return;
|
||||||
|
|
||||||
uint8_t *const image_pointer = _image.data();
|
uint8_t *const image_pointer = image_.data();
|
||||||
|
|
||||||
// correct if the writing cursor was reset while a client was writing
|
// correct if the writing cursor was reset while a client was writing
|
||||||
if(_next_write_x_position == 0 && _next_write_y_position == 0)
|
if(next_write_x_position_ == 0 && next_write_y_position_ == 0)
|
||||||
{
|
{
|
||||||
memmove(&image_pointer[_bytes_per_pixel], &image_pointer[_write_target_pointer * _bytes_per_pixel], actual_length * _bytes_per_pixel);
|
memmove(&image_pointer[bytes_per_pixel_], &image_pointer[write_target_pointer_ * bytes_per_pixel_], actual_length * bytes_per_pixel_);
|
||||||
_write_target_pointer = 1;
|
write_target_pointer_ = 1;
|
||||||
_last_allocation_amount = actual_length;
|
last_allocation_amount_ = actual_length;
|
||||||
_next_write_x_position = (uint16_t)(actual_length + 2);
|
next_write_x_position_ = (uint16_t)(actual_length + 2);
|
||||||
_write_x_position = 1;
|
write_x_position_ = 1;
|
||||||
_write_y_position = 0;
|
write_y_position_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// book end the allocation with duplicates of the first and last pixel, to protect
|
// book end the allocation with duplicates of the first and last pixel, to protect
|
||||||
// against rounding errors when this run is drawn
|
// against rounding errors when this run is drawn
|
||||||
memcpy( &image_pointer[(_write_target_pointer - 1) * _bytes_per_pixel],
|
memcpy( &image_pointer[(write_target_pointer_ - 1) * bytes_per_pixel_],
|
||||||
&image_pointer[_write_target_pointer * _bytes_per_pixel],
|
&image_pointer[write_target_pointer_ * bytes_per_pixel_],
|
||||||
_bytes_per_pixel);
|
bytes_per_pixel_);
|
||||||
|
|
||||||
memcpy( &image_pointer[(_write_target_pointer + actual_length) * _bytes_per_pixel],
|
memcpy( &image_pointer[(write_target_pointer_ + actual_length) * bytes_per_pixel_],
|
||||||
&image_pointer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel],
|
&image_pointer[(write_target_pointer_ + actual_length - 1) * bytes_per_pixel_],
|
||||||
_bytes_per_pixel);
|
bytes_per_pixel_);
|
||||||
|
|
||||||
// return any allocated length that wasn't actually used to the available pool
|
// return any allocated length that wasn't actually used to the available pool
|
||||||
_next_write_x_position -= (_last_allocation_amount - actual_length);
|
next_write_x_position_ -= (last_allocation_amount_ - actual_length);
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *TextureBuilder::get_image_pointer()
|
|
||||||
{
|
|
||||||
return _image.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t TextureBuilder::get_and_finalise_current_line()
|
|
||||||
{
|
|
||||||
uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0);
|
|
||||||
_next_write_x_position = _next_write_y_position = 0;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t TextureBuilder::get_last_write_x_position()
|
uint16_t TextureBuilder::get_last_write_x_position()
|
||||||
{
|
{
|
||||||
return _write_x_position;
|
return write_x_position_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t TextureBuilder::get_last_write_y_position()
|
uint16_t TextureBuilder::get_last_write_y_position()
|
||||||
{
|
{
|
||||||
return _write_y_position;
|
return write_y_position_;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t TextureBuilder::get_bytes_per_pixel()
|
void TextureBuilder::submit()
|
||||||
{
|
{
|
||||||
return _bytes_per_pixel;
|
uint16_t height = write_y_position_ + (next_write_x_position_ ? 1 : 0);
|
||||||
|
next_write_x_position_ = next_write_y_position_ = 0;
|
||||||
|
|
||||||
|
glTexSubImage2D( GL_TEXTURE_2D, 0,
|
||||||
|
0, 0,
|
||||||
|
InputBufferBuilderWidth, height,
|
||||||
|
formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE,
|
||||||
|
image_.data());
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,10 @@ namespace CRT {
|
|||||||
*/
|
*/
|
||||||
class TextureBuilder {
|
class TextureBuilder {
|
||||||
public:
|
public:
|
||||||
/// Constructs an instance of InputTextureBuilder that contains a texture of colour depth @c bytes_per_pixel.
|
/// Constructs an instance of InputTextureBuilder that contains a texture of colour depth @c bytes_per_pixel;
|
||||||
|
/// this creates a new texture and binds it to the current active texture unit.
|
||||||
TextureBuilder(size_t bytes_per_pixel);
|
TextureBuilder(size_t bytes_per_pixel);
|
||||||
|
virtual ~TextureBuilder();
|
||||||
|
|
||||||
/// Finds the first available space of at least @c required_length pixels in size. Calls must be paired off
|
/// Finds the first available space of at least @c required_length pixels in size. Calls must be paired off
|
||||||
/// with calls to @c reduce_previous_allocation_to.
|
/// with calls to @c reduce_previous_allocation_to.
|
||||||
@ -38,42 +40,36 @@ class TextureBuilder {
|
|||||||
/// and indicates that its actual final size was @c actual_length.
|
/// and indicates that its actual final size was @c actual_length.
|
||||||
void reduce_previous_allocation_to(size_t actual_length);
|
void reduce_previous_allocation_to(size_t actual_length);
|
||||||
|
|
||||||
/// @returns the row that was the final one to receive data; also resets the builder to restart filling of
|
|
||||||
/// the texture from row 0.
|
|
||||||
uint16_t get_and_finalise_current_line();
|
|
||||||
|
|
||||||
/// @returns a pointer to the image data for this texture.
|
|
||||||
uint8_t *get_image_pointer();
|
|
||||||
|
|
||||||
/// @returns the start column for the most recent allocated write area.
|
/// @returns the start column for the most recent allocated write area.
|
||||||
uint16_t get_last_write_x_position();
|
uint16_t get_last_write_x_position();
|
||||||
|
|
||||||
/// @returns the row of the most recent allocated write area.
|
/// @returns the row of the most recent allocated write area.
|
||||||
uint16_t get_last_write_y_position();
|
uint16_t get_last_write_y_position();
|
||||||
|
|
||||||
/// @returns the number of bytes per pixel as supplied to the constructor.
|
|
||||||
size_t get_bytes_per_pixel();
|
|
||||||
|
|
||||||
/// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture
|
/// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture
|
||||||
/// being full; @c false if calls may succeed.
|
/// being full; @c false if calls may succeed.
|
||||||
bool is_full();
|
bool is_full();
|
||||||
|
|
||||||
|
/// Updates the currently-bound texture with all new data provided since the last @c submit.
|
||||||
|
void submit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// where pixel data will be put to the next time a write is requested
|
// where pixel data will be put to the next time a write is requested
|
||||||
uint16_t _next_write_x_position, _next_write_y_position;
|
uint16_t next_write_x_position_, next_write_y_position_;
|
||||||
|
|
||||||
// the most recent position returned for pixel data writing
|
// the most recent position returned for pixel data writing
|
||||||
uint16_t _write_x_position, _write_y_position;
|
uint16_t write_x_position_, write_y_position_;
|
||||||
|
|
||||||
// details of the most recent allocation
|
// details of the most recent allocation
|
||||||
size_t _write_target_pointer;
|
size_t write_target_pointer_;
|
||||||
size_t _last_allocation_amount;
|
size_t last_allocation_amount_;
|
||||||
|
|
||||||
// the buffer size
|
// the buffer size
|
||||||
size_t _bytes_per_pixel;
|
size_t bytes_per_pixel_;
|
||||||
|
|
||||||
// the buffer
|
// the buffer
|
||||||
std::vector<uint8_t> _image;
|
std::vector<uint8_t> image_;
|
||||||
|
GLuint texture_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user