// Copyright 2021 Benjamin Barenblat // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. // Buffer objects. #ifndef GLPLANET_SRC_GL_BUFFER_H_ #define GLPLANET_SRC_GL_BUFFER_H_ #include #include #include #include #include #include "src/gl/error.h" #include "third_party/abseil/absl/types/span.h" #include "third_party/glew/include/GL/glew.h" namespace gl { // A byte buffer in GPU memory. class Buffer final { public: // The frequency with which data in the buffer will be accessed. enum class AccessFrequency : uint8_t { kStatic, kStream, kDynamic }; // The type of that access. enum class AccessNature : uint8_t { kDraw, kRead, kCopy }; explicit Buffer() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) : size_bytes_(0), frequency_(AccessFrequency::kStatic), nature_(AccessNature::kDraw) { gl_internal::CheckThreadSafety(); glCreateBuffers(1, &buffer_); gl_internal::UnnecessaryErrorCheck(); } Buffer(const Buffer&); Buffer& operator=(const Buffer& other) { if (this != &other) { Buffer other2(other); swap(*this, other2); } return *this; } Buffer(Buffer&& other) noexcept : buffer_(0), size_bytes_(0), frequency_(AccessFrequency::kStatic), nature_(AccessNature::kDraw) { *this = std::move(other); } Buffer& operator=(Buffer&& other) noexcept { swap(*this, other); return *this; } ~Buffer() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) { gl_internal::CheckThreadSafety(); glDeleteBuffers(1, &buffer_); gl_internal::UnnecessaryErrorCheck(); } friend void swap(Buffer& left, Buffer& right) noexcept { using ::std::swap; swap(left.buffer_, right.buffer_); swap(left.size_bytes_, right.size_bytes_); swap(left.frequency_, right.frequency_); swap(left.nature_, right.nature_); } // Loads data into the buffer. void SetData(const void*, int size_bytes, AccessFrequency, AccessNature); int size() const noexcept { return size_bytes_; } // The GL identifier for this buffer. unsigned int id() const noexcept { return buffer_; } private: GLuint buffer_; int size_bytes_; AccessFrequency frequency_; AccessNature nature_; }; // A vertex buffer object. class VertexBuffer final { public: explicit VertexBuffer() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) = default; // A shorthand to construct the buffer and load data into it in one step. template explicit VertexBuffer(absl::Span data, Buffer::AccessFrequency frequency, Buffer::AccessNature nature) { SetData(data, frequency, nature); } VertexBuffer(const VertexBuffer&) = default; VertexBuffer& operator=(const VertexBuffer&) = default; VertexBuffer(VertexBuffer&&) = default; VertexBuffer& operator=(VertexBuffer&&) = default; // Loads data into the buffer. template void SetData(absl::Span data, Buffer::AccessFrequency frequency, Buffer::AccessNature nature) { buffer_.SetData(data.data(), data.size() * sizeof(T), frequency, nature); element_size_ = sizeof(T); } // The number of vertices in this buffer. int size() const noexcept { assert(buffer_.size() % element_size() == 0); return buffer_.size() / element_size(); } // The size, in bytes, of an individual vertex. int element_size() const noexcept { return element_size_; } // The GL identifier for this buffer. unsigned int id() const noexcept { return buffer_.id(); } private: Buffer buffer_; int element_size_; }; // The types that can go in an element buffer. template concept IsElement = std::same_as || std::same_as || std::same_as; // An element buffer. class ElementBuffer final { public: explicit ElementBuffer() noexcept(!(gl_internal::kThreadSafetyChecks || gl_internal::kAggressiveErrorChecking)) = default; // Constructs an element buffer containing the specified data. template explicit ElementBuffer(absl::Span data, Buffer::AccessFrequency frequency, Buffer::AccessNature nature) { SetData(data, frequency, nature); } ElementBuffer(const ElementBuffer&) = default; ElementBuffer& operator=(const ElementBuffer&) = default; ElementBuffer(ElementBuffer&&) noexcept = default; ElementBuffer& operator=(ElementBuffer&&) noexcept = default; // Loads data into the buffer. template void SetData(absl::Span data, Buffer::AccessFrequency frequency, Buffer::AccessNature nature) { buffer_.SetData(data.data(), data.size() * sizeof(T), frequency, nature); element_size_ = sizeof(T); } // The number of elements in this buffer. int size() const noexcept { assert(buffer_.size() % element_size() == 0); return buffer_.size() / element_size(); } // The size, in bytes, of an individual element. This will always be 1, 2, // or 4. int element_size() const noexcept { assert(element_size_ == 1 || element_size_ == 2 || element_size_ == 4); return element_size_; } // The GL identifier for this buffer. unsigned int id() const noexcept { return buffer_.id(); } private: Buffer buffer_; int element_size_; }; } // namespace gl #endif // GLPLANET_SRC_GL_BUFFER_H_