/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_VERTEXARRAY #define INCLUDED_VERTEXARRAY #include "renderer/backend/Format.h" #include "renderer/backend/IBuffer.h" #include "renderer/backend/IDeviceCommandContext.h" #include "renderer/VertexBufferManager.h" #include // Iterator template class VertexArrayIterator { public: typedef T Type; public: VertexArrayIterator() : m_Data(0), m_Stride(0) { } VertexArrayIterator(char* data, size_t stride) : m_Data(data), m_Stride(stride) { } VertexArrayIterator(const VertexArrayIterator& rhs) : m_Data(rhs.m_Data), m_Stride(rhs.m_Stride) { } VertexArrayIterator& operator=(const VertexArrayIterator& rhs) { m_Data = rhs.m_Data; m_Stride = rhs.m_Stride; return *this; } // Accessors T& operator*() const { return *(T*)m_Data; } T* operator->() const { return (T*)m_Data; } T& operator[](size_t idx) const { return *(T*)(m_Data + idx*m_Stride); } // Walking VertexArrayIterator& operator++() { m_Data += m_Stride; return *this; } VertexArrayIterator operator++(int) { VertexArrayIterator tmp = *this; m_Data += m_Stride; return tmp; } VertexArrayIterator& operator--() { m_Data -= m_Stride; return *this; } VertexArrayIterator operator--(int) { VertexArrayIterator tmp = *this; m_Data -= m_Stride; return tmp; } VertexArrayIterator& operator+=(ssize_t rhs) { m_Data += rhs*m_Stride; return *this; } VertexArrayIterator& operator-=(ssize_t rhs) { m_Data -= rhs*m_Stride; return *this; } VertexArrayIterator operator+(ssize_t rhs) const { VertexArrayIterator tmp = *this; tmp.m_Data += rhs*m_Stride; return tmp; } VertexArrayIterator operator-(ssize_t rhs) const { VertexArrayIterator tmp = *this; tmp.m_Data -= rhs*m_Stride; return tmp; } // Accessors for raw buffer data, for performance-critical code char* GetData() const { return m_Data; } size_t GetStride() const { return m_Stride; } private: char* m_Data; size_t m_Stride; }; // Manage a vertex array with a runtime-determined set of attributes. // // Purpose: Different rendering paths sometimes require different sets of // attributes (e.g. normal vector vs. color data), which is difficult to // support with hardcoded vertex structures. // This class chooses the vertex layout at runtime, based on the attributes // that are actually needed. // // Note that this class will not allocate any backend resources until one // of the Upload functions is called. class VertexArray { public: struct Attribute { Renderer::Backend::Format format = Renderer::Backend::Format::UNDEFINED; // Offset (in bytes) into a vertex structure (filled in by Layout()) uint32_t offset = 0; VertexArray* vertexArray = nullptr; Attribute() {} // Get an iterator over the backing store for the given attribute that // initially points at the first vertex. // Supported types T: CVector3D, CVector4D, float[2], SColor4ub, // u16, u16[2], u8[4], short, short[2]. // This function verifies at runtime that the requested type T matches // the attribute definition passed to AddAttribute(). template VertexArrayIterator GetIterator() const; }; public: VertexArray( const Renderer::Backend::IBuffer::Type type, const bool dynamic); ~VertexArray(); // Set the number of vertices stored in the array void SetNumberOfVertices(const size_t numberOfVertices); // Add vertex attributes void AddAttribute(Attribute* attr); size_t GetNumberOfVertices() const { return m_NumberOfVertices; } uint32_t GetStride() const { return m_Stride; } // Layout the vertex array format and create backing buffer in RAM. // You must call Layout() after changing the number of vertices or // attributes. // All vertex data is lost when a vertex array is re-layouted. void Layout(); // (Re-)Upload the attributes of the vertex array from the backing store to // the underlying buffer. void Upload(); // Make this vertex array's data available for the next series of calls to Bind void PrepareForRendering(); void UploadIfNeeded(Renderer::Backend::IDeviceCommandContext* deviceCommandContext); // If you know for certain that you'll never have to change the data again, // call this to free some memory. void FreeBackingStore(); Renderer::Backend::IBuffer* GetBuffer() { return m_VB ? m_VB->m_Owner->GetBuffer() : nullptr; } uint32_t GetOffset() const { return m_VB ? m_VB->m_Index : 0; } private: void Free(); template VertexArrayIterator MakeIterator(const Attribute* attr) { ENSURE(attr->format != Renderer::Backend::Format::UNDEFINED); return VertexArrayIterator(m_BackingStore + attr->offset, m_Stride); } Renderer::Backend::IBuffer::Type m_Type; bool m_Dynamic; size_t m_NumberOfVertices; std::vector m_Attributes; CVertexBufferManager::Handle m_VB; uint32_t m_Stride; char* m_BackingStore; // 16-byte aligned, to allow fast SSE access }; /** * A VertexArray that is specialised to handle 16-bit array indices. * Call UploadIfNeeded() before use in Draw/DrawIndexed. */ class VertexIndexArray : public VertexArray { public: VertexIndexArray(const bool dynamic); /// Gets the iterator over the (only) attribute in this array, i.e. a u16. VertexArrayIterator GetIterator() const; private: Attribute m_Attr; }; #endif // INCLUDED_VERTEXARRAY