So this was a terrible thing in many ways.
What we're doing:
- Converting all of the coefficients to floats (+ doing a bit of
preprocessing) and uploading the the GPU.
- Uploading the data_indices array from the document to the GPU.
- Using the vertex ids in the IBO when rendering béziers to get
the object id, then looking up the data_indices array (as a texture)
to find which bézier coefficients to use, then reading the bézier
coefficients from another buffer texture and finally having the
geometry shader generate 100 lines much as the CPU one does.
We're using buffer textures to access these things because they don't
have fixed sizes (and can get big), so we can't use uniform buffers and
because shader storage buffer objects need OpenGL 4.3, which only graphics
cards manufactured in the last 45 seconds actually support.
return GL_PIXEL_UNPACK_BUFFER;
case GraphicsBuffer::BufferTypeUniform:
return GL_UNIFORM_BUFFER;
+ case GraphicsBuffer::BufferTypeTexture:
+ return GL_TEXTURE_BUFFER;
case GraphicsBuffer::BufferTypeDrawIndirect:
return GL_DRAW_INDIRECT_BUFFER;
default:
BufferTypePixelPack, // Pixel Pack buffer
BufferTypePixelUnpack,
BufferTypeUniform, // Uniform/Constant buffer
+ BufferTypeTexture, // I was hoping to avoid this one.
BufferTypeDrawIndirect,
};
const size_t GetSize() const { return m_buffer_size; }
void Invalidate();
+
+ // WARNING: The buffer handle can change for (almost) no reason.
+ // If you do _anything_ to the buffer, you'll need to call this
+ // again to see if we've recreated it in a vain attempt to outsmart
+ // the driver.
+ GLuint GetHandle() const { return m_buffer_handle; }
void Bind() const;
private:
}
}
+void BezierRenderer::PrepareBezierGPUBuffer(const Objects& objects)
+{
+ m_bezier_coeffs.SetType(GraphicsBuffer::BufferTypeTexture);
+ m_bezier_coeffs.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw);
+ m_bezier_coeffs.Resize(objects.beziers.size()*sizeof(GPUBezierCoeffs));
+ BufferBuilder<GPUBezierCoeffs> builder(m_bezier_coeffs.Map(false, true, true), m_bezier_coeffs.GetSize());
+
+ for (auto bez : objects.beziers)
+ {
+ GPUBezierCoeffs coeffs = {
+ (float)bez.x0, (float)bez.y0,
+ (float)bez.x1 - (float)bez.x0, (float)bez.y1 - (float)bez.y0,
+ (float)bez.x2 - (float)bez.x0, (float)bez.y2 - (float)bez.y0
+ };
+ builder.Add(coeffs);
+ }
+ m_bezier_coeffs.UnMap();
+ glGenTextures(1, &m_bezier_buffer_texture);
+ glBindTexture(GL_TEXTURE_BUFFER, m_bezier_buffer_texture);
+ glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_bezier_coeffs.GetHandle());
+
+ m_bezier_ids.SetType(GraphicsBuffer::BufferTypeTexture);
+ m_bezier_ids.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw);
+ m_bezier_ids.Upload(objects.data_indices.size() * sizeof(uint32_t), &objects.data_indices[0]);
+
+ glGenTextures(1, &m_bezier_id_buffer_texture);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_BUFFER, m_bezier_id_buffer_texture);
+ glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, m_bezier_ids.GetHandle());
+ glActiveTexture(GL_TEXTURE0);
+}
+
+void BezierRenderer::RenderUsingGPU()
+{
+ if (!m_shader_program.Valid())
+ Warn("Shader is invalid (objects are of type %d)", m_type);
+ m_shader_program.Use();
+ glUniform1i(m_shader_program.GetUniformLocation("bezier_buffer_texture"), 0);
+ glUniform1i(m_shader_program.GetUniformLocation("bezier_id_buffer_texture"), 1);
+ m_ibo.Bind();
+ glDrawElements(GL_LINES, m_indexes.size()*2, GL_UNSIGNED_INT, 0);
+}
+
/**
* For debug, save pixels to bitmap
*/
class BezierRenderer : public ObjectRenderer
{
public:
- BezierRenderer() : ObjectRenderer(BEZIER, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/rect_outline_geom.glsl") {}
+ BezierRenderer() : ObjectRenderer(BEZIER, "shaders/rect_vert.glsl", "shaders/rect_frag.glsl", "shaders/bezier_texbuf_geom.glsl") {}
virtual ~BezierRenderer() {}
- virtual void RenderUsingGPU()
- {
- Error("Cannot render beziers on the GPU; they will appear as outlined rectangles.");
- ObjectRenderer::RenderUsingGPU();
- }
+ virtual void RenderUsingGPU();
virtual void RenderUsingCPU(const Objects & objects, const View & view, const CPURenderTarget & target);
+ void PrepareBezierGPUBuffer(const Objects & objects);
+ private:
+ GraphicsBuffer m_bezier_coeffs;
+ GraphicsBuffer m_bezier_ids;
+ struct GPUBezierCoeffs
+ {
+ float x0, y0;
+ float x1, y1;
+ float x2, y2;
+ };
+
+ GLuint m_bezier_buffer_texture;
+ GLuint m_bezier_id_buffer_texture;
+
};
}
--- /dev/null
+#version 150
+
+uniform samplerBuffer bezier_buffer_texture;
+uniform isamplerBuffer bezier_id_buffer_texture;
+
+layout(lines) in;
+layout(line_strip, max_vertices = 100) out;
+
+in int objectid[];
+
+void main()
+{
+ int bezierid = texelFetch(bezier_id_buffer_texture, objectid[0]).r;
+ vec2 boundssize = gl_in[1].gl_Position.xy - gl_in[0].gl_Position.xy;
+ vec2 coeff0 = texelFetch(bezier_buffer_texture, bezierid*3).rg + gl_in[0].gl_Position.xy ;
+ vec2 coeff1 = texelFetch(bezier_buffer_texture, bezierid*3+1).rg * boundssize + gl_in[0].gl_Position.xy;
+ vec2 coeff2 = texelFetch(bezier_buffer_texture, bezierid*3+2).rg * boundssize + gl_in[0].gl_Position.xy;
+ for (int i = 0; i <= 100; ++i)
+ {
+ float t = i * 0.01f;
+ float oneminust = 1.0f - t;
+ float bernstein0 = t*t;
+ float bernstein1 = 2*t*oneminust;
+ float bernstein2 = oneminust*oneminust;
+ gl_Position = vec4(coeff0*bernstein0 + coeff1*bernstein1 + coeff2*bernstein2, 0.0, 1.0);
+ EmitVertex();
+
+ }
+ EndPrimitive();
+}
+
layout(location = 0) in vec2 position;
+out int objectid;
+
void main()
{
vec2 transformed_position;
gl_Position.y = 1 - (transformed_position.y*2);
gl_Position.z = 0.0;
gl_Position.w = 1.0;
+
+ objectid = gl_VertexID / 2;
}
for (unsigned i = 0; i < m_object_renderers.size(); ++i)
{
m_object_renderers[i]->FinaliseBuffers();
- }
+ }
+ dynamic_cast<BezierRenderer*>(m_object_renderers[BEZIER])->PrepareBezierGPUBuffer(m_document.m_objects);
m_render_dirty = false;
}