mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Attempts to put in better OpenGL safety rails.
This commit is contained in:
parent
6d76b7cd94
commit
8ecf885629
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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] {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user