/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 2006-2007 Rivo Laks SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin SPDX-FileCopyrightText: 2023 Xaver Hugl SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kwin_export.h" #include #include #include #include #include #include #include namespace KWin { enum VertexAttributeType { VA_Position = 0, VA_TexCoord = 1, VertexAttributeCount = 2, }; struct GLVertex2D { QVector2D position; QVector2D texcoord; }; struct GLVertex3D { QVector3D position; QVector2D texcoord; }; /** * Describes the format of a vertex attribute stored in a buffer object. * * The attribute format consists of the attribute index, the number of * vector components, the data type, and the offset of the first element * relative to the start of the vertex data. */ struct GLVertexAttrib { size_t attributeIndex; size_t componentCount; GLenum type; /** The type (e.g. GL_FLOAT) */ size_t relativeOffset; /** The relative offset of the attribute */ }; class GLVertexBufferPrivate; /** * @short Vertex Buffer Object * * This is a short helper class to use vertex buffer objects (VBO). A VBO can be used to buffer * vertex data and to store them on graphics memory. * * @author Martin Gräßlin * @since 4.6 */ class KWIN_EXPORT GLVertexBuffer { public: /** * Enum to define how often the vertex data in the buffer object changes. */ enum UsageHint { Dynamic, ///< frequent changes, but used several times for rendering Static, ///< No changes to data Stream ///< Data only used once for rendering, updated very frequently }; explicit GLVertexBuffer(UsageHint hint); ~GLVertexBuffer(); /** * Specifies how interleaved vertex attributes are laid out in * the buffer object. * * Note that the attributes and the stride should be 32 bit aligned * or a performance penalty may be incurred. * * For some hardware the optimal stride is a multiple of 32 bytes. * * Example: * * struct Vertex { * QVector3D position; * QVector2D texcoord; * }; * * const std::array attribs = { * GLVertexAttrib{ VA_Position, 3, GL_FLOAT, offsetof(Vertex, position) }, * GLVertexAttrib{ VA_TexCoord, 2, GL_FLOAT, offsetof(Vertex, texcoord) }, * }; * * Vertex vertices[6]; * vbo->setAttribLayout(std::span(attribs), sizeof(Vertex)); * vbo->setData(vertices, sizeof(vertices)); */ void setAttribLayout(std::span attribs, size_t stride); /** * Uploads data into the buffer object's data store. */ void setData(const void *data, size_t sizeInBytes); /** * Sets the number of vertices that will be drawn by the render() method. */ void setVertexCount(int count); // clang-format off template requires std::is_same, GLVertex2D>::value void setVertices(const T &range) { setData(range.data(), range.size() * sizeof(GLVertex2D)); setVertexCount(range.size()); setAttribLayout(std::span(GLVertex2DLayout), sizeof(GLVertex2D)); } template requires std::is_same, GLVertex3D>::value void setVertices(const T &range) { setData(range.data(), range.size() * sizeof(GLVertex3D)); setVertexCount(range.size()); setAttribLayout(std::span(GLVertex3DLayout), sizeof(GLVertex3D)); } template requires std::is_same, QVector2D>::value void setVertices(const T &range) { setData(range.data(), range.size() * sizeof(QVector2D)); setVertexCount(range.size()); static constexpr GLVertexAttrib layout{ .attributeIndex = VA_Position, .componentCount = 2, .type = GL_FLOAT, .relativeOffset = 0, }; setAttribLayout(std::span(&layout, 1), sizeof(QVector2D)); } // clang-format on /** * Maps an unused range of the data store into the client's address space. * * The data store will be reallocated if it is smaller than the given size. * * The buffer object is mapped for writing, not reading. Attempts to read from * the mapped buffer range may result in system errors, including program * termination. The data in the mapped region is undefined until it has been * written to. If subsequent GL calls access unwritten memory, the results are * undefined and system errors, including program termination, may occur. * * No GL calls that access the buffer object must be made while the buffer * object is mapped. The returned pointer must not be passed as a parameter * value to any GL function. * * It is assumed that the GL_ARRAY_BUFFER_BINDING will not be changed while * the buffer object is mapped. */ template std::optional> map(size_t count) { if (const auto m = map(sizeof(T) * count)) { return std::span(reinterpret_cast(m), count); } else { return std::nullopt; } } /** * Flushes the mapped buffer range and unmaps the buffer. */ void unmap(); /** * Binds the vertex arrays to the context. */ void bindArrays(); /** * Disables the vertex arrays. */ void unbindArrays(); /** * Draws count vertices beginning with first. */ void draw(GLenum primitiveMode, int first, int count); /** * Draws count vertices beginning with first. */ void draw(const QRegion ®ion, GLenum primitiveMode, int first, int count, bool hardwareClipping = false); /** * Renders the vertex data in given @a primitiveMode. * Please refer to OpenGL documentation of glDrawArrays or glDrawElements for allowed * values for @a primitiveMode. Best is to use GL_TRIANGLES or similar to be future * compatible. */ void render(GLenum primitiveMode); /** * Same as above restricting painting to @a region if @a hardwareClipping is true. * It's within the caller's responsibility to enable GL_SCISSOR_TEST. */ void render(const QRegion ®ion, GLenum primitiveMode, bool hardwareClipping = false); /** * Resets the instance to default values. * Useful for shared buffers. * @since 4.7 */ void reset(); /** * Notifies the vertex buffer that we are done painting the frame. * * @internal */ void endOfFrame(); /** * Notifies the vertex buffer that we are about to paint a frame. * * @internal */ void beginFrame(); void setPersistent(); /** * @return A shared VBO for streaming data * @since 4.7 */ static GLVertexBuffer *streamingBuffer(); static constexpr std::array GLVertex2DLayout{ GLVertexAttrib{ .attributeIndex = VA_Position, .componentCount = 2, .type = GL_FLOAT, .relativeOffset = offsetof(GLVertex2D, position), }, GLVertexAttrib{ .attributeIndex = VA_TexCoord, .componentCount = 2, .type = GL_FLOAT, .relativeOffset = offsetof(GLVertex2D, texcoord), }, }; static constexpr std::array GLVertex3DLayout{ GLVertexAttrib{ .attributeIndex = VA_Position, .componentCount = 3, .type = GL_FLOAT, .relativeOffset = offsetof(GLVertex3D, position), }, GLVertexAttrib{ .attributeIndex = VA_TexCoord, .componentCount = 2, .type = GL_FLOAT, .relativeOffset = offsetof(GLVertex3D, texcoord), }, }; private: GLvoid *map(size_t size); const std::unique_ptr d; }; }