1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-28 09:54:49 +00:00

Attempts to put in better OpenGL safety rails.

This commit is contained in:
Thomas Harte 2019-02-18 10:29:40 -05:00
parent 6d76b7cd94
commit 8ecf885629
5 changed files with 172 additions and 148 deletions

View File

@ -22,4 +22,22 @@
#include <GL/gl.h>
#endif
// To consider: might it be smarter to switch and log on error,
// rather than raising an exception? They're conventionally
// something you're permitted to ignore.
//
// (and, from that indecision, hence the pointless decision
// on whether to use an assert based on NDEBUG)
#ifndef NDEBUG
#define test_gl_error() assert(!glGetError());
#else
#define test_gl_error() while(false) {}
#endif
#ifndef NDEBUG
#define test_gl(command, ...) do { command(__VA_ARGS__); test_gl_error(); } while(false);
#else
#define test_gl(command, ...) command(__VA_ARGS__)
#endif
#endif /* OpenGL_h */

View File

@ -33,16 +33,16 @@ Rectangle::Rectangle(float x, float y, float width, float height):
){
pixel_shader_.bind();
glGenVertexArrays(1, &drawing_vertex_array_);
glGenBuffers(1, &drawing_array_buffer_);
test_gl(glGenVertexArrays, 1, &drawing_vertex_array_);
test_gl(glGenBuffers, 1, &drawing_array_buffer_);
glBindVertexArray(drawing_vertex_array_);
glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_);
test_gl(glBindVertexArray, drawing_vertex_array_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, drawing_array_buffer_);
GLint position_attribute = pixel_shader_.get_attrib_location("position");
glEnableVertexAttribArray(static_cast<GLuint>(position_attribute));
test_gl(glEnableVertexAttribArray, GLuint(position_attribute));
glVertexAttribPointer(
test_gl(glVertexAttribPointer,
(GLuint)position_attribute,
2,
GL_FLOAT,
@ -61,14 +61,14 @@ Rectangle::Rectangle(float x, float y, float width, float height):
buffer[6] = x + width; buffer[7] = y + height;
// Upload buffer.
glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_);
glBufferData(GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, drawing_array_buffer_);
test_gl(glBufferData, GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW);
}
void Rectangle::draw(float red, float green, float blue) {
pixel_shader_.bind();
glUniform4f(colour_uniform_, red, green, blue, 1.0);
test_gl(glUniform4f, colour_uniform_, red, green, blue, 1.0);
glBindVertexArray(drawing_vertex_array_);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
test_gl(glBindVertexArray, drawing_vertex_array_);
test_gl(glDrawArrays, GL_TRIANGLE_STRIP, 0, 4);
}

View File

@ -21,18 +21,18 @@ namespace {
GLuint Shader::compile_shader(const std::string &source, GLenum type) {
GLuint shader = glCreateShader(type);
const char *c_str = source.c_str();
glShaderSource(shader, 1, &c_str, NULL);
glCompileShader(shader);
test_gl(glShaderSource, shader, 1, &c_str, NULL);
test_gl(glCompileShader, shader);
#ifndef NDEBUG
GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
test_gl(glGetShaderiv, shader, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE) {
GLint logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
test_gl(glGetShaderiv, shader, GL_INFO_LOG_LENGTH, &logLength);
if(logLength > 0) {
GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
glGetShaderInfoLog(shader, logLength, &logLength, log);
GLchar *log = new GLchar[std::size_t(logLength)];
test_gl(glGetShaderInfoLog, shader, logLength, &logLength, log);
LOG("Compile log:\n" << log);
delete[] log;
}
@ -63,11 +63,11 @@ void Shader::init(const std::string &vertex_shader, const std::string &fragment_
const GLuint vertex = compile_shader(vertex_shader, GL_VERTEX_SHADER);
const GLuint fragment = compile_shader(fragment_shader, GL_FRAGMENT_SHADER);
glAttachShader(shader_program_, vertex);
glAttachShader(shader_program_, fragment);
test_gl(glAttachShader, shader_program_, vertex);
test_gl(glAttachShader, shader_program_, fragment);
for(const auto &binding : attribute_bindings) {
glBindAttribLocation(shader_program_, binding.index, binding.name.c_str());
test_gl(glBindAttribLocation, shader_program_, binding.index, binding.name.c_str());
#ifndef NDEBUG
const auto error = glGetError();
switch(error) {
@ -85,20 +85,20 @@ void Shader::init(const std::string &vertex_shader, const std::string &fragment_
#endif
}
glLinkProgram(shader_program_);
test_gl(glLinkProgram, shader_program_);
#ifndef NDEBUG
GLint logLength;
glGetProgramiv(shader_program_, GL_INFO_LOG_LENGTH, &logLength);
test_gl(glGetProgramiv, shader_program_, GL_INFO_LOG_LENGTH, &logLength);
if(logLength > 0) {
GLchar *log = new GLchar[static_cast<std::size_t>(logLength)];
glGetProgramInfoLog(shader_program_, logLength, &logLength, log);
GLchar *log = new GLchar[std::size_t(logLength)];
test_gl(glGetProgramInfoLog, shader_program_, logLength, &logLength, log);
LOG("Link log:\n" << log);
delete[] log;
}
GLint didLink = 0;
glGetProgramiv(shader_program_, GL_LINK_STATUS, &didLink);
test_gl(glGetProgramiv, shader_program_, GL_LINK_STATUS, &didLink);
if(didLink == GL_FALSE) {
throw ProgramLinkageError;
}
@ -112,7 +112,7 @@ Shader::~Shader() {
void Shader::bind() const {
// if(bound_shader != this) {
glUseProgram(shader_program_);
test_gl(glUseProgram, shader_program_);
// bound_shader = this;
// }
flush_functions();
@ -120,7 +120,7 @@ void Shader::bind() const {
void Shader::unbind() {
// bound_shader = nullptr;
glUseProgram(0);
test_gl(glUseProgram, 0);
}
GLint Shader::get_attrib_location(const std::string &name) const {
@ -134,9 +134,9 @@ GLint Shader::get_uniform_location(const std::string &name) const {
void Shader::enable_vertex_attribute_with_pointer(const std::string &name, GLint size, GLenum type, GLboolean normalised, GLsizei stride, const GLvoid *pointer, GLuint divisor) {
GLint location = get_attrib_location(name);
if(location >= 0) {
glEnableVertexAttribArray((GLuint)location);
glVertexAttribPointer((GLuint)location, size, type, normalised, stride, pointer);
glVertexAttribDivisor((GLuint)location, divisor);
test_gl(glEnableVertexAttribArray, GLuint(location));
test_gl(glVertexAttribPointer, GLuint(location), size, type, normalised, stride, pointer);
test_gl(glVertexAttribDivisor, GLuint(location), divisor);
} else {
LOG("Couldn't enable vertex attribute " << name);
}
@ -227,7 +227,7 @@ void Shader::set_uniform(const std::string &name, GLuint value1, GLuint value2,
}
void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLint *values) {
std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
std::size_t number_of_values = std::size_t(count) * std::size_t(size);
std::vector<GLint> values_copy(values, values + number_of_values);
enqueue_function([name, size, count, values_copy, this] {
@ -241,7 +241,7 @@ void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, con
}
void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLfloat *values) {
std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
std::size_t number_of_values = std::size_t(count) * std::size_t(size);
std::vector<GLfloat> values_copy(values, values + number_of_values);
enqueue_function([name, size, count, values_copy, this] {
@ -255,7 +255,7 @@ void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, con
}
void Shader::set_uniform(const std::string &name, GLint size, GLsizei count, const GLuint *values) {
std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size);
std::size_t number_of_values = std::size_t(count) * std::size_t(size);
std::vector<GLuint> values_copy(values, values + number_of_values);
enqueue_function([name, size, count, values_copy, this] {
@ -273,7 +273,7 @@ void Shader::set_uniform_matrix(const std::string &name, GLint size, bool transp
}
void Shader::set_uniform_matrix(const std::string &name, GLint size, GLsizei count, bool transpose, const GLfloat *values) {
std::size_t number_of_values = static_cast<std::size_t>(count) * static_cast<std::size_t>(size) * static_cast<std::size_t>(size);
std::size_t number_of_values = std::size_t(count) * std::size_t(size) * std::size_t(size);
std::vector<GLfloat> values_copy(values, values + number_of_values);
enqueue_function([name, size, count, transpose, values_copy, this] {

View File

@ -18,8 +18,8 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit,
height_(height),
texture_unit_(texture_unit) {
// Generate and bind a frame buffer.
glGenFramebuffers(1, &framebuffer_);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
test_gl(glGenFramebuffers, 1, &framebuffer_);
test_gl(glBindFramebuffer, GL_FRAMEBUFFER, framebuffer_);
// Round the width and height up to the next power of two.
expanded_width_ = 1;
@ -28,23 +28,23 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit,
while(expanded_height_ < height) expanded_height_ <<= 1;
// Generate a texture and bind it to the nominated texture unit.
glGenTextures(1, &texture_);
test_gl(glGenTextures, 1, &texture_);
bind_texture();
// Set dimensions and set the user-supplied magnification filter.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(expanded_width_), static_cast<GLsizei>(expanded_height_), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
test_gl(glTexImage2D, GL_TEXTURE_2D, 0, GL_RGBA, GLsizei(expanded_width_), GLsizei(expanded_height_), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
test_gl(glTexParameteri, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// Set the texture as colour attachment 0 on the frame buffer.
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
test_gl(glFramebufferTexture2D, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0);
// Also add a stencil buffer if requested.
if(has_stencil_buffer) {
glGenRenderbuffers(1, &renderbuffer_);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer_);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX1, expanded_width_, expanded_height_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer_);
test_gl(glGenRenderbuffers, 1, &renderbuffer_);
test_gl(glBindRenderbuffer, GL_RENDERBUFFER, renderbuffer_);
test_gl(glRenderbufferStorage, GL_RENDERBUFFER, GL_STENCIL_INDEX1, expanded_width_, expanded_height_);
test_gl(glFramebufferRenderbuffer, GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer_);
}
// Check for successful construction.
@ -61,13 +61,13 @@ TextureTarget::~TextureTarget() {
}
void TextureTarget::bind_framebuffer() {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
glViewport(0, 0, width_, height_);
test_gl(glBindFramebuffer, GL_FRAMEBUFFER, framebuffer_);
test_gl(glViewport, 0, 0, width_, height_);
}
void TextureTarget::bind_texture() const {
glActiveTexture(texture_unit_);
glBindTexture(GL_TEXTURE_2D, texture_);
test_gl(glActiveTexture, texture_unit_);
test_gl(glBindTexture, GL_TEXTURE_2D, texture_);
}
void TextureTarget::draw(float aspect_ratio, float colour_threshold) const {
@ -102,24 +102,24 @@ void TextureTarget::draw(float aspect_ratio, float colour_threshold) const {
pixel_shader_.reset(new Shader(vertex_shader, fragment_shader));
pixel_shader_->bind();
glGenVertexArrays(1, &drawing_vertex_array_);
glGenBuffers(1, &drawing_array_buffer_);
test_gl(glGenVertexArrays, 1, &drawing_vertex_array_);
test_gl(glGenBuffers, 1, &drawing_array_buffer_);
glBindVertexArray(drawing_vertex_array_);
glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_);
test_gl(glBindVertexArray, drawing_vertex_array_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, drawing_array_buffer_);
const GLint position_attribute = pixel_shader_->get_attrib_location("position");
const GLint tex_coord_attribute = pixel_shader_->get_attrib_location("texCoord");
glEnableVertexAttribArray(static_cast<GLuint>(position_attribute));
glEnableVertexAttribArray(static_cast<GLuint>(tex_coord_attribute));
test_gl(glEnableVertexAttribArray, GLuint(position_attribute));
test_gl(glEnableVertexAttribArray, GLuint(tex_coord_attribute));
const GLsizei vertex_stride = 4 * sizeof(GLfloat);
glVertexAttribPointer((GLuint)position_attribute, 2, GL_FLOAT, GL_FALSE, vertex_stride, (void *)0);
glVertexAttribPointer((GLuint)tex_coord_attribute, 2, GL_FLOAT, GL_FALSE, vertex_stride, (void *)(2 * sizeof(GLfloat)));
test_gl(glVertexAttribPointer, GLuint(position_attribute), 2, GL_FLOAT, GL_FALSE, vertex_stride, (void *)0);
test_gl(glVertexAttribPointer, GLuint(tex_coord_attribute), 2, GL_FLOAT, GL_FALSE, vertex_stride, (void *)(2 * sizeof(GLfloat)));
const GLint texIDUniform = pixel_shader_->get_uniform_location("texID");
glUniform1i(texIDUniform, static_cast<GLint>(texture_unit_ - GL_TEXTURE0));
test_gl(glUniform1i, texIDUniform, GLint(texture_unit_ - GL_TEXTURE0));
threshold_uniform_ = pixel_shader_->get_uniform_location("threshold");
}
@ -132,14 +132,14 @@ void TextureTarget::draw(float aspect_ratio, float colour_threshold) const {
buffer[2] = 0.0f;
buffer[3] = 0.0f;
buffer[6] = 0.0f;
buffer[7] = static_cast<float>(height_) / static_cast<float>(expanded_height_);
buffer[10] = static_cast<float>(width_) / static_cast<float>(expanded_width_);
buffer[7] = float(height_) / float(expanded_height_);
buffer[10] = float(width_) / float(expanded_width_);
buffer[11] = 0.0f;
buffer[14] = buffer[10];
buffer[15] = buffer[7];
// determine positions; rule is to keep the same height and centre
float internal_aspect_ratio = static_cast<float>(width_) / static_cast<float>(height_);
float internal_aspect_ratio = float(width_) / float(height_);
float aspect_ratio_ratio = internal_aspect_ratio / aspect_ratio;
buffer[0] = -aspect_ratio_ratio; buffer[1] = -1.0f;
@ -148,13 +148,13 @@ void TextureTarget::draw(float aspect_ratio, float colour_threshold) const {
buffer[12] = aspect_ratio_ratio; buffer[13] = 1.0f;
// upload buffer
glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_);
glBufferData(GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, drawing_array_buffer_);
test_gl(glBufferData, GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW);
}
pixel_shader_->bind();
glUniform1f(threshold_uniform_, colour_threshold);
test_gl(glUniform1f, threshold_uniform_, colour_threshold);
glBindVertexArray(drawing_vertex_array_);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
test_gl(glBindVertexArray, drawing_vertex_array_);
test_gl(glDrawArrays, GL_TRIANGLE_STRIP, 0, 4);
}

View File

@ -8,6 +8,7 @@
#include "ScanTarget.hpp"
#include "OpenGL.hpp"
#include "Primitives/Rectangle.hpp"
#include <cassert>
@ -62,13 +63,13 @@ const GLenum formatForDepth(std::size_t depth) {
template <typename T> void ScanTarget::allocate_buffer(const T &array, GLuint &buffer_name, GLuint &vertex_array_name) {
const auto buffer_size = array.size() * sizeof(array[0]);
glGenBuffers(1, &buffer_name);
glBindBuffer(GL_ARRAY_BUFFER, buffer_name);
glBufferData(GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW);
test_gl(glGenBuffers, 1, &buffer_name);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, buffer_name);
test_gl(glBufferData, GL_ARRAY_BUFFER, GLsizeiptr(buffer_size), NULL, GL_STREAM_DRAW);
glGenVertexArrays(1, &vertex_array_name);
glBindVertexArray(vertex_array_name);
glBindBuffer(GL_ARRAY_BUFFER, buffer_name);
test_gl(glGenVertexArrays, 1, &vertex_array_name);
test_gl(glBindVertexArray, vertex_array_name);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, buffer_name);
}
ScanTarget::ScanTarget(GLuint target_framebuffer, float output_gamma) :
@ -89,11 +90,11 @@ ScanTarget::ScanTarget(GLuint target_framebuffer, float output_gamma) :
// and specify GL_MAP_PERSISTENT_BIT. Then map the buffer now, and let the client
// write straight into it.
glGenTextures(1, &write_area_texture_name_);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
test_gl(glGenTextures, 1, &write_area_texture_name_);
test_gl(glClearColor, 0.0f, 0.0f, 0.0f, 0.0f);
glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR);
glBlendColor(0.4f, 0.4f, 0.4f, 1.0f);
test_gl(glBlendFunc, GL_SRC_ALPHA, GL_CONSTANT_COLOR);
test_gl(glBlendColor, 0.4f, 0.4f, 0.4f, 1.0f);
is_drawing_.clear();
}
@ -307,8 +308,8 @@ void ScanTarget::setup_pipeline() {
}
// Prepare to bind line shaders.
glBindVertexArray(line_vertex_array_);
glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_);
test_gl(glBindVertexArray, line_vertex_array_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, line_buffer_name_);
// Destroy or create a QAM buffer and shader, if appropriate.
const bool needs_qam_buffer = (modals_.display_type == DisplayType::CompositeColour || modals_.display_type == DisplayType::SVideo);
@ -337,8 +338,8 @@ void ScanTarget::setup_pipeline() {
// Establish an input shader.
input_shader_ = composition_shader();
glBindVertexArray(scan_vertex_array_);
glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_);
test_gl(glBindVertexArray, scan_vertex_array_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, scan_buffer_name_);
enable_vertex_attributes(ShaderType::Composition, *input_shader_);
set_uniforms(ShaderType::Composition, *input_shader_);
input_shader_->set_uniform("textureName", GLint(SourceDataTextureUnit - GL_TEXTURE0));
@ -370,13 +371,14 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
// Submit scans; only the new ones need to be communicated.
size_t new_scans = (submit_pointers.scan_buffer + scan_buffer_.size() - read_pointers.scan_buffer) % scan_buffer_.size();
if(new_scans) {
glBindBuffer(GL_ARRAY_BUFFER, scan_buffer_name_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, scan_buffer_name_);
// Map only the required portion of the buffer.
const size_t new_scans_size = new_scans * sizeof(Scan);
uint8_t *const destination = static_cast<uint8_t *>(
glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(new_scans_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)
);
test_gl_error();
if(read_pointers.scan_buffer < submit_pointers.scan_buffer) {
memcpy(destination, &scan_buffer_[read_pointers.scan_buffer], new_scans_size);
@ -387,23 +389,23 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
}
// Flush and unmap the buffer.
glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(new_scans_size));
glUnmapBuffer(GL_ARRAY_BUFFER);
test_gl(glFlushMappedBufferRange, GL_ARRAY_BUFFER, 0, GLsizeiptr(new_scans_size));
test_gl(glUnmapBuffer, GL_ARRAY_BUFFER);
}
// Submit texture.
if(submit_pointers.write_area != read_pointers.write_area) {
glActiveTexture(SourceDataTextureUnit);
glBindTexture(GL_TEXTURE_2D, write_area_texture_name_);
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_) {
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(
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(data_type_size_),
@ -420,30 +422,33 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
const auto end_y = TextureAddressGetY(submit_pointers.write_area);
if(end_y >= start_y) {
// Submit the direct region from the submit pointer to the read pointer.
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, start_y,
WriteAreaWidth,
1 + end_y - start_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
test_gl(glTexSubImage2D,
GL_TEXTURE_2D, 0,
0, start_y,
WriteAreaWidth,
1 + end_y - start_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
} else {
// The circular buffer wrapped around; submit the data from the read pointer to the end of
// the buffer and from the start of the buffer to the submit pointer.
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, 0,
WriteAreaWidth,
1 + end_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[0]);
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, start_y,
WriteAreaWidth,
WriteAreaHeight - start_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
test_gl(glTexSubImage2D,
GL_TEXTURE_2D, 0,
0, 0,
WriteAreaWidth,
1 + end_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[0]);
test_gl(glTexSubImage2D,
GL_TEXTURE_2D, 0,
0, start_y,
WriteAreaWidth,
WriteAreaHeight - start_y,
formatForDepth(data_type_size_),
GL_UNSIGNED_BYTE,
&write_area_texture_[size_t(TextureAddress(0, start_y)) * data_type_size_]);
}
}
@ -455,25 +460,25 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
const uint16_t first_line_to_clear = (read_pointers.line+1)%line_buffer_.size();
const uint16_t final_line_to_clear = submit_pointers.line;
if(first_line_to_clear != final_line_to_clear) {
glEnable(GL_SCISSOR_TEST);
test_gl(glEnable, GL_SCISSOR_TEST);
if(first_line_to_clear < final_line_to_clear) {
glScissor(0, first_line_to_clear, unprocessed_line_texture_.get_width(), final_line_to_clear - first_line_to_clear);
glClear(GL_COLOR_BUFFER_BIT);
test_gl(glScissor, 0, first_line_to_clear, unprocessed_line_texture_.get_width(), final_line_to_clear - first_line_to_clear);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
} else {
glScissor(0, 0, unprocessed_line_texture_.get_width(), final_line_to_clear);
glClear(GL_COLOR_BUFFER_BIT);
glScissor(0, first_line_to_clear, unprocessed_line_texture_.get_width(), unprocessed_line_texture_.get_height() - first_line_to_clear);
glClear(GL_COLOR_BUFFER_BIT);
test_gl(glScissor, 0, 0, unprocessed_line_texture_.get_width(), final_line_to_clear);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
test_gl(glScissor, 0, first_line_to_clear, unprocessed_line_texture_.get_width(), unprocessed_line_texture_.get_height() - first_line_to_clear);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
test_gl(glDisable, GL_SCISSOR_TEST);
}
// Apply new spans. They definitely always go to the first buffer.
glBindVertexArray(scan_vertex_array_);
test_gl(glBindVertexArray, scan_vertex_array_);
input_shader_->bind();
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans));
test_gl(glDrawArraysInstanced, GL_TRIANGLE_STRIP, 0, 4, GLsizei(new_scans));
}
// Ensure the accumulation buffer is properly sized.
@ -488,13 +493,13 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
true));
if(accumulation_texture_) {
new_framebuffer->bind_framebuffer();
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
test_gl(glClear, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glActiveTexture(AccumulationTextureUnit);
test_gl(glActiveTexture, AccumulationTextureUnit);
accumulation_texture_->bind_texture();
accumulation_texture_->draw(float(output_width) / float(output_height));
glClear(GL_STENCIL_BUFFER_BIT);
test_gl(glClear, GL_STENCIL_BUFFER_BIT);
new_framebuffer->bind_texture();
}
@ -510,7 +515,7 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
uint16_t new_lines = (submit_pointers.line + LineBufferHeight - read_pointers.line) % LineBufferHeight;
if(new_lines) {
// Prepare to output lines.
glBindVertexArray(line_vertex_array_);
test_gl(glBindVertexArray, line_vertex_array_);
// Bind the accumulation framebuffer, unless there's going to be QAM work first.
if(!qam_separation_shader_ || line_metadata_buffer_[read_pointers.line].is_first_in_frame) {
@ -518,16 +523,16 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
output_shader_->bind();
// Enable blending and stenciling.
glEnable(GL_BLEND);
glEnable(GL_STENCIL_TEST);
test_gl(glEnable, GL_BLEND);
test_gl(glEnable, GL_STENCIL_TEST);
}
// Set the proper stencil function regardless.
glStencilFunc(GL_EQUAL, 0, GLuint(~0));
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
test_gl(glStencilFunc, GL_EQUAL, 0, GLuint(~0));
test_gl(glStencilOp, GL_KEEP, GL_KEEP, GL_INCR);
// Prepare to upload data that will consitute lines.
glBindBuffer(GL_ARRAY_BUFFER, line_buffer_name_);
test_gl(glBindBuffer, GL_ARRAY_BUFFER, line_buffer_name_);
// Divide spans by which frame they're in.
uint16_t start_line = read_pointers.line;
@ -547,10 +552,10 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
full_display_rectangle_.draw(0.0f, 0.0f, 0.0f);
}
stencil_is_valid_ = true;
glClear(GL_STENCIL_BUFFER_BIT);
test_gl(glClear, GL_STENCIL_BUFFER_BIT);
// Rebind the program for span output.
glBindVertexArray(line_vertex_array_);
test_gl(glBindVertexArray, line_vertex_array_);
if(!qam_separation_shader_) {
output_shader_->bind();
}
@ -559,56 +564,57 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
// Upload.
const auto buffer_size = lines * sizeof(Line);
if(!end_line || end_line > start_line) {
glBufferSubData(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), &line_buffer_[start_line]);
test_gl(glBufferSubData, GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), &line_buffer_[start_line]);
} else {
uint8_t *destination = static_cast<uint8_t *>(
glMapBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size), GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)
);
assert(destination);
test_gl_error();
const size_t buffer_length = line_buffer_.size() * sizeof(Line);
const size_t start_position = start_line * sizeof(Line);
memcpy(&destination[0], &line_buffer_[start_line], buffer_length - start_position);
memcpy(&destination[buffer_length - start_position], &line_buffer_[0], end_line * sizeof(Line));
glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size));
glUnmapBuffer(GL_ARRAY_BUFFER);
test_gl(glFlushMappedBufferRange, GL_ARRAY_BUFFER, 0, GLsizeiptr(buffer_size));
test_gl(glUnmapBuffer, GL_ARRAY_BUFFER);
}
// Produce colour information, if required.
if(qam_separation_shader_) {
qam_separation_shader_->bind();
qam_chroma_texture_->bind_framebuffer();
glClear(GL_COLOR_BUFFER_BIT); // TODO: this is here as a hint that the old framebuffer doesn't need reloading;
// test whether that's a valid optimisation on desktop OpenGL.
test_gl(glClear, GL_COLOR_BUFFER_BIT); // TODO: this is here as a hint that the old framebuffer doesn't need reloading;
// test whether that's a valid optimisation on desktop OpenGL.
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(lines));
test_gl(glDisable, GL_BLEND);
test_gl(glDisable, GL_STENCIL_TEST);
test_gl(glDrawArraysInstanced, GL_TRIANGLE_STRIP, 0, 4, GLsizei(lines));
accumulation_texture_->bind_framebuffer();
output_shader_->bind();
glEnable(GL_BLEND);
glEnable(GL_STENCIL_TEST);
test_gl(glEnable, GL_BLEND);
test_gl(glEnable, GL_STENCIL_TEST);
}
// Render to the output.
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, GLsizei(lines));
test_gl(glDrawArraysInstanced, GL_TRIANGLE_STRIP, 0, 4, GLsizei(lines));
start_line = end_line;
new_lines -= lines;
}
// Disable blending and the stencil test again.
glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND);
test_gl(glDisable, GL_STENCIL_TEST);
test_gl(glDisable, GL_BLEND);
}
// Copy the accumulatiion texture to the target.
glBindFramebuffer(GL_FRAMEBUFFER, target_framebuffer_);
glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height);
test_gl(glBindFramebuffer, GL_FRAMEBUFFER, target_framebuffer_);
test_gl(glViewport, 0, 0, (GLsizei)output_width, (GLsizei)output_height);
glClear(GL_COLOR_BUFFER_BIT);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
accumulation_texture_->bind_texture();
accumulation_texture_->draw(float(output_width) / float(output_height), 4.0f / 255.0f);