From: David Gow Date: Wed, 18 Jun 2014 15:36:27 +0000 (+0800) Subject: Béziers on the GPU. X-Git-Url: https://git.ucc.asn.au/?p=ipdf%2Fcode.git;a=commitdiff_plain;h=09fc4981be389620d3c269beacf0630de45871bb Béziers on the GPU. 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. --- diff --git a/bin/ipdf b/bin/ipdf index 412cf88..4739d21 100755 Binary files a/bin/ipdf and b/bin/ipdf differ diff --git a/src/graphicsbuffer.cpp b/src/graphicsbuffer.cpp index eb98450..576c2d6 100644 --- a/src/graphicsbuffer.cpp +++ b/src/graphicsbuffer.cpp @@ -56,6 +56,8 @@ static GLenum BufferTypeToGLType(GraphicsBuffer::BufferType buffer_type) 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: diff --git a/src/graphicsbuffer.h b/src/graphicsbuffer.h index 9513cb7..3dbb6fb 100644 --- a/src/graphicsbuffer.h +++ b/src/graphicsbuffer.h @@ -20,6 +20,7 @@ namespace IPDF BufferTypePixelPack, // Pixel Pack buffer BufferTypePixelUnpack, BufferTypeUniform, // Uniform/Constant buffer + BufferTypeTexture, // I was hoping to avoid this one. BufferTypeDrawIndirect, }; @@ -54,6 +55,12 @@ namespace IPDF 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: diff --git a/src/objectrenderer.cpp b/src/objectrenderer.cpp index c552bd8..755df98 100644 --- a/src/objectrenderer.cpp +++ b/src/objectrenderer.cpp @@ -251,6 +251,49 @@ void BezierRenderer::RenderUsingCPU(const Objects & objects, const View & view, } } +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 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 */ diff --git a/src/objectrenderer.h b/src/objectrenderer.h index 4dd7fe1..4ebb66a 100644 --- a/src/objectrenderer.h +++ b/src/objectrenderer.h @@ -108,14 +108,24 @@ namespace IPDF 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; + }; } diff --git a/src/shaders/bezier_texbuf_geom.glsl b/src/shaders/bezier_texbuf_geom.glsl new file mode 100644 index 0000000..2ee2836 --- /dev/null +++ b/src/shaders/bezier_texbuf_geom.glsl @@ -0,0 +1,31 @@ +#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(); +} + diff --git a/src/shaders/rect_vert.glsl b/src/shaders/rect_vert.glsl index cc15e56..a9f258a 100644 --- a/src/shaders/rect_vert.glsl +++ b/src/shaders/rect_vert.glsl @@ -12,6 +12,8 @@ layout(std140, binding=0) uniform ViewBounds layout(location = 0) in vec2 position; +out int objectid; + void main() { vec2 transformed_position; @@ -22,4 +24,6 @@ void main() gl_Position.y = 1 - (transformed_position.y*2); gl_Position.z = 0.0; gl_Position.w = 1.0; + + objectid = gl_VertexID / 2; } diff --git a/src/view.cpp b/src/view.cpp index cbcdbf4..4bba591 100644 --- a/src/view.cpp +++ b/src/view.cpp @@ -288,6 +288,7 @@ void View::PrepareRender() for (unsigned i = 0; i < m_object_renderers.size(); ++i) { m_object_renderers[i]->FinaliseBuffers(); - } + } + dynamic_cast(m_object_renderers[BEZIER])->PrepareBezierGPUBuffer(m_document.m_objects); m_render_dirty = false; }