diff --git a/code/Modules/Assets/CMakeLists.txt b/code/Modules/Assets/CMakeLists.txt index 98659355f..4ff28e953 100644 --- a/code/Modules/Assets/CMakeLists.txt +++ b/code/Modules/Assets/CMakeLists.txt @@ -2,24 +2,10 @@ fips_begin_module(Assets) fips_vs_warning_level(3) fips_dir(Gfx) fips_files( + FullscreenQuadBuilder.cc FullscreenQuadBuilder.h MeshBuilder.cc MeshBuilder.h ShapeBuilder.cc ShapeBuilder.h VertexWriter.cc VertexWriter.h TextureLoader.cc TextureLoader.h - OmshParser.cc OmshParser.h - MeshLoader.cc MeshLoader.h ) fips_end_module() - -fips_begin_unittest(Assets) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - MeshBuilderTest.cc - ShapeBuilderTest.cc - VertexWriterTest.cc - ) - fips_deps(Gfx Assets) -fips_end_unittest() - - diff --git a/code/Modules/Assets/Gfx/FullscreenQuadBuilder.cc b/code/Modules/Assets/Gfx/FullscreenQuadBuilder.cc new file mode 100644 index 000000000..2e1c44b23 --- /dev/null +++ b/code/Modules/Assets/Gfx/FullscreenQuadBuilder.cc @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// FullscreenQuadBuilder.cc +//------------------------------------------------------------------------------ +#include "Pre.h" +#include "FullscreenQuadBuilder.h" + +namespace Oryol { + +static const float vertsNoFlip[20] = { + -1.0f, +1.0f, 0.0f, 0.0f, 1.0f, + +1.0f, +1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + +1.0f, -1.0f, 0.0f, 1.0f, 0.0f +}; + +static const float vertsFlip[20] = { + -1.0f, +1.0f, 0.0f, 0.0f, 0.0f, + +1.0f, +1.0f, 0.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, + +1.0f, -1.0f, 0.0f, 1.0f, 1.0f +}; + +static_assert(sizeof(vertsNoFlip) == sizeof(vertsFlip), "FullscreenQuadBuilder"); + +FullscreenQuadBuilder::Result +FullscreenQuadBuilder::Build() { + Result res; + res.VertexBufferDesc + .SetType(BufferType::VertexBuffer) + .SetSize(sizeof(vertsNoFlip)) + .SetContent(this->flipV ? vertsFlip : vertsNoFlip); + res.PipelineDesc + .SetLayout(0, { + { "position", VertexFormat::Float3 }, + { "texcoord0", VertexFormat::Float2 } + }) + .SetPrimitiveType(PrimitiveType::TriangleStrip); + return res; +} + +} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/FullscreenQuadBuilder.h b/code/Modules/Assets/Gfx/FullscreenQuadBuilder.h new file mode 100644 index 000000000..6e4706f4e --- /dev/null +++ b/code/Modules/Assets/Gfx/FullscreenQuadBuilder.h @@ -0,0 +1,49 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + @class Oryol::FullscreenQuadBuilder + @ingroup Assets + @brief helper class to build a fullscreen quad mesh. + + FIXME: probably makes sense to rewrite this to a single triangle + + Positions (vertex attr name: "position") + 0: -1.0,+1.0,0.0 + 1: +1.0,+1.0,0.0 + 2: -1.0,-1.0,0.0 + 3: +1.0,-1.0,0.0 + + UV Coords (vertex attr name: "texcoord0", FlipV == false: + 0: 0.0, 1.0 + 1: 1.0, 1.0 + 2: 0.0, 0.0 + 3: 1.0, 0.0 + + UV Coords, FlipV == true: + 0: 0.0, 0.0 + 1: 1.0, 0.0 + 2: 0.0, 1.0 + 3: 1.0, 1.0 +*/ +#include "Gfx/GfxTypes.h" + +namespace Oryol { + +class FullscreenQuadBuilder { +public: + struct Result { + BufferDesc VertexBufferDesc; + struct PipelineDesc PipelineDesc; // blueprint pipeline desc + int NumElements = 4; // rendered as triangle strip + }; + bool flipV = false; + + // texture v coord is (1.0 - y) + FullscreenQuadBuilder& FlipV(bool b) { flipV = b; return *this; } + bool FlipV() const { return flipV; } + + // populate and return Result object + Result Build(); +}; + +} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/MeshBuilder.cc b/code/Modules/Assets/Gfx/MeshBuilder.cc index bcd851be9..6694d7bdd 100644 --- a/code/Modules/Assets/Gfx/MeshBuilder.cc +++ b/code/Modules/Assets/Gfx/MeshBuilder.cc @@ -4,64 +4,55 @@ #include "Pre.h" #include "MeshBuilder.h" #include "Core/Assertion.h" -#include namespace Oryol { //------------------------------------------------------------------------------ -MeshBuilder& -MeshBuilder::Begin() { - o_assert(!this->inBegin); - o_assert(this->NumVertices > 0); - o_assert(!this->Layout.Empty()); - this->inBegin = true; - - // setup MeshSetup object - MeshSetup& meshSetup = this->setupAndData.Setup; - meshSetup = MeshSetup::FromData(this->VertexUsage, this->IndexUsage); - meshSetup.Layout = this->Layout; - meshSetup.NumVertices = this->NumVertices; - meshSetup.NumIndices = this->NumIndices; - meshSetup.IndicesType = this->IndicesType; - for (const auto& primGroup : this->PrimitiveGroups) { - meshSetup.AddPrimitiveGroup(primGroup); - } - - // compute the required stream size - const int vbSize = Memory::RoundUp(this->NumVertices * this->Layout.ByteSize(), 4); - const int ibSize = this->NumIndices * IndexType::ByteSize(this->IndicesType); +MeshBuilder::Result +MeshBuilder::Build(std::function func) { + o_assert_dbg(!this->inBuild); + o_assert_dbg(this->numVertices > 0); + o_assert_dbg(!this->layout.Empty()); + this->inBuild = true; + + // compute the data buffer size + const int vbSize = this->numVertices * this->layout.ByteSize(); + const int ibSize = this->numIndices * IndexType::ByteSize(this->indexType); int allSize = vbSize + ibSize; - meshSetup.VertexDataOffset = 0; - if (ibSize > 0) { - meshSetup.IndexDataOffset = vbSize; - } - + // setup the data buffer object - this->vertexPointer = this->setupAndData.Data.Add(allSize); + this->vertexPointer = (uint8_t*) Memory::Alloc(allSize); this->indexPointer = this->vertexPointer + vbSize; this->endPointer = this->indexPointer + ibSize; - - return *this; -} -//------------------------------------------------------------------------------ -SetupAndData -MeshBuilder::Build() { - o_assert(this->inBegin); - - this->inBegin = false; - - // NOTE: explicit moves required by VS2013 - SetupAndData result(std::move(this->setupAndData)); - - // clear private data, not configuration data + // call the content-build function + func(*this); + + // setup the Result object + Result res; + res.Data.MoveRaw(this->vertexPointer, allSize); + res.Layout = this->layout; + res.IndexType = this->indexType; + res.VertexBufferDesc = BufferDesc() + .SetSize(vbSize) + .SetType(BufferType::VertexBuffer) + .SetUsage(this->vertexUsage) + .SetContent(this->vertexPointer); + if (ibSize > 0) { + res.IndexBufferDesc = BufferDesc() + .SetSize(ibSize) + .SetType(BufferType::IndexBuffer) + .SetUsage(this->indexUsage) + .SetContent(this->indexPointer); + } + else { + res.IndexBufferDesc = BufferDesc(); + } + this->inBuild = false; this->vertexPointer = nullptr; this->indexPointer = nullptr; this->endPointer = nullptr; - this->setupAndData.Setup = MeshSetup::FromData(); - this->setupAndData.Data.Clear(); - - return result; + return res; } } // namespace Oryol diff --git a/code/Modules/Assets/Gfx/MeshBuilder.h b/code/Modules/Assets/Gfx/MeshBuilder.h index ee33a06d3..8dddbc1e7 100644 --- a/code/Modules/Assets/Gfx/MeshBuilder.h +++ b/code/Modules/Assets/Gfx/MeshBuilder.h @@ -20,76 +20,69 @@ MeshBuilder the number of vertices and their layout, the number of indices (in case of indexed geometry), the index type (16- or 32-bits) and finally the primitive group definitions, then call - the Begin() method, and start writing vertices and indices, - when done call End() and get the resulting SetupAndData object - object which can be handed to Gfx::CreateResource(). - + the Build() method, and in the callback function write vertices and indices. + The MeshBuilder::Result object returned by the Build() method contains + all the required information to create buffers and pipeline + objects needed to render the mesh data. + Vertex format packing happens on the fly when writing vertex data according to the vertex layout given. - This is the format of the stream data that will be written: - - [1..numVertices] - [1..N bytes per vertex] - [4-byte aligned, 1..numIndices] - [2 or 4 bytes per index] - @see VertexWriter, ShapeBuilder */ #include "Core/Types.h" #include "Gfx/GfxTypes.h" #include "Assets/Gfx/VertexWriter.h" -#include "Resource/SetupAndData.h" +#include namespace Oryol { class MeshBuilder { public: - /// number of vertices - uint32_t NumVertices = 0; - /// number of indices (default 0 for non-indexed meshes) - uint32_t NumIndices = 0; - /// index type (default is 16-bit indices) - IndexType::Code IndicesType = IndexType::Index16; - /// read/write access to vertex layout - class VertexLayout Layout; - /// primitive groups (at least one must be defined) - Array PrimitiveGroups; - /// vertex data usage - Usage::Code VertexUsage = Usage::Immutable; - /// index data usage - Usage::Code IndexUsage = Usage::Immutable; - - /// begin writing vertex and index data - MeshBuilder& Begin(); - /// write 1D component vertex data - MeshBuilder& Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x); - /// write 2D vertex data - MeshBuilder& Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y); - /// write 3D vertex data - MeshBuilder& Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y, float z); - /// write 4D vertex data - MeshBuilder& Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y, float z, float w); - /// write 16-bit vertex-index at index-buffer-index - MeshBuilder& Index(uint32_t index, uint16_t vertexIndex); - /// write 32-bit vertex-index at index-buffer-index - MeshBuilder& Index32(uint32_t index, uint32_t vertexIndex); - /// write 16-bit triangle indices - MeshBuilder& Triangle(uint32_t triangleIndex, uint16_t vertexIndex0, uint16_t vertexIndex1, uint16_t vertexIndex2); - /// write 32-bit triangle indices - MeshBuilder& Triangle32(uint32_t triangleIndex, uint32_t vertexIndex0, uint32_t vertexIndex1, uint32_t vertexIndex2); - /// end writing vertex and index data, return result, and reset MeshBuilfer - SetupAndData Build(); + MeshBuilder& NumVertices(int n) { numVertices = n; return *this; } + int NumVertices() const { return numVertices; } + MeshBuilder& NumIndices(int n) { numIndices = n; return *this; } + int NumIndices() const { return numIndices; } + MeshBuilder& IndexType(Oryol::IndexType::Code t) { indexType = t; return *this; } + Oryol::IndexType::Code IndexType() const { return indexType; } + MeshBuilder& Layout(const VertexLayout& l) { layout = l; return *this; } + const VertexLayout& Layout() const { return layout; } + VertexLayout& Layout() { return layout; } + MeshBuilder& VertexUsage(Usage::Code u) { vertexUsage = u; return *this; } + Usage::Code VertexUsage() const { return vertexUsage; } + MeshBuilder& IndexUsage(Usage::Code u) { indexUsage = u; return *this; } + Usage::Code IndexUsage() const { return indexUsage; } + struct Result { + BufferDesc VertexBufferDesc; + BufferDesc IndexBufferDesc; + VertexLayout Layout; + Oryol::IndexType::Code IndexType; + MemoryBuffer Data; + }; + Result Build(std::function func); + + /// these functions must be called from within the Build() callback + MeshBuilder& Vertex(int vertexIndex, int compIndex, float x); + MeshBuilder& Vertex(int vertexIndex, int compIndex, float x, float y); + MeshBuilder& Vertex(int vertexIndex, int compIndex, float x, float y, float z); + MeshBuilder& Vertex(int vertexIndex, int compIndex, float x, float y, float z, float w); + MeshBuilder& Index(int index, uint16_t vertexIndex); + MeshBuilder& Index32(int index, uint32_t vertexIndex); + MeshBuilder& Triangle(int triangleIndex, uint16_t vertexIndex0, uint16_t vertexIndex1, uint16_t vertexIndex2); + MeshBuilder& Triangle32(int triangleIndex, uint32_t vertexIndex0, uint32_t vertexIndex1, uint32_t vertexIndex2); private: - /// clear/reset the object void clear(); - /// compute byte offset into vertex buffer given vertex and component index - uint32_t vertexByteOffset(uint32_t vertexIndex, int compIndex) const; + int vertexByteOffset(int vertexIndex, int compIndex) const; - SetupAndData setupAndData; - bool inBegin = false; - + int numVertices = 0; + int numIndices = 0; + IndexType::Code indexType = IndexType::UInt16; + Usage::Code vertexUsage = Usage::Immutable; + Usage::Code indexUsage = Usage::Immutable; + VertexLayout layout; + + bool inBuild = false; uint8_t* vertexPointer = nullptr; uint8_t* indexPointer = nullptr; uint8_t* endPointer = nullptr; @@ -97,9 +90,9 @@ class MeshBuilder { //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Index(uint32_t index, uint16_t vertexIndex) { - o_assert_dbg(this->inBegin && (index < this->NumIndices) && (this->IndicesType == IndexType::Index16)); - o_assert_dbg(vertexIndex < this->NumVertices); +MeshBuilder::Index(int index, uint16_t vertexIndex) { + o_assert_dbg(this->inBuild && (index < this->numIndices) && (this->indexType == IndexType::UInt16)); + o_assert_dbg(vertexIndex < this->numVertices); uint16_t* ptr = ((uint16_t*)this->indexPointer) + index; o_assert_dbg(ptr < (uint16_t*)this->endPointer); @@ -109,9 +102,9 @@ MeshBuilder::Index(uint32_t index, uint16_t vertexIndex) { //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Index32(uint32_t index, uint32_t vertexIndex) { - o_assert_dbg(this->inBegin && (index < this->NumIndices) && (this->IndicesType == IndexType::Index32)); - o_assert_dbg(vertexIndex < this->NumVertices); +MeshBuilder::Index32(int index, uint32_t vertexIndex) { + o_assert_dbg(this->inBuild && (index < this->numIndices) && (this->indexType == IndexType::UInt32)); + o_assert_dbg((int)vertexIndex < this->numVertices); uint32_t* ptr = ((uint32_t*)this->indexPointer) + index; o_assert_dbg(ptr < (uint32_t*)this->endPointer); @@ -121,9 +114,9 @@ MeshBuilder::Index32(uint32_t index, uint32_t vertexIndex) { //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Triangle(uint32_t triIndex, uint16_t vi0, uint16_t vi1, uint16_t vi2) { - o_assert_dbg(this->inBegin && (triIndex*3 < this->NumIndices)); - o_assert_dbg((vi0 < this->NumVertices) && (vi1 < this->NumVertices) && (vi2 < this->NumVertices)); +MeshBuilder::Triangle(int triIndex, uint16_t vi0, uint16_t vi1, uint16_t vi2) { + o_assert_dbg(this->inBuild && (triIndex*3 < this->numIndices)); + o_assert_dbg((vi0 < this->numVertices) && (vi1 < this->numVertices) && (vi2 < this->numVertices)); uint16_t* ptr = ((uint16_t*)this->indexPointer) + triIndex * 3; o_assert_dbg(ptr < (uint16_t*)this->endPointer); @@ -132,50 +125,46 @@ MeshBuilder::Triangle(uint32_t triIndex, uint16_t vi0, uint16_t vi1, uint16_t vi } //------------------------------------------------------------------------------ -inline uint32_t -MeshBuilder::vertexByteOffset(uint32_t vertexIndex, int compIndex) const { - o_assert_dbg(vertexIndex < this->NumVertices); +inline int +MeshBuilder::vertexByteOffset(int vertexIndex, int compIndex) const { + o_assert_dbg(vertexIndex < this->numVertices); o_assert_dbg(InvalidIndex != compIndex); - return vertexIndex * this->Layout.ByteSize() + this->Layout.ComponentByteOffset(compIndex); + return vertexIndex * this->layout.ByteSize() + this->layout.ComponentByteOffset(compIndex); } //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x) { - o_assert_dbg(this->inBegin); - const int compIndex = this->Layout.ComponentIndexByVertexAttr(attr); +MeshBuilder::Vertex(int vertexIndex, int compIndex, float x) { + o_assert_dbg(this->inBuild); uint8_t* ptr = this->vertexPointer + this->vertexByteOffset(vertexIndex, compIndex); - VertexWriter::Write(ptr, this->Layout.ComponentAt(compIndex).Format, x); + VertexWriter::Write(ptr, this->layout.ComponentAt(compIndex).Format, x); return *this; } //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y) { - o_assert_dbg(this->inBegin); - const int compIndex = this->Layout.ComponentIndexByVertexAttr(attr); +MeshBuilder::Vertex(int vertexIndex, int compIndex, float x, float y) { + o_assert_dbg(this->inBuild); uint8_t* ptr = this->vertexPointer + this->vertexByteOffset(vertexIndex, compIndex); - VertexWriter::Write(ptr, this->Layout.ComponentAt(compIndex).Format, x, y); + VertexWriter::Write(ptr, this->layout.ComponentAt(compIndex).Format, x, y); return *this; } //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y, float z) { - o_assert_dbg(this->inBegin); - const int compIndex = this->Layout.ComponentIndexByVertexAttr(attr); +MeshBuilder::Vertex(int vertexIndex, int compIndex, float x, float y, float z) { + o_assert_dbg(this->inBuild); uint8_t* ptr = this->vertexPointer + this->vertexByteOffset(vertexIndex, compIndex); - VertexWriter::Write(ptr, this->Layout.ComponentAt(compIndex).Format, x, y, z); + VertexWriter::Write(ptr, this->layout.ComponentAt(compIndex).Format, x, y, z); return *this; } //------------------------------------------------------------------------------ inline MeshBuilder& -MeshBuilder::Vertex(uint32_t vertexIndex, VertexAttr::Code attr, float x, float y, float z, float w) { - o_assert_dbg(this->inBegin); - const int compIndex = this->Layout.ComponentIndexByVertexAttr(attr); +MeshBuilder::Vertex(int vertexIndex, int compIndex, float x, float y, float z, float w) { + o_assert_dbg(this->inBuild); uint8_t* ptr = this->vertexPointer + this->vertexByteOffset(vertexIndex, compIndex); - VertexWriter::Write(ptr, this->Layout.ComponentAt(compIndex).Format, x, y, z, w); + VertexWriter::Write(ptr, this->layout.ComponentAt(compIndex).Format, x, y, z, w); return *this; } diff --git a/code/Modules/Assets/Gfx/MeshLoader.cc b/code/Modules/Assets/Gfx/MeshLoader.cc deleted file mode 100644 index 191c5076d..000000000 --- a/code/Modules/Assets/Gfx/MeshLoader.cc +++ /dev/null @@ -1,91 +0,0 @@ -//------------------------------------------------------------------------------ -// MeshLoader.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "MeshLoader.h" -#include "Assets/Gfx/OmshParser.h" -#include "Gfx/Gfx.h" -#include "Gfx/private/gfxResourceContainer.h" -#include "IO/IO.h" - -namespace Oryol { - -//------------------------------------------------------------------------------ -MeshLoader::MeshLoader(const MeshSetup& setup_) : -MeshLoaderBase(setup_) { - // empty -} - -//------------------------------------------------------------------------------ -MeshLoader::MeshLoader(const MeshSetup& setup_, LoadedFunc loadedFunc_) : -MeshLoaderBase(setup_, loadedFunc_) { - // empty -} - -//------------------------------------------------------------------------------ -MeshLoader::~MeshLoader() { - o_assert_dbg(!this->ioRequest); -} - -//------------------------------------------------------------------------------ -void -MeshLoader::Cancel() { - if (this->ioRequest) { - this->ioRequest->Cancelled = true; - this->ioRequest = nullptr; - } -} - -//------------------------------------------------------------------------------ -Id -MeshLoader::Start() { - this->resId = Gfx::resource()->prepareAsync(this->setup); - this->ioRequest = IO::LoadFile(setup.Locator.Location()); - return this->resId; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -MeshLoader::Continue() { - o_assert_dbg(this->resId.IsValid()); - o_assert_dbg(this->ioRequest.isValid()); - - ResourceState::Code result = ResourceState::Pending; - - if (this->ioRequest->Handled) { - if (IOStatus::OK == this->ioRequest->Status) { - // async loading has finished, use OmshParser to - // create a MeshSetup object from the loaded data - const void* data = this->ioRequest->Data.Data(); - const int numBytes = this->ioRequest->Data.Size(); - - MeshSetup meshSetup = MeshSetup::FromData(this->setup); - if (OmshParser::Parse(data, numBytes, meshSetup)) { - - // call the Loaded callback if defined, this - // gives the app a chance to look at the - // setup object, and possibly modify it - if (this->onLoaded) { - this->onLoaded(meshSetup); - } - - // NOTE: the prepared resource might have already been - // destroyed at this point, if this happens, initAsync will - // silently fail and return ResourceState::InvalidState - // (the same for failedAsync) - result = Gfx::resource()->initAsync(this->resId, meshSetup, data, numBytes); - } - else { - result = Gfx::resource()->failedAsync(this->resId); - } - } - else { - // IO had failed - result = Gfx::resource()->failedAsync(this->resId); - } - this->ioRequest = nullptr; - } - return result; -} - -} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/MeshLoader.h b/code/Modules/Assets/Gfx/MeshLoader.h deleted file mode 100644 index 5cb3b6253..000000000 --- a/code/Modules/Assets/Gfx/MeshLoader.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::MeshLoader - @ingroup Assets - @brief standard mesh loader for loading .omsh files - - NOTE: .omsh files are created by the oryol-exporter tool - in the project https://github.com/floooh/oryol-tools -*/ -#include "Gfx/MeshLoaderBase.h" -#include "IO/private/ioRequests.h" - -namespace Oryol { - -class MeshLoader : public MeshLoaderBase { - OryolClassDecl(MeshLoader); -public: - /// constructor without success-callback - MeshLoader(const MeshSetup& setup); - /// constructor with success callback - MeshLoader(const MeshSetup& setup, LoadedFunc onLoaded); - /// destructor - ~MeshLoader(); - /// start loading, return a resource id - virtual Id Start() override; - /// continue loading, return resource state (Pending, Valid, Failed) - virtual ResourceState::Code Continue() override; - /// cancel the load process - virtual void Cancel() override; -private: - Id resId; - Ptr ioRequest; -}; - -} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/OmshParser.cc b/code/Modules/Assets/Gfx/OmshParser.cc deleted file mode 100644 index 836bccc42..000000000 --- a/code/Modules/Assets/Gfx/OmshParser.cc +++ /dev/null @@ -1,127 +0,0 @@ -//------------------------------------------------------------------------------ -// OmshParser.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "OmshParser.h" - -namespace Oryol { - -//------------------------------------------------------------------------------ -PrimitiveType::Code -OmshParser::translatePrimType(uint32_t omshPrimType) { - switch (omshPrimType) { - case 0: return PrimitiveType::Points; - case 1: return PrimitiveType::Lines; - case 3: return PrimitiveType::LineStrip; - case 4: return PrimitiveType::Triangles; - case 5: return PrimitiveType::TriangleStrip; - default: return PrimitiveType::InvalidPrimitiveType; - } -} - -//------------------------------------------------------------------------------ -bool -OmshParser::Parse(const void* ptr, uint32_t size, MeshSetup& outSetup) { - o_assert_dbg(ptr); - o_assert_dbg(size > 4); - o_assert_dbg(outSetup.NumPrimitiveGroups() == 0); - o_assert_dbg(outSetup.Layout.Empty()); - - // size must be multiple of 4 - if ((size & 3) != 0) { - return false; - } - - const uint32_t* u32StartPtr = (const uint32_t*) ptr; - const uint32_t* u32Ptr = u32StartPtr; - const uint32_t u32Size = size >> 2; - const uint32_t* u32EndPtr = u32StartPtr + u32Size; - - // check if enough data for header - uint32_t u32CheckSize = 7; - if (u32CheckSize > u32Size) { - return false; - } - - // start parsing static header - const uint32_t magic = *u32Ptr++; - if (magic != 'OMSH') { - return false; - } - outSetup.NumVertices = *u32Ptr++; - const uint32_t vertexSize = *u32Ptr++; - outSetup.NumIndices = *u32Ptr++; - const uint32_t indexSize = *u32Ptr++; - if (2 == indexSize) { - outSetup.IndicesType = IndexType::Index16; - } - else if (4 == indexSize) { - outSetup.IndicesType = IndexType::Index32; - } - else { - return false; - } - const uint32_t numVertexAttrs = *u32Ptr++; - if (numVertexAttrs > VertexAttr::NumVertexAttrs) { - return false; - } - const uint32_t numPrimGroups = *u32Ptr++; - if (numPrimGroups > GfxConfig::MaxNumPrimGroups) { - return false; - } - - // check if enough data for vertex components - u32CheckSize += numVertexAttrs * 2; - if (u32CheckSize > u32Size) { - return false; - } - for (uint32_t i = 0; i < numVertexAttrs; i++) { - VertexLayout::Component comp; - comp.Attr = (VertexAttr::Code) *u32Ptr++; - comp.Format = (VertexFormat::Code) *u32Ptr++; - outSetup.Layout.Add(comp); - } - - // check if enough data for primitive groups - u32CheckSize += numPrimGroups * 3; - if (u32CheckSize > u32Size) { - return false; - } - for(uint32_t i = 0; i < numPrimGroups; i++) { - PrimitiveGroup primGroup; - // skip primitive type - u32Ptr++; - primGroup.BaseElement = *u32Ptr++; - primGroup.NumElements = *u32Ptr++; - outSetup.AddPrimitiveGroup(primGroup); - } - - // check if enough data for vertices - const uint32_t u32VertexDataSize = (outSetup.NumVertices * vertexSize) >> 2; - u32CheckSize += u32VertexDataSize; - if (u32CheckSize > u32Size) { - return false; - } - outSetup.VertexDataOffset = int(u32Ptr - u32StartPtr) << 2; - u32Ptr += u32VertexDataSize; - - // check if enough data for indices (index block is padded - // so that size is multiple of 4) - uint32_t indexDataSize = outSetup.NumIndices * indexSize; - if ((indexDataSize & 3) != 0) { - indexDataSize += 2; - o_assert_dbg((indexDataSize & 3) == 0); - } - const uint32_t u32IndexDataSize = indexDataSize >> 2; - u32CheckSize += u32IndexDataSize; - if (u32CheckSize > u32Size) { - return false; - } - outSetup.IndexDataOffset = int(u32Ptr - u32StartPtr) << 2; - u32Ptr += u32IndexDataSize; - - // cleanly reached the end? - return (u32EndPtr == u32Ptr); -} - -} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/OmshParser.h b/code/Modules/Assets/Gfx/OmshParser.h deleted file mode 100644 index 92635d627..000000000 --- a/code/Modules/Assets/Gfx/OmshParser.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::OmshParser - @ingroup Assets - @brief in-memory OMSH mesh file-format parser - - Takes a piece of memory with OMSH data in it, and returns - a MeshSetup object. OMSH mesh data is created by the - oryol-export tool ( https://github.com/floooh/oryol-tools ) - - OMSH file format (see oryol-tools project): - - struct { - uint32 magic = 'OMSH'; - uint32 numVertices; - uint32 vertexSize; // size of one vertex in bytes (guaranteed multiple of 4) - uint32 numIndices; - uint32 indexSize; // size of one index, must be 2 or 4 - uint32 numVertexAttrs; - uint32 numPrimitiveGroups; - struct { - uint32 attr; // values of VertexAttr::Code - uint32 format; // values of VertexFormat::Code - } vertexAttrs[numVertexAttr]; - struct { - uint32 primitiveType; // values of OmshPrimitiveType (see below) - uint32 baseElement; - uint32 numElements; - } primitiveGroups[numPrimitiveGroups]; - uint8 vertexData[numVertices * vertexSize]; - uint8 indexData[numIndices * indexSize]; - - optional: 2 zero-bytes of padding if odd number of 16-bit-indices - }; - - enum OmshPrimitiveType { - Points = 0, - Lines = 1, - LineLoop = 2, - LineStrip = 3, - Triangles = 4, - TriangleStrip = 5, - TriangleFan = 6, - } -*/ -#include "Gfx/GfxTypes.h" - -namespace Oryol { - -class OmshParser { -public: - /// parse block of memory into MeshSetup object - static bool Parse(const void* ptr, uint32_t size, MeshSetup& outSetup); -private: - /// translate OMSH primtype to actual primtype - static PrimitiveType::Code translatePrimType(uint32_t omshPrimType); -}; - -} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/ShapeBuilder.cc b/code/Modules/Assets/Gfx/ShapeBuilder.cc index d20081766..bd5df701c 100644 --- a/code/Modules/Assets/Gfx/ShapeBuilder.cc +++ b/code/Modules/Assets/Gfx/ShapeBuilder.cc @@ -11,12 +11,19 @@ namespace Oryol { //------------------------------------------------------------------------------ -ShapeBuilder::ShapeBuilder() : -RandomColors(false), -curPrimGroupBaseElement(0), -curPrimGroupNumElements(0), -color(1.0f, 1.0f, 1.0f, 1.0f) { - // empty +ShapeBuilder& +ShapeBuilder::RandomColors(bool b) { + this->randomColors = b; + return *this; +} + +//------------------------------------------------------------------------------ +ShapeBuilder& +ShapeBuilder::Positions(const StringAtom& name, VertexFormat::Code fmt) { + o_assert_dbg(this->posIndex == InvalidIndex); + this->posIndex = this->layout.NumComponents(); + this->layout.Add(name, fmt); + return *this; } //------------------------------------------------------------------------------ @@ -28,18 +35,45 @@ ShapeBuilder::Transform(const glm::mat4& m) { //------------------------------------------------------------------------------ ShapeBuilder& -ShapeBuilder::Color(const glm::vec4& c) { +ShapeBuilder::VertexColor(const glm::vec4& c) { this->color = c; return *this; } +//------------------------------------------------------------------------------ +ShapeBuilder& +ShapeBuilder::Normals(const StringAtom& name, VertexFormat::Code fmt) { + o_assert_dbg(this->normalIndex == InvalidIndex); + this->normalIndex = this->layout.NumComponents(); + this->layout.Add(name, fmt); + return *this; +} + +//------------------------------------------------------------------------------ +ShapeBuilder& +ShapeBuilder::TexCoords(const StringAtom& name, VertexFormat::Code fmt) { + o_assert_dbg(this->texCoordIndex == InvalidIndex); + this->texCoordIndex = this->layout.NumComponents(); + this->layout.Add(name, fmt); + return *this; +} + +//------------------------------------------------------------------------------ +ShapeBuilder& +ShapeBuilder::Colors(const Oryol::StringAtom &name, VertexFormat::Code fmt) { + o_assert_dbg(this->colorIndex == InvalidIndex); + this->colorIndex = this->layout.NumComponents(); + this->layout.Add(name, fmt); + return *this; +} + //------------------------------------------------------------------------------ void ShapeBuilder::buildPrimitiveGroup() { o_assert(this->curPrimGroupNumElements > 0); PrimitiveGroup primGroup(this->curPrimGroupBaseElement, this->curPrimGroupNumElements); - this->meshBuilder.PrimitiveGroups.Add(primGroup); + this->primGroups.Add(primGroup); this->curPrimGroupBaseElement += this->curPrimGroupNumElements; this->curPrimGroupNumElements = 0; } @@ -57,7 +91,7 @@ ShapeBuilder::Box(float w, float h, float d, int tiles, bool buildPrimGroup) { shape.f2 = d; shape.i0 = tiles; shape.color = this->color; - this->UpdateNumElements(shape); + this->updateNumElements(shape); this->shapes.Add(shape); this->curPrimGroupNumElements += shape.numTris * 3; if (buildPrimGroup) { @@ -78,7 +112,7 @@ ShapeBuilder::Sphere(float radius, int slices, int stacks, bool buildPrimGroup) shape.i0 = slices; shape.i1 = stacks; shape.color = this->color; - this->UpdateNumElements(shape); + this->updateNumElements(shape); this->shapes.Add(shape); this->curPrimGroupNumElements += shape.numTris * 3; if (buildPrimGroup) { @@ -100,7 +134,7 @@ ShapeBuilder::Cylinder(float radius, float length, int slices, int stacks, bool shape.i0 = slices; shape.i1 = stacks; shape.color = this->color; - this->UpdateNumElements(shape); + this->updateNumElements(shape); this->shapes.Add(shape); this->curPrimGroupNumElements += shape.numTris * 3; if (buildPrimGroup) { @@ -122,7 +156,7 @@ ShapeBuilder::Torus(float ringRadius, float radius, int sides, int rings, bool b shape.i0 = sides; shape.i1 = rings; shape.color = this->color; - this->UpdateNumElements(shape); + this->updateNumElements(shape); this->shapes.Add(shape); this->curPrimGroupNumElements += shape.numTris * 3; if (buildPrimGroup) { @@ -143,7 +177,7 @@ ShapeBuilder::Plane(float w, float d, int tiles, bool buildPrimGroup) { shape.f1 = d; shape.i0 = tiles; shape.color = this->color; - this->UpdateNumElements(shape); + this->updateNumElements(shape); this->shapes.Add(shape); this->curPrimGroupNumElements += shape.numTris * 3; if (buildPrimGroup) { @@ -154,7 +188,7 @@ ShapeBuilder::Plane(float w, float d, int tiles, bool buildPrimGroup) { //------------------------------------------------------------------------------ void -ShapeBuilder::UpdateNumElements(ShapeData& shape) { +ShapeBuilder::updateNumElements(ShapeData& shape) { switch (shape.type) { case BoxShape: { @@ -204,7 +238,7 @@ ShapeBuilder::UpdateNumElements(ShapeData& shape) { } //------------------------------------------------------------------------------ -SetupAndData +ShapeBuilder::Result ShapeBuilder::Build() { o_assert(!this->shapes.Empty()); @@ -213,9 +247,6 @@ ShapeBuilder::Build() { this->buildPrimitiveGroup(); } - // assign vertex layout - this->meshBuilder.Layout = this->Layout; - // overall number of vertices and indices int numVerticesAll = 0; int numIndicesAll = 0; @@ -225,59 +256,60 @@ ShapeBuilder::Build() { } // configure the mesh builder - this->meshBuilder.NumVertices = numVerticesAll; - this->meshBuilder.IndicesType = IndexType::Index16; - this->meshBuilder.NumIndices = numIndicesAll; - this->meshBuilder.Begin(); - int curVertexIndex = 0; - int curTriIndex = 0; - for (const ShapeData& shape : this->shapes) { - switch (shape.type) { - case BoxShape: - this->BuildBox(shape, curVertexIndex, curTriIndex); - break; - case SphereShape: - this->BuildSphere(shape, curVertexIndex, curTriIndex); - break; - case CylinderShape: - this->BuildCylinder(shape, curVertexIndex, curTriIndex); - break; - case TorusShape: - this->BuildTorus(shape, curVertexIndex, curTriIndex); - break; - case PlaneShape: - this->BuildPlane(shape, curVertexIndex, curTriIndex); - break; - default: - o_assert(false); - break; - } - curVertexIndex += shape.numVertices; - curTriIndex += shape.numTris; - } - SetupAndData result = this->meshBuilder.Build(); + auto mesh = MeshBuilder() + .Layout(this->layout) + .NumVertices(numVerticesAll) + .IndexType(IndexType::UInt16) + .NumIndices(numIndicesAll) + .Build([this](MeshBuilder& mb) { + int curVertexIndex = 0; + int curTriIndex = 0; + for (const ShapeData& shape : this->shapes) { + switch (shape.type) { + case BoxShape: this->buildBox(mb, shape, curVertexIndex, curTriIndex); break; + case SphereShape: this->buildSphere(mb, shape, curVertexIndex, curTriIndex); break; + case CylinderShape: this->buildCylinder(mb, shape, curVertexIndex, curTriIndex); break; + case TorusShape: this->buildTorus(mb, shape, curVertexIndex, curTriIndex); break; + case PlaneShape: this->buildPlane(mb, shape, curVertexIndex, curTriIndex); break; + default: o_assert(false); break; + } + curVertexIndex += shape.numVertices; + curTriIndex += shape.numTris; + } + }); + ShapeBuilder::Result shapeResult; + shapeResult.VertexBufferDesc = std::move(mesh.VertexBufferDesc); + shapeResult.IndexBufferDesc = std::move(mesh.IndexBufferDesc); + shapeResult.PipelineDesc.Layouts[0] = std::move(mesh.Layout); + shapeResult.PipelineDesc.IndexType = mesh.IndexType; + shapeResult.Data = std::move(mesh.Data); + shapeResult.PrimitiveGroups = std::move(this->primGroups); // clear private data (but not config params) this->curPrimGroupBaseElement = 0; this->curPrimGroupNumElements = 0; + this->posIndex = InvalidIndex; + this->normalIndex = InvalidIndex; + this->texCoordIndex = InvalidIndex; + this->colorIndex = InvalidIndex; this->transform = glm::mat4(); this->color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); this->shapes.Clear(); - this->meshBuilder.PrimitiveGroups.Clear(); + this->primGroups.Clear(); - return result; + return shapeResult; } //------------------------------------------------------------------------------ void -ShapeBuilder::BuildVertexColors(const ShapeData& shape, int startVertexIndex) { - o_assert(this->meshBuilder.Layout.Contains(VertexAttr::Color0)); - if (this->RandomColors) { +ShapeBuilder::buildVertexColors(MeshBuilder& mb, const ShapeData& shape, int startVertexIndex) { + o_assert(InvalidIndex != this->colorIndex); + if (this->randomColors) { const glm::vec3 minRand(0.0f, 0.0f, 0.0f); const glm::vec3 maxRand(1.0f, 1.0f, 1.0f); for (int i = 0; i < shape.numVertices; i++) { glm::vec3 rnd = glm::linearRand(minRand, maxRand); - this->meshBuilder.Vertex(startVertexIndex + i, VertexAttr::Color0, rnd.x, rnd.y, rnd.z, 1.0f); + mb.Vertex(startVertexIndex + i, this->colorIndex, rnd.x, rnd.y, rnd.z, 1.0f); } } else { @@ -286,16 +318,15 @@ ShapeBuilder::BuildVertexColors(const ShapeData& shape, int startVertexIndex) { const float b = shape.color.z; const float a = shape.color.w; for (int i = 0; i < shape.numVertices; i++) { - this->meshBuilder.Vertex(startVertexIndex + i, VertexAttr::Color0, r, g, b, a); + mb.Vertex(startVertexIndex + i, this->colorIndex, r, g, b, a); } } } //------------------------------------------------------------------------------ void -ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriIndex) { - const auto& vertexLayout = this->meshBuilder.Layout; - o_assert(vertexLayout.Contains(VertexAttr::Position)); +ShapeBuilder::buildBox(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex) { + o_assert(InvalidIndex != this->posIndex); const int startVertexIndex = curVertexIndex; const int numTiles = shape.i0; @@ -315,8 +346,8 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd const float dz = d / numTiles; const float duv = 1.0f / numTiles; - const bool hasNormals = vertexLayout.Contains(VertexAttr::Normal); - const bool hasTexCoords = vertexLayout.Contains(VertexAttr::TexCoord0); + const bool hasNormals = InvalidIndex != this->normalIndex; + const bool hasTexCoords = InvalidIndex != this->texCoordIndex; // bottom/top plane vertices glm::vec4 pos(0.0f, 0.0f, 0.0f, 1.0f); @@ -337,12 +368,12 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd for (int iz = 0; iz <= numTiles; iz++) { pos.z = z0 + dz * iz; glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormals) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } if (hasTexCoords) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::TexCoord0, ix * duv, iz * duv); + mb.Vertex(curVertexIndex, this->texCoordIndex, ix * duv, iz * duv); } curVertexIndex++; } @@ -366,12 +397,12 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd for (int iz = 0; iz <= numTiles; iz++) { pos.z = z0 + dz * iz; glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormals) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } if (hasTexCoords) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::TexCoord0, iy * duv, iz * duv); + mb.Vertex(curVertexIndex, this->texCoordIndex, iy * duv, iz * duv); } curVertexIndex++; } @@ -395,12 +426,12 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd for (int iy = 0; iy <= numTiles; iy++) { pos.y = y0 + dy * iy; glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormals) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } if (hasTexCoords) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::TexCoord0, ix * duv, iy * duv); + mb.Vertex(curVertexIndex, this->texCoordIndex, ix * duv, iy * duv); } curVertexIndex++; } @@ -408,16 +439,10 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd } o_assert((curVertexIndex - startVertexIndex) == shape.numVertices); - if (vertexLayout.Contains(VertexAttr::Color0)) { - this->BuildVertexColors(shape, startVertexIndex); - } - if (vertexLayout.Contains(VertexAttr::Binormal)) { - o_warn("FIXME: ShapeBuilder::BuildBox() binormals not implemented yet!\n"); + if (InvalidIndex != this->colorIndex) { + this->buildVertexColors(mb, shape, startVertexIndex); } - if (vertexLayout.Contains(VertexAttr::Tangent)) { - o_warn("FIXME: ShapeBuilder::BuildBox() tangents not implemented yet!\n"); - } - + // write indices ORYOL_UNUSED const int startTriIndex = curTriIndex; for (int face = 0; face < 6; face++) { @@ -431,8 +456,8 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd uint16_t i3 = i2 + 1; // the 2 tile triangles - this->meshBuilder.Triangle(curTriIndex++, i0, i1, i3); - this->meshBuilder.Triangle(curTriIndex++, i0, i3, i2); + mb.Triangle(curTriIndex++, i0, i1, i3); + mb.Triangle(curTriIndex++, i0, i3, i2); } } } @@ -458,9 +483,8 @@ ShapeBuilder::BuildBox(const ShapeData& shape, int curVertexIndex, int curTriInd + + + + + + south pole */ void -ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTriIndex) { - const auto& vertexLayout = this->meshBuilder.Layout; - o_assert(vertexLayout.Contains(VertexAttr::Position)); +ShapeBuilder::buildSphere(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex) { + o_assert(InvalidIndex != this->posIndex); const int startVertexIndex = curVertexIndex; const int numSlices = shape.i0; const int numStacks = shape.i1; @@ -470,9 +494,9 @@ ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTri const float du = 1.0f / numSlices; const float dv = 1.0f / numStacks; - bool hasNormals = vertexLayout.Contains(VertexAttr::Normal); - bool hasTexCoords = vertexLayout.Contains(VertexAttr::TexCoord0); - + const bool hasNormals = InvalidIndex != this->normalIndex; + const bool hasTexCoords = InvalidIndex != this->texCoordIndex; + for (int stack = 0; stack <= numStacks; stack++) { const float stackAngle = (pi * stack) / numStacks; const float sinStack = glm::sin(stackAngle); @@ -484,35 +508,29 @@ ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTri const glm::vec3 norm(sinSlice * sinStack, cosSlice * sinStack, cosStack); const glm::vec4 pos(norm * radius, 1.0f); const glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormals) { const glm::vec4 tnorm = shape.transform * glm::vec4(norm, 0.0f); - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, tnorm.x, tnorm.y, tnorm.z); + mb.Vertex(curVertexIndex, this->normalIndex, tnorm.x, tnorm.y, tnorm.z); } if (hasTexCoords) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::TexCoord0, du * slice, dv * stack); + mb.Vertex(curVertexIndex, this->texCoordIndex, du * slice, dv * stack); } curVertexIndex++; } } o_assert((curVertexIndex - startVertexIndex) == shape.numVertices); - if (vertexLayout.Contains(VertexAttr::Color0)) { - this->BuildVertexColors(shape, startVertexIndex); + if (InvalidIndex != this->colorIndex) { + this->buildVertexColors(mb, shape, startVertexIndex); } - if (vertexLayout.Contains(VertexAttr::Binormal)) { - o_warn("FIXME: ShapeBuilder::BuildSphere() binormals not implemented yet!\n"); - } - if (vertexLayout.Contains(VertexAttr::Tangent)) { - o_warn("FIXME: ShapeBuilder::BuildSphere() tangents not implemented yet!\n"); - } - + // north-pole triangles ORYOL_UNUSED const int startTriIndex = curTriIndex; int rowA = startVertexIndex; int rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice, rowB + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice, rowB + slice + 1); } // stack triangles @@ -520,8 +538,8 @@ ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTri rowA = startVertexIndex + stack * (numSlices + 1); rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowA + slice + 1); - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice, rowB + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowA + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice, rowB + slice + 1); } } @@ -529,9 +547,9 @@ ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTri rowA = startVertexIndex + (numStacks - 1) * (numSlices + 1); rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowA + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowA + slice + 1); } - o_assert((curTriIndex - startTriIndex) == shape.numTris); + o_assert_dbg((curTriIndex - startTriIndex) == shape.numTris); } //------------------------------------------------------------------------------ @@ -556,9 +574,8 @@ ShapeBuilder::BuildSphere(const ShapeData& shape, int curVertexIndex, int curTri */ void -ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curTriIndex) { - const auto& vertexLayout = this->meshBuilder.Layout; - o_assert(vertexLayout.Contains(VertexAttr::Position)); +ShapeBuilder::buildCylinder(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex) { + o_assert(InvalidIndex != this->posIndex); const int startVertexIndex = curVertexIndex; const int numSlices = shape.i0; const int numStacks = shape.i1; @@ -566,7 +583,7 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT const float length = shape.f2; const float pi = glm::pi(); const float twoPi = 2.0f * pi; - const bool hasNormal = vertexLayout.Contains(VertexAttr::Normal); + const bool hasNormal = InvalidIndex != this->normalIndex; // north cap center vertices glm::vec4 norm = shape.transform * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f); @@ -574,9 +591,9 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT glm::vec4 cpos(0.0f, y, 0.0f, 1.0f); glm::vec4 tcpos = shape.transform * cpos; for (int slice = 0; slice <= numSlices; slice++) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tcpos.x, tcpos.y, tcpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tcpos.x, tcpos.y, tcpos.z); if (hasNormal) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } @@ -588,9 +605,9 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT const float cosSlice = glm::cos(sliceAngle); const glm::vec4 pos(sinSlice * radius, y, cosSlice * radius, 1.0f); const glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormal) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } @@ -605,11 +622,11 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT const float cosSlice = glm::cos(sliceAngle); const glm::vec4 pos(sinSlice * radius, y, cosSlice * radius, 1.0f); const glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormal) { glm::vec4 norm(sinSlice, 0.0f, cosSlice, 0.0f); norm = shape.transform * norm; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } @@ -624,9 +641,9 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT const float cosSlice = glm::cos(sliceAngle); const glm::vec4 pos(sinSlice * radius, y, cosSlice * radius, 1.0f); const glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormal) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } @@ -635,25 +652,19 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT cpos = glm::vec4(0.0f, y, 0.0f, 1.0f); tcpos = shape.transform * cpos; for (int slice = 0; slice <= numSlices; slice++) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tcpos.x, tcpos.y, tcpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tcpos.x, tcpos.y, tcpos.z); if (hasNormal) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } o_assert((curVertexIndex - startVertexIndex) == shape.numVertices); - if (vertexLayout.Contains(VertexAttr::Color0)) { - this->BuildVertexColors(shape, startVertexIndex); - } - if (vertexLayout.Contains(VertexAttr::Binormal)) { - o_warn("FIXME: ShapeBuilder::BuildCylinder() binormals not implemented yet!\n"); + if (InvalidIndex != this->colorIndex) { + this->buildVertexColors(mb, shape, startVertexIndex); } - if (vertexLayout.Contains(VertexAttr::Tangent)) { - o_warn("FIXME: ShapeBuilder::BuildCylinder() tangents not implemented yet!\n"); - } - if (vertexLayout.Contains(VertexAttr::TexCoord0)) { - o_warn("FIXME: ShapeBuilder::BuildCylinder() texcoord not implemented yet!\n"); + if (InvalidIndex != this->texCoordIndex) { + o_warn("FIXME: ShapeBuilder::buildCylinder() texcoord not implemented yet!\n"); } // north cap triangles @@ -661,7 +672,7 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT int rowA = startVertexIndex; int rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowB + slice); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowB + slice); } // shaft triangles @@ -669,8 +680,8 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT rowA = startVertexIndex + (stack + 2) * (numSlices + 1); rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowA + slice + 1, rowB + slice + 1); - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowB + slice); + mb.Triangle(curTriIndex++, rowA + slice, rowA + slice + 1, rowB + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowB + slice + 1, rowB + slice); } } @@ -678,7 +689,7 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT rowA = startVertexIndex + (numStacks + 3) * (numSlices + 1); rowB = rowA + numSlices + 1; for (int slice = 0; slice < numSlices; slice++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + slice, rowA + slice + 1, rowB + slice + 1); + mb.Triangle(curTriIndex++, rowA + slice, rowA + slice + 1, rowB + slice + 1); } o_assert((curTriIndex - startTriIndex) == shape.numTris); } @@ -703,9 +714,8 @@ ShapeBuilder::BuildCylinder(const ShapeData& shape, int curVertexIndex, int curT */ void -ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriIndex) { - const auto& vertexLayout = this->meshBuilder.Layout; - o_assert(vertexLayout.Contains(VertexAttr::Position)); +ShapeBuilder::buildTorus(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex) { + o_assert(InvalidIndex != this->posIndex); const int startVertexIndex = curVertexIndex; static const float ringRadius = shape.f0; static const float radius = shape.f1; @@ -713,7 +723,7 @@ ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriI static const int numRings = shape.i1; const float pi = glm::pi(); const float twoPi = 2.0f * pi; - const bool hasNormals = vertexLayout.Contains(VertexAttr::Normal); + const bool hasNormals = InvalidIndex != this->normalIndex; // vertex positions for (int side = 0; side <= numSides; side++) { @@ -732,7 +742,7 @@ ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriI // surface position const glm::vec4 tpos = shape.transform * glm::vec4(spx, spy, spz, 1.0f); - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); // surface normal if (hasNormals) { @@ -742,24 +752,18 @@ ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriI const float ipz = 0.0f; glm::vec4 norm = glm::normalize(glm::vec4(spx - ipx, spy - ipy, spz - ipz, 0.0f)); norm = shape.transform * norm; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } curVertexIndex++; } } o_assert((curVertexIndex - startVertexIndex) == shape.numVertices); - if (vertexLayout.Contains(VertexAttr::Color0)) { - this->BuildVertexColors(shape, startVertexIndex); - } - if (vertexLayout.Contains(VertexAttr::Binormal)) { - o_warn("FIXME: ShapeBuilder::BuildTorus() binormals not implemented yet!\n"); - } - if (vertexLayout.Contains(VertexAttr::Tangent)) { - o_warn("FIXME: ShapeBuilder::BuildTorus() tangents not implemented yet!\n"); + if (InvalidIndex != this->colorIndex) { + this->buildVertexColors(mb, shape, startVertexIndex); } - if (vertexLayout.Contains(VertexAttr::TexCoord0)) { - o_warn("FIXME: ShapeBuilder::BuildTorus() texcoord not implemented yet!\n"); + if (InvalidIndex != this->texCoordIndex) { + o_warn("FIXME: ShapeBuilder::buildTorus() texcoord not implemented yet!\n"); } // triangles @@ -768,8 +772,8 @@ ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriI const int rowA = startVertexIndex + side * (numRings + 1); const int rowB = rowA + numRings + 1; for (int ring = 0; ring < numRings; ring++) { - this->meshBuilder.Triangle(curTriIndex++, rowA + ring, rowA + ring + 1, rowB + ring + 1); - this->meshBuilder.Triangle(curTriIndex++, rowA + ring, rowB + ring + 1, rowB + ring); + mb.Triangle(curTriIndex++, rowA + ring, rowA + ring + 1, rowB + ring + 1); + mb.Triangle(curTriIndex++, rowA + ring, rowB + ring + 1, rowB + ring); } } o_assert((curTriIndex - startTriIndex) == shape.numTris); @@ -794,9 +798,8 @@ ShapeBuilder::BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriI +--+--+--+--+ */ void -ShapeBuilder::BuildPlane(const ShapeData& shape, int curVertexIndex, int curTriIndex) { - const auto& vertexLayout = this->meshBuilder.Layout; - o_assert(vertexLayout.Contains(VertexAttr::Position)); +ShapeBuilder::buildPlane(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex) { + o_assert(InvalidIndex != this->posIndex); const int startVertexIndex = curVertexIndex; const int numTiles = shape.i0; @@ -808,8 +811,8 @@ ShapeBuilder::BuildPlane(const ShapeData& shape, int curVertexIndex, int curTriI const float dz = -d / numTiles; const float duv = 1.0f / numTiles; - const bool hasNormal = vertexLayout.Contains(VertexAttr::Normal); - const bool hasTexCoords = vertexLayout.Contains(VertexAttr::TexCoord0); + const bool hasNormal = InvalidIndex != this->normalIndex; + const bool hasTexCoords = InvalidIndex != this->texCoordIndex; // vertices glm::vec4 pos(0.0f, 0.0f, 0.0f, 1.0f); @@ -819,26 +822,20 @@ ShapeBuilder::BuildPlane(const ShapeData& shape, int curVertexIndex, int curTriI for (int iz = 0; iz <= numTiles; iz++) { pos.z = z0 + dz * iz; glm::vec4 tpos = shape.transform * pos; - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Position, tpos.x, tpos.y, tpos.z); + mb.Vertex(curVertexIndex, this->posIndex, tpos.x, tpos.y, tpos.z); if (hasNormal) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::Normal, norm.x, norm.y, norm.z); + mb.Vertex(curVertexIndex, this->normalIndex, norm.x, norm.y, norm.z); } if (hasTexCoords) { - this->meshBuilder.Vertex(curVertexIndex, VertexAttr::TexCoord0, ix * duv, iz * duv); + mb.Vertex(curVertexIndex, this->texCoordIndex, ix * duv, iz * duv); } curVertexIndex++; } } o_assert((curVertexIndex - startVertexIndex) == shape.numVertices); - if (vertexLayout.Contains(VertexAttr::Color0)) { - this->BuildVertexColors(shape, startVertexIndex); - } - if (vertexLayout.Contains(VertexAttr::Binormal)) { - o_warn("FIXME: ShapeBuilder::BuildPlane() binormals not implemented yet!\n"); - } - if (vertexLayout.Contains(VertexAttr::Tangent)) { - o_warn("FIXME: ShapeBuilder::BuildPlane() tangents not implemented yet!\n"); + if (InvalidIndex != this->colorIndex) { + this->buildVertexColors(mb, shape, startVertexIndex); } // write indices @@ -852,11 +849,11 @@ ShapeBuilder::BuildPlane(const ShapeData& shape, int curVertexIndex, int curTriI uint16_t i3 = i2 + 1; // the 2 tile triangles - this->meshBuilder.Triangle(curTriIndex++, i0, i1, i3); - this->meshBuilder.Triangle(curTriIndex++, i0, i3, i2); + mb.Triangle(curTriIndex++, i0, i1, i3); + mb.Triangle(curTriIndex++, i0, i3, i2); } } o_assert((curTriIndex - startTriIndex) == shape.numTris); } -} // namespace Oryol \ No newline at end of file +} // namespace Oryol diff --git a/code/Modules/Assets/Gfx/ShapeBuilder.h b/code/Modules/Assets/Gfx/ShapeBuilder.h index 163b9181a..b19112faf 100644 --- a/code/Modules/Assets/Gfx/ShapeBuilder.h +++ b/code/Modules/Assets/Gfx/ShapeBuilder.h @@ -7,7 +7,9 @@ @todo describe ShapeBuilder */ #include "Assets/Gfx/MeshBuilder.h" +#include "Core/String/StringAtom.h" #include "Core/Containers/Array.h" +#include "Core/Containers/InlineArray.h" #include "glm/mat4x4.hpp" #include "glm/vec4.hpp" @@ -15,18 +17,20 @@ namespace Oryol { class ShapeBuilder { public: - /// constructor - ShapeBuilder(); - - /// read/write access to vertex layout - class VertexLayout Layout; - /// random-vertex-colors flag - bool RandomColors; - + /// declare position vertex components + ShapeBuilder& Positions(const StringAtom& name, VertexFormat::Code fmt); + /// declare normal vertex components + ShapeBuilder& Normals(const StringAtom& name, VertexFormat::Code fmt); + /// declare texture coords vertex components + ShapeBuilder& TexCoords(const StringAtom& name, VertexFormat::Code fmt); + /// declare color + ShapeBuilder& Colors(const StringAtom& name, VertexFormat::Code fmt); + /// enable random vertex colors + ShapeBuilder& RandomColors(bool b); /// put new transform ShapeBuilder& Transform(const glm::mat4& t); - /// put new color - ShapeBuilder& Color(const glm::vec4& c); + /// put a new vertex color + ShapeBuilder& VertexColor(const glm::vec4& c); /// add a box shape ShapeBuilder& Box(float w, float h, float d, int tiles, bool buildPrimGroup=true); /// add a sphere shape @@ -37,9 +41,17 @@ class ShapeBuilder { ShapeBuilder& Torus(float ringRadius, float radius, int sides, int rings, bool builPrimGroup=true); /// add a plane ShapeBuilder& Plane(float w, float d, int tiles, bool buildPrimGroup=true); - + + /// result struct + struct Result { + BufferDesc VertexBufferDesc; + BufferDesc IndexBufferDesc; + struct PipelineDesc PipelineDesc; + MemoryBuffer Data; + Array PrimitiveGroups; + }; /// build geometry and clear object state - SetupAndData Build(); + Result Build(); private: enum ShapeType { @@ -63,29 +75,27 @@ class ShapeBuilder { int numTris; }; - /// update number of vertices and triangles in shape - void UpdateNumElements(ShapeData& shapeData); - /// helper method: build vertex colors - void BuildVertexColors(const ShapeData& shape, int startVertexIndex); - /// build box vertices and indices - void BuildBox(const ShapeData& shape, int curVertexIndex, int curTriIndex); - /// build sphere vertices and indices - void BuildSphere(const ShapeData& shape, int curVertexIndex, int curTriIndex); - /// build cylinder vertices and indices - void BuildCylinder(const ShapeData& shape, int curVertexIndex, int curTriIndex); - /// build torus vertices and indices - void BuildTorus(const ShapeData& shape, int curVertexIndex, int curTriIndex); - /// build plane vertices and indices - void BuildPlane(const ShapeData& shape, int curVertexIndex, int curTriIndex); - /// build a primitive group + void updateNumElements(ShapeData& shapeData); + void buildVertexColors(MeshBuilder& mb, const ShapeData& shape, int startVertexIndex); + void buildBox(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex); + void buildSphere(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex); + void buildCylinder(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex); + void buildTorus(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex); + void buildPlane(MeshBuilder& mb, const ShapeData& shape, int curVertexIndex, int curTriIndex); void buildPrimitiveGroup(); - - int curPrimGroupBaseElement; - int curPrimGroupNumElements; + + bool randomColors = false; + int curPrimGroupBaseElement = 0; + int curPrimGroupNumElements = 0; + int posIndex = InvalidIndex; + int normalIndex = InvalidIndex; + int texCoordIndex = InvalidIndex; + int colorIndex = InvalidIndex; glm::mat4 transform; - glm::vec4 color; + glm::vec4 color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); Array shapes; - MeshBuilder meshBuilder; + Array primGroups; + VertexLayout layout; }; - + } // namespace Oryol diff --git a/code/Modules/Assets/Gfx/TextureLoader.cc b/code/Modules/Assets/Gfx/TextureLoader.cc index 20eb116c7..7961723a7 100644 --- a/code/Modules/Assets/Gfx/TextureLoader.cc +++ b/code/Modules/Assets/Gfx/TextureLoader.cc @@ -5,104 +5,20 @@ #include "TextureLoader.h" #include "IO/IO.h" #include "Gfx/Gfx.h" -#include "Gfx/private/gfxResourceContainer.h" #define GLIML_ASSERT(x) o_assert(x) #include "gliml.h" namespace Oryol { //------------------------------------------------------------------------------ -TextureLoader::TextureLoader(const TextureSetup& setup_) : -TextureLoaderBase(setup_) { - // empty -} - -//------------------------------------------------------------------------------ -TextureLoader::TextureLoader(const TextureSetup& setup_, LoadedFunc loadedFunc_) : -TextureLoaderBase(setup_, loadedFunc_) { - // empty -} - -//------------------------------------------------------------------------------ -TextureLoader::~TextureLoader() { - o_assert_dbg(!this->ioRequest); -} - -//------------------------------------------------------------------------------ -void -TextureLoader::Cancel() { - if (this->ioRequest) { - this->ioRequest->Cancelled = true; - this->ioRequest = nullptr; - } -} - -//------------------------------------------------------------------------------ -Id -TextureLoader::Start() { - this->resId = Gfx::resource()->prepareAsync(this->setup); - this->ioRequest = IO::LoadFile(setup.Locator.Location()); - return this->resId; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -TextureLoader::Continue() { - o_assert_dbg(this->resId.IsValid()); - o_assert_dbg(this->ioRequest.isValid()); - - ResourceState::Code result = ResourceState::Pending; - - if (this->ioRequest->Handled) { - if (IOStatus::OK == this->ioRequest->Status) { - // yeah, IO is done, let gliml parse the texture data - // and create the texture resource - const uint8_t* data = this->ioRequest->Data.Data(); - const int numBytes = this->ioRequest->Data.Size(); - - gliml::context ctx; - ctx.enable_dxt(true); - ctx.enable_pvrtc(true); - ctx.enable_etc2(true); - if (ctx.load(data, numBytes)) { - TextureSetup texSetup = this->buildSetup(this->setup, &ctx, data); - - // call the Loaded callback if defined, this - // gives the app a chance to look at the - // setup object, and possibly modify it - if (this->onLoaded) { - this->onLoaded(texSetup); - } - - // NOTE: the prepared texture resource might have already been - // destroyed at this point, if this happens, initAsync will - // silently fail and return ResourceState::InvalidState - // (the same for failedAsync) - result = Gfx::resource()->initAsync(this->resId, texSetup, data, numBytes); - } - else { - result = Gfx::resource()->failedAsync(this->resId); - } - } - else { - // IO had failed - result = Gfx::resource()->failedAsync(this->resId); - } - this->ioRequest = nullptr; - } - return result; -} - -//------------------------------------------------------------------------------ -TextureSetup -TextureLoader::buildSetup(const TextureSetup& blueprint, const gliml::context* ctx, const uint8_t* data) { - const int w = ctx->image_width(0, 0); - const int h = ctx->image_height(0, 0); - const int d = ctx->image_depth(0, 0); - const int numFaces = ctx->num_faces(); - const int numMips = ctx->num_mipmaps(0); - PixelFormat::Code pixelFormat = PixelFormat::InvalidPixelFormat; - switch(ctx->image_internal_format()) { +static TextureDesc buildDesc(const TextureDesc& blueprint, const gliml::context& ctx, const uint8_t* data) { + const int w = ctx.image_width(0, 0); + const int h = ctx.image_height(0, 0); + const int d = ctx.image_depth(0, 0); + const int numFaces = ctx.num_faces(); + const int numMips = ctx.num_mipmaps(0); + PixelFormat::Code pixelFormat = PixelFormat::Invalid; + switch(ctx.image_internal_format()) { case GLIML_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: pixelFormat = PixelFormat::DXT1; break; @@ -131,16 +47,16 @@ TextureLoader::buildSetup(const TextureSetup& blueprint, const gliml::context* c pixelFormat = PixelFormat::ETC2_SRGB8; break; case GLIML_GL_RGB: - if (ctx->image_type() == GLIML_GL_UNSIGNED_BYTE) { + if (ctx.image_type() == GLIML_GL_UNSIGNED_BYTE) { pixelFormat = PixelFormat::RGB8; } - else if (ctx->image_type() == GLIML_GL_UNSIGNED_SHORT_5_6_5) { + else if (ctx.image_type() == GLIML_GL_UNSIGNED_SHORT_5_6_5) { pixelFormat = PixelFormat::R5G6B5; } break; - + case GLIML_GL_RGBA: - switch (ctx->image_type()) { + switch (ctx.image_type()) { case GLIML_GL_UNSIGNED_BYTE: pixelFormat = PixelFormat::RGBA8; break; @@ -154,37 +70,63 @@ TextureLoader::buildSetup(const TextureSetup& blueprint, const gliml::context* c break; } break; - + default: break; } - o_assert(PixelFormat::InvalidPixelFormat != pixelFormat); - TextureSetup newSetup; - switch (ctx->texture_target()) { + o_assert(PixelFormat::Invalid != pixelFormat); + auto desc = TextureDesc(blueprint) + .SetWidth(w) + .SetHeight(h) + .SetNumMipMaps(numMips) + .SetFormat(pixelFormat); + switch (ctx.texture_target()) { case GLIML_GL_TEXTURE_2D: - newSetup = TextureSetup::FromPixelData2D(w, h, numMips, pixelFormat, this->setup); + desc.SetType(TextureType::Texture2D); break; case GLIML_GL_TEXTURE_3D: - newSetup = TextureSetup::FromPixelData3D(w, h, d, numMips, pixelFormat, this->setup); + desc.SetType(TextureType::Texture3D) + .SetDepth(d); break; case GLIML_GL_TEXTURE_CUBE_MAP: - newSetup = TextureSetup::FromPixelDataCube(w, h, numMips, pixelFormat, this->setup); + desc.SetType(TextureType::TextureCube); break; default: o_error("Unknown texture type!\n"); break; } - - // setup mipmap offsets - o_assert_dbg(GfxConfig::MaxNumTextureMipMaps >= ctx->num_mipmaps(0)); + + // setup mipmap content + o_assert_dbg(GfxConfig::MaxNumTextureMipMaps >= ctx.num_mipmaps(0)); for (int faceIndex = 0; faceIndex < numFaces; faceIndex++) { for (int mipIndex = 0; mipIndex < numMips; mipIndex++) { - const uint8_t* cur = (const uint8_t*) ctx->image_data(faceIndex, mipIndex); - newSetup.ImageData.Offsets[faceIndex][mipIndex] = int(cur - data); - newSetup.ImageData.Sizes[faceIndex][mipIndex] = ctx->image_size(faceIndex, mipIndex); + desc.SetMipContent(faceIndex, mipIndex, ctx.image_data(faceIndex, mipIndex)); + desc.SetMipSize(faceIndex, mipIndex, ctx.image_size(faceIndex, mipIndex)); } } - return newSetup; + return desc; +} + +//------------------------------------------------------------------------------ +Id +TextureLoader::Load(const TextureDesc& desc) { + Id resId = Gfx::AllocTexture(desc.Locator); + IO::Load(URL(desc.Locator.Location()), [resId, desc](IO::LoadResult result) { + const uint8_t* data = result.Data.Data(); + const int dataSize = result.Data.Size(); + gliml::context ctx; + ctx.enable_dxt(true); + ctx.enable_pvrtc(true); + ctx.enable_etc2(true); + if (ctx.load(data, dataSize)) { + TextureDesc initDesc = buildDesc(desc, ctx, data); + Gfx::InitTexture(resId, initDesc); + } + }, + [resId](const URL& url, IOStatus::Code ioStatus) { + Gfx::FailTexture(resId); + }); + return resId; } } // namespace Oryol diff --git a/code/Modules/Assets/Gfx/TextureLoader.h b/code/Modules/Assets/Gfx/TextureLoader.h index 2713d4e92..ed1b7c34c 100644 --- a/code/Modules/Assets/Gfx/TextureLoader.h +++ b/code/Modules/Assets/Gfx/TextureLoader.h @@ -5,37 +5,14 @@ @ingroup Assets @brief standard texture loader for most block-compressed texture file formats */ -#include "Gfx/TextureLoaderBase.h" -#include "IO/private/ioRequests.h" - -namespace gliml { -class context; -} +#include "Gfx/GfxTypes.h" namespace Oryol { -class TextureLoader : public TextureLoaderBase { - OryolClassDecl(TextureLoader); +class TextureLoader { public: - /// constructor without success-callback - TextureLoader(const TextureSetup& setup); - /// constructor with success callback - TextureLoader(const TextureSetup& setup, LoadedFunc onLoaded); - /// destructor - ~TextureLoader(); - /// start loading, return a resource id - virtual Id Start() override; - /// continue loading, return resource state (Pending, Valid, Failed) - virtual ResourceState::Code Continue() override; - /// cancel the load process - virtual void Cancel() override; - -private: - /// convert gliml context attrs into a TextureSetup object - TextureSetup buildSetup(const TextureSetup& blueprint, const gliml::context* ctx, const uint8_t* data); - - Id resId; - Ptr ioRequest; + /// asynchronously load a texture from an URL + static Id Load(const TextureDesc& desc); }; -} // namespace Oryol \ No newline at end of file +} // namespace Oryol diff --git a/code/Modules/Assets/UnitTests/MeshBuilderTest.cc b/code/Modules/Assets/UnitTests/MeshBuilderTest.cc deleted file mode 100644 index 932a8c5b5..000000000 --- a/code/Modules/Assets/UnitTests/MeshBuilderTest.cc +++ /dev/null @@ -1,86 +0,0 @@ -//------------------------------------------------------------------------------ -// MeshBuilderTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Assets/Gfx/MeshBuilder.h" -#include - -using namespace Oryol; - -//------------------------------------------------------------------------------ -TEST(MeshBuilderTest) { - - // build a quad with 4 vertices and 2 triangles - MeshBuilder mb; - mb.NumVertices = 4; - mb.NumIndices = 6; - mb.IndicesType = IndexType::Index16; - mb.Layout - .Add(VertexAttr::Position, VertexFormat::Float3) - .Add(VertexAttr::TexCoord0, VertexFormat::Float2); - mb.PrimitiveGroups.Add(0, 6); - mb.Begin() - // positions - .Vertex(0, VertexAttr::Position, 0.0f, 0.0f, 0.0f) // top-left - .Vertex(1, VertexAttr::Position, 1.0f, 0.0f, 0.0f) // top-right - .Vertex(2, VertexAttr::Position, 1.0f, 1.0f, 0.0f) // bottom-right - .Vertex(3, VertexAttr::Position, 0.0f, 1.0f, 0.0f) // bottom-left - - // uvs - .Vertex(0, VertexAttr::TexCoord0, 0.0f, 0.0f) - .Vertex(1, VertexAttr::TexCoord0, 1.0f, 0.0f) - .Vertex(2, VertexAttr::TexCoord0, 1.0f, 1.0f) - .Vertex(3, VertexAttr::TexCoord0, 0.0f, 1.0f) - - // indices - .Triangle(0, 0, 1, 2) - .Triangle(1, 0, 2, 3); - auto buildResult = mb.Build(); - - // get the resulting stream object - const MeshSetup& meshSetup = buildResult.Setup; - CHECK(meshSetup.ShouldSetupFromData()); - - // check MeshSetup - CHECK(meshSetup.NumVertices == 4); - CHECK(meshSetup.NumIndices == 6); - CHECK(meshSetup.IndicesType == IndexType::Index16); - CHECK(meshSetup.VertexUsage == Usage::Immutable); - CHECK(meshSetup.IndexUsage == Usage::Immutable); - CHECK(meshSetup.Layout.NumComponents() == 2); - CHECK(meshSetup.Layout.ComponentAt(0).Attr == VertexAttr::Position); - CHECK(meshSetup.Layout.ComponentAt(0).Format == VertexFormat::Float3); - CHECK(meshSetup.Layout.ComponentAt(1).Attr == VertexAttr::TexCoord0); - CHECK(meshSetup.Layout.ComponentAt(1).Format == VertexFormat::Float2); - CHECK(meshSetup.NumPrimitiveGroups() == 1); - CHECK(meshSetup.PrimitiveGroup(0).BaseElement == 0); - CHECK(meshSetup.PrimitiveGroup(0).NumElements == 6); - - // see MeshBuilder header for those sizes - const uint32_t vbufSize = 4 * 5 * sizeof(float); - const uint32_t ibufSize = 6 * sizeof(uint16_t); - const uint32_t allDataSize = vbufSize + ibufSize; - CHECK(buildResult.Data.Size() == allDataSize); - - // check the generated data - const uint8_t* ptr = buildResult.Data.Data(); - o_assert(nullptr != ptr); - CHECK(ptr != nullptr); - - // check vertices - const float* vPtr = (const float*) ptr; - CHECK(vPtr[0] == 0.0f); CHECK(vPtr[1] == 0.0f); CHECK(vPtr[2] == 0.0f); - CHECK(vPtr[3] == 0.0f); CHECK(vPtr[4] == 0.0f); - CHECK(vPtr[5] == 1.0f); CHECK(vPtr[6] == 0.0f); CHECK(vPtr[7] == 0.0f); - CHECK(vPtr[8] == 1.0f); CHECK(vPtr[9] == 0.0f); - CHECK(vPtr[10] == 1.0f); CHECK(vPtr[11] == 1.0f); CHECK(vPtr[12] == 0.0f); - CHECK(vPtr[13] == 1.0f); CHECK(vPtr[14] == 1.0f); - CHECK(vPtr[15] == 0.0f); CHECK(vPtr[16] == 1.0f); CHECK(vPtr[17] == 0.0f); - CHECK(vPtr[18] == 0.0f); CHECK(vPtr[19] == 1.0f); - - // check indices - const uint16_t* iPtr = (const uint16_t*) &(vPtr[20]); - CHECK(iPtr[0] == 0); CHECK(iPtr[1] == 1); CHECK(iPtr[2] == 2); - CHECK(iPtr[3] == 0); CHECK(iPtr[4] == 2); CHECK(iPtr[5] == 3); -} diff --git a/code/Modules/Assets/UnitTests/ShapeBuilderTest.cc b/code/Modules/Assets/UnitTests/ShapeBuilderTest.cc deleted file mode 100644 index 1dc107335..000000000 --- a/code/Modules/Assets/UnitTests/ShapeBuilderTest.cc +++ /dev/null @@ -1,84 +0,0 @@ -//------------------------------------------------------------------------------ -// ShapeBuilderTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Assets/Gfx/ShapeBuilder.h" -#include "Gfx/Gfx.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/renderer.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/private/gfxFactory.h" -#include - -#if ORYOL_OPENGL -#include "Gfx/private/gl/gl_impl.h" -#endif - -using namespace Oryol; -using namespace _priv; - -//------------------------------------------------------------------------------ -TEST(ShapeBuilderTest) { - #if !ORYOL_UNITTESTS_HEADLESS - - // setup a GL context - auto gfxSetup = GfxSetup::Window(400, 300, "Oryol Test"); - - displayMgr displayManager; - meshPool meshPool; - texturePool texPool; - class renderer renderer; - - gfxPointers ptrs; - ptrs.displayMgr = &displayManager; - ptrs.renderer = &renderer; - ptrs.meshPool = &meshPool; - ptrs.texturePool = &texPool; - displayManager.SetupDisplay(gfxSetup, ptrs); - - // setup a meshFactory object - renderer.setup(gfxSetup, ptrs); - gfxFactory factory; - factory.setup(ptrs); - - // the state builder - ShapeBuilder shapeBuilder; - - // build a simple cube - shapeBuilder.Layout.Add(VertexAttr::Position, VertexFormat::Float3); - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1); - auto buildResult = shapeBuilder.Build(); - - const void* data = buildResult.Data.Data(); - const int size = buildResult.Data.Size(); - - // ...create a mesh from it and verify the mesh - mesh simpleCube; - simpleCube.Setup = buildResult.Setup; - factory.initMesh(simpleCube, data, size); - CHECK(simpleCube.vertexBufferAttrs.NumVertices == 24); - CHECK(simpleCube.vertexBufferAttrs.Layout.NumComponents() == 1); - CHECK(simpleCube.vertexBufferAttrs.Layout.ByteSize() == 12); - CHECK(simpleCube.vertexBufferAttrs.Layout.ComponentAt(0).IsValid()); - CHECK(simpleCube.vertexBufferAttrs.Layout.ComponentAt(0).Attr == VertexAttr::Position); - CHECK(simpleCube.vertexBufferAttrs.Layout.ComponentAt(0).Format == VertexFormat::Float3); - CHECK(simpleCube.vertexBufferAttrs.Layout.ComponentAt(0).ByteSize() == 12); - CHECK(simpleCube.indexBufferAttrs.NumIndices == 36); - CHECK(simpleCube.indexBufferAttrs.Type == IndexType::Index16); - CHECK(simpleCube.indexBufferAttrs.BufferUsage == Usage::Immutable); - CHECK(simpleCube.numPrimGroups == 1); - CHECK(simpleCube.primGroups[0].BaseElement == 0); - CHECK(simpleCube.primGroups[0].NumElements == 36); - #if ORYOL_OPENGL - CHECK(simpleCube.buffers[mesh::vb].glBuffers[0] != 0); - CHECK(simpleCube.buffers[mesh::ib].glBuffers[0] != 0); - #endif - - factory.destroyMesh(simpleCube); - factory.discard(); - renderer.discard(); - displayManager.DiscardDisplay(); - - #endif -} diff --git a/code/Modules/Assets/UnitTests/VertexWriterTest.cc b/code/Modules/Assets/UnitTests/VertexWriterTest.cc deleted file mode 100644 index d47168208..000000000 --- a/code/Modules/Assets/UnitTests/VertexWriterTest.cc +++ /dev/null @@ -1,131 +0,0 @@ -//------------------------------------------------------------------------------ -// VertexWriterTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Assets/Gfx/VertexWriter.h" -#include "Core/Memory/Memory.h" - -using namespace Oryol; - -//------------------------------------------------------------------------------ -TEST(VertexWriterTest) { - - uint8_t scratchSpace[1024]; - - // test non-packed functions - Memory::Clear(scratchSpace, sizeof(scratchSpace)); - uint8_t* ptr = scratchSpace; - ptr = VertexWriter::Write(ptr, VertexFormat::Float, 1.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Float2, 2.0f, 3.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Float3, 4.0f, 5.0f, 6.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Float4, 7.0f, 8.0f, 9.0f, 10.0f); - CHECK(ptr == (scratchSpace + 40)); - const float* fp = (const float*) scratchSpace; - CHECK(fp[0] == 1.0f); - CHECK(fp[1] == 2.0f); - CHECK(fp[2] == 3.0f); - CHECK(fp[3] == 4.0f); - CHECK(fp[4] == 5.0f); - CHECK(fp[5] == 6.0f); - CHECK(fp[6] == 7.0f); - CHECK(fp[7] == 8.0f); - CHECK(fp[8] == 9.0f); - CHECK(fp[9] == 10.0f); - - // test packed-stuff - Memory::Clear(scratchSpace, sizeof(scratchSpace)); - ptr = scratchSpace; - - // test UByte4 packing - const uint8_t* u8p = ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::UByte4, 0.0f, 128.5f, -1.0f, 255.5f); - CHECK(u8p[0] == 0); - CHECK(u8p[1] == 128); - CHECK(u8p[2] == 0); - CHECK(u8p[3] == 255); - u8p = ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::UByte4, 64.0f, -64.0f, 2000.0f, -2000.0f); - CHECK(u8p[0] == 64); - CHECK(u8p[1] == 0); - CHECK(u8p[2] == 255); - CHECK(u8p[3] == 0); - - // test UByte4N packing - u8p = ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::UByte4N, -1.0f, +1.0f, 0.5f, -32123.0f); - CHECK(u8p[0] == 0); - CHECK(u8p[1] == 255); - CHECK(u8p[2] == 128); - CHECK(u8p[3] == 0); - - // test Byte4N packing - const int8_t* i8p = (int8_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Byte4, -64.0f, 0.0f, +64.0f, 127.5f); - CHECK(i8p[0] == -64); - CHECK(i8p[1] == 0); - CHECK(i8p[2] == 64); - CHECK(i8p[3] == 127); - i8p = (int8_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Byte4, -200.0f, +200.0f, -1.0f, +1.0f); - CHECK(i8p[0] == -128); - CHECK(i8p[1] == 127); - CHECK(i8p[2] == -1); - CHECK(i8p[3] == 1); - - // test Byte4N packing - i8p = (int8_t*) ptr; - VertexWriter::Write(ptr, VertexFormat::Byte4N, -1.0f, +1.0f, -0.5f, +0.5f); - CHECK(i8p[0] == -127); - CHECK(i8p[1] == 127); - CHECK(i8p[2] == -64); - CHECK(i8p[3] == 64); - - // test Short4 packing - Memory::Clear(scratchSpace, sizeof(scratchSpace)); - ptr = scratchSpace; - const int16_t* i16p = (const int16_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Short4, -40000.0f, +40000.0f, -2000.0f, +2000.0f); - CHECK(i16p[0] == -32768); - CHECK(i16p[1] == 32767); - CHECK(i16p[2] == -2000); - CHECK(i16p[3] == 2000); - - // test Short4N packing - i16p = (const int16_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Short4N, -2.0f, +2.0f, -1.0f, +1.0f); - CHECK(i16p[0] == -32767); - CHECK(i16p[1] == 32767); - CHECK(i16p[2] == -32767); - CHECK(i16p[3] == 32767); - i16p = (const int16_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Short4N, -0.5f, +0.5f, 0.0f, 0.0f); - CHECK(i16p[0] == -16384); - CHECK(i16p[1] == 16384); - CHECK(i16p[2] == 0); - CHECK(i16p[3] == 0); - - // test Short2 packing - i16p = (const int16_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Short2, -40000.0f, +40000.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Short2, -2000.0f, +2000.0f); - CHECK(i16p[0] == -32768); - CHECK(i16p[1] == 32767); - CHECK(i16p[2] == -2000); - CHECK(i16p[3] == 2000); - - // test Short2N packing - i16p = (const int16_t*) ptr; - ptr = VertexWriter::Write(ptr, VertexFormat::Short2N, -2.0f, +2.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Short2N, -1.0f, +1.0f); - ptr = VertexWriter::Write(ptr, VertexFormat::Short2N, -0.5f, +0.5f); - VertexWriter::Write(ptr, VertexFormat::Short2N, 0.0f, 0.0f); - CHECK(i16p[0] == -32767); - CHECK(i16p[1] == 32767); - CHECK(i16p[2] == -32767); - CHECK(i16p[3] == 32767); - CHECK(i16p[4] == -16384); - CHECK(i16p[5] == 16384); - CHECK(i16p[6] == 0); - CHECK(i16p[7] == 0); -} \ No newline at end of file diff --git a/code/Modules/Core/Assertion.h b/code/Modules/Core/Assertion.h index 4af8d5433..f97eb3942 100644 --- a/code/Modules/Core/Assertion.h +++ b/code/Modules/Core/Assertion.h @@ -9,7 +9,7 @@ #define ORYOL_TRAP() abort() -#if !(__GNUC__ || __GNUC__) +#if !__GNUC__ // on Visual Studio, replace __PRETTY_FUNCTION__ with __FUNCSIG__ #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif diff --git a/code/Modules/Core/CMakeLists.txt b/code/Modules/Core/CMakeLists.txt index 35e3632da..7fbb0e32c 100644 --- a/code/Modules/Core/CMakeLists.txt +++ b/code/Modules/Core/CMakeLists.txt @@ -30,7 +30,7 @@ fips_begin_module(Core) Array.h ArrayMap.h Slice.h - Buffer.h + MemoryBuffer.h HashSet.h KeyValuePair.h Map.h @@ -110,39 +110,39 @@ if (FIPS_USE_VLD) add_dependencies(Core vld_copy_dlls) endif() -fips_begin_unittest(Core) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - PtrTest.cc - SliceTest.cc - InlineArrayTest.cc - StackTraceTest.cc - BufferTest.cc - ArgsTest.cc - ArrayTest.cc - StaticArrayTest.cc - ArrayMapTest.cc - CreationTest.cc - CreatorTest.cc - HashSetTest.cc - MapTest.cc - MemoryTest.cc - QueueTest.cc - RttiTest.cc - RunLoopTest.cc - SetTest.cc - StringAtomTest.cc - StringBuilderTest.cc - StringConverterTest.cc - StringTest.cc - WideStringTest.cc - elementBufferTest.cc - ClockTest.cc - DurationTest.cc - TimePointTest.cc - LogTest.cc - ) - fips_deps(Core) -fips_end_unittest() +#fips_begin_unittest(Core) +# fips_vs_warning_level(3) +# fips_dir(UnitTests) +# fips_files( +# PtrTest.cc +# SliceTest.cc +# InlineArrayTest.cc +# StackTraceTest.cc +# MemoryBufferTest.cc +# ArgsTest.cc +# ArrayTest.cc +# StaticArrayTest.cc +# ArrayMapTest.cc +# CreationTest.cc +# CreatorTest.cc +# HashSetTest.cc +# MapTest.cc +# MemoryTest.cc +# QueueTest.cc +# RttiTest.cc +# RunLoopTest.cc +# SetTest.cc +# StringAtomTest.cc +# StringBuilderTest.cc +# StringConverterTest.cc +# StringTest.cc +# WideStringTest.cc +# elementBufferTest.cc +# ClockTest.cc +# DurationTest.cc +# TimePointTest.cc +# LogTest.cc +# ) +# fips_deps(Core) +#fips_end_unittest() diff --git a/code/Modules/Core/Containers/Buffer.h b/code/Modules/Core/Containers/MemoryBuffer.h similarity index 81% rename from code/Modules/Core/Containers/Buffer.h rename to code/Modules/Core/Containers/MemoryBuffer.h index a4df21b19..0531c8708 100644 --- a/code/Modules/Core/Containers/Buffer.h +++ b/code/Modules/Core/Containers/MemoryBuffer.h @@ -1,7 +1,7 @@ #pragma once //------------------------------------------------------------------------------ /** - @class Oryol::Buffer + @class Oryol::MemoryBuffer @ingroup Core @brief growable memory buffer for raw data */ @@ -11,22 +11,22 @@ namespace Oryol { -class Buffer { +class MemoryBuffer { public: /// default constructor - Buffer(); + MemoryBuffer() { }; /// move constructor - Buffer(Buffer&& rhs); + MemoryBuffer(MemoryBuffer&& rhs); /// destructor - ~Buffer(); + ~MemoryBuffer(); /// always force move-construct - Buffer(const Buffer& rhs) = delete; + MemoryBuffer(const MemoryBuffer& rhs) = delete; /// always force move-assign - void operator=(const Buffer& rhs) = delete; + void operator=(const MemoryBuffer& rhs) = delete; /// move-assignment - void operator=(Buffer&& rhs); + void operator=(MemoryBuffer&& rhs); /// get number of bytes in buffer int Size() const; @@ -37,6 +37,8 @@ class Buffer { /// get number of free bytes at back int Spare() const; + /// take ownership of a memory chunk allocated with Memory::Alloc + void MoveRaw(const void* ptr, int numBytes); /// make room for N more bytes void Reserve(int numBytes); /// add bytes to buffer @@ -60,23 +62,14 @@ class Buffer { /// append-copy content into currently allocated buffer, bump size void copy(const uint8_t* ptr, int numBytes); - int size; - int capacity; - uint8_t* data; + int size = 0; + int capacity = 0; + uint8_t* data = nullptr; }; //------------------------------------------------------------------------------ inline -Buffer::Buffer() : -size(0), -capacity(0), -data(nullptr) { - // empty -} - -//------------------------------------------------------------------------------ -inline -Buffer::Buffer(Buffer&& rhs) : +MemoryBuffer::MemoryBuffer(MemoryBuffer&& rhs) : size(rhs.size), capacity(rhs.capacity), data(rhs.data) { @@ -87,13 +80,23 @@ data(rhs.data) { //------------------------------------------------------------------------------ inline -Buffer::~Buffer() { +MemoryBuffer::~MemoryBuffer() { + this->destroy(); +} + +//------------------------------------------------------------------------------ +inline void +MemoryBuffer::MoveRaw(const void* ptr, int numBytes) { + o_assert(ptr && (numBytes > 0)); this->destroy(); + this->data = (uint8_t*) ptr; + this->capacity = numBytes; + this->size = numBytes; } //------------------------------------------------------------------------------ inline void -Buffer::alloc(int newCapacity) { +MemoryBuffer::alloc(int newCapacity) { o_assert_dbg(newCapacity > this->capacity); o_assert_dbg(newCapacity > this->size); @@ -111,7 +114,7 @@ Buffer::alloc(int newCapacity) { //------------------------------------------------------------------------------ inline void -Buffer::destroy() { +MemoryBuffer::destroy() { if (this->data) { Memory::Free(this->data); } @@ -122,7 +125,7 @@ Buffer::destroy() { //------------------------------------------------------------------------------ inline void -Buffer::copy(const uint8_t* ptr, int numBytes) { +MemoryBuffer::copy(const uint8_t* ptr, int numBytes) { // NOTE: it is valid to call copy with numBytes==0 o_assert_dbg(this->data); o_assert_dbg((this->size + numBytes) <= this->capacity); @@ -132,7 +135,7 @@ Buffer::copy(const uint8_t* ptr, int numBytes) { //------------------------------------------------------------------------------ inline void -Buffer::operator=(Buffer&& rhs) { +MemoryBuffer::operator=(MemoryBuffer&& rhs) { this->destroy(); this->size = rhs.size; this->capacity = rhs.capacity; @@ -144,31 +147,31 @@ Buffer::operator=(Buffer&& rhs) { //------------------------------------------------------------------------------ inline int -Buffer::Size() const { +MemoryBuffer::Size() const { return this->size; } //------------------------------------------------------------------------------ inline bool -Buffer::Empty() const { +MemoryBuffer::Empty() const { return 0 == this->size; } //------------------------------------------------------------------------------ inline int -Buffer::Capacity() const { +MemoryBuffer::Capacity() const { return this->capacity; } //------------------------------------------------------------------------------ inline int -Buffer::Spare() const { +MemoryBuffer::Spare() const { return this->capacity - this->size; } //------------------------------------------------------------------------------ inline void -Buffer::Reserve(int numBytes) { +MemoryBuffer::Reserve(int numBytes) { // need to grow? if ((this->size + numBytes) > this->capacity) { const int newCapacity = this->size + numBytes; @@ -178,14 +181,14 @@ Buffer::Reserve(int numBytes) { //------------------------------------------------------------------------------ inline void -Buffer::Add(const uint8_t* data, int numBytes) { +MemoryBuffer::Add(const uint8_t* data, int numBytes) { this->Reserve(numBytes); this->copy(data, numBytes); } //------------------------------------------------------------------------------ inline uint8_t* -Buffer::Add(int numBytes) { +MemoryBuffer::Add(int numBytes) { this->Reserve(numBytes); uint8_t* ptr = this->data + this->size; this->size += numBytes; @@ -194,13 +197,13 @@ Buffer::Add(int numBytes) { //------------------------------------------------------------------------------ inline void -Buffer::Clear() { +MemoryBuffer::Clear() { this->size = 0; } //------------------------------------------------------------------------------ inline int -Buffer::Remove(int offset, int numBytes) { +MemoryBuffer::Remove(int offset, int numBytes) { o_assert_dbg(offset >= 0); o_assert_dbg(numBytes >= 0); if (offset >= this->size) { @@ -224,14 +227,14 @@ Buffer::Remove(int offset, int numBytes) { //------------------------------------------------------------------------------ inline const uint8_t* -Buffer::Data() const { +MemoryBuffer::Data() const { o_assert(this->data); return this->data; } //------------------------------------------------------------------------------ inline uint8_t* -Buffer::Data() { +MemoryBuffer::Data() { o_assert(this->data); return this->data; } diff --git a/code/Modules/Core/String/ConvertUTF.c b/code/Modules/Core/String/ConvertUTF.c index 9b3deebd6..3a9a8cd34 100644 --- a/code/Modules/Core/String/ConvertUTF.c +++ b/code/Modules/Core/String/ConvertUTF.c @@ -20,6 +20,10 @@ * remains attached. */ +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + /* --------------------------------------------------------------------- Conversions between UTF32, UTF-16, and UTF-8. Source code file. diff --git a/code/Modules/Core/UnitTests/MapTest.cc b/code/Modules/Core/UnitTests/MapTest.cc index b0e5e19aa..6891cdefe 100644 --- a/code/Modules/Core/UnitTests/MapTest.cc +++ b/code/Modules/Core/UnitTests/MapTest.cc @@ -6,6 +6,7 @@ #include "UnitTest++/src/UnitTest++.h" #include "Core/Containers/Map.h" #include "Core/String/String.h" +#include "Core/String/StringAtom.h" using namespace Oryol; @@ -155,4 +156,161 @@ TEST(MapTest) { testMap.Add(counter, "testString"); testMap.Erase(counter); } + + // test removing the first element + Map map5; + map5.Add(0, 0); + map5.Add(1, 1); + map5.Add(2, 2); + map5.Add(3, 3); + map5.Add(4, 4); + map5.Add(5, 5); + CHECK(map5.KeyAtIndex(0) == 0); + CHECK(map5.KeyAtIndex(1) == 1); + int key = map5.KeyAtIndex(0); + map5.Erase(key); + CHECK(map5.KeyAtIndex(0) == 1); +} + +namespace MapTest { + +class Locator { +public: + static const uint32_t NonSharedSignature = 0xFFFFFFFF; + static const uint32_t DefaultSignature = 0xFFFFFFFE; + + Locator(); + Locator(const StringAtom& location); + Locator(const char* location); + Locator(const StringAtom& location, uint32_t signature); + Locator(const char* location, uint32_t signature); + + static Locator NonShared(); + static Locator NonShared(const StringAtom& location); + + Locator(const Locator& rhs); + void operator=(const Locator& rhs); + + bool operator==(const Locator& rhs) const; + bool operator!=(const Locator& rhs) const; + bool operator<(const Locator& rhs) const; + + bool IsShared() const; + bool HasValidLocation() const; + const StringAtom& Location() const; + uint32_t Signature() const; + +private: + StringAtom location; + uint32_t signature; +}; + +inline Locator::Locator(const StringAtom& loc) : +location(loc), +signature(DefaultSignature) { + // empty +} + +inline Locator::Locator(const char* loc) : +location(loc), +signature(DefaultSignature) { + // empty +} + +inline Locator::Locator(const StringAtom& loc, uint32_t sig) : +location(loc), +signature(sig) { + // empty +} + +inline Locator::Locator(const char* loc, uint32_t sig) : +location(loc), +signature(sig) { + // empty +} + +inline Locator Locator::NonShared() { + return Locator(StringAtom(), NonSharedSignature); +} + +inline Locator Locator::NonShared(const StringAtom& loc) { + return Locator(loc, NonSharedSignature); +} + +inline Locator::Locator() : +signature(DefaultSignature) { + // empty } + +inline Locator::Locator(const Locator& rhs) : +location(rhs.location), +signature(rhs.signature) { + // empty +} + +inline void Locator::operator=(const Locator& rhs) { + this->location = rhs.location; + this->signature = rhs.signature; +} + +inline bool Locator::operator==(const Locator& rhs) const { + return (this->location == rhs.location) && (this->signature == rhs.signature); +} + +inline bool Locator::operator!=(const Locator& rhs) const { + return (this->location != rhs.location) || (this->signature != rhs.signature); +} + +inline bool Locator::operator<(const Locator& rhs) const { + if (this->location == rhs.location) { + return this->signature < rhs.signature; + } + else { + return this->location < rhs.location; + } +} + +inline bool Locator::IsShared() const { + return NonSharedSignature != this->signature; +} + +inline bool Locator::HasValidLocation() const { + return this->location.IsValid(); +} + +inline const StringAtom& Locator::Location() const { + return this->location; +} + +inline uint32_t Locator::Signature() const { + return this->signature; +} + +TEST(MapTest2) { + Locator l0("IMUIShader"); + Locator l1("LambertShader"); + Locator l2("model", 1); + Locator l3("model", 2); + Map map; + map.Add(l0, 2); + map.Add(l1, 4); + map.Add(l2, 6); + map.Add(l3, 7); + CHECK(map.KeyAtIndex(0) == l0); + CHECK(map.KeyAtIndex(1) == l1); + CHECK(map.KeyAtIndex(2) == l2); + CHECK(map.KeyAtIndex(3) == l3); + CHECK(map.ValueAtIndex(0) == 2); + CHECK(map.ValueAtIndex(1) == 4); + CHECK(map.ValueAtIndex(2) == 6); + CHECK(map.ValueAtIndex(3) == 7); + map.Erase(l0); + CHECK(map.KeyAtIndex(0) == l1); + CHECK(map.KeyAtIndex(1) == l2); + CHECK(map.KeyAtIndex(2) == l3); + CHECK(map.ValueAtIndex(0) == 4); + CHECK(map.ValueAtIndex(1) == 6); + CHECK(map.ValueAtIndex(2) == 7); +} + +} // namespace MapTest diff --git a/code/Modules/Core/UnitTests/BufferTest.cc b/code/Modules/Core/UnitTests/MemoryBufferTest.cc similarity index 93% rename from code/Modules/Core/UnitTests/BufferTest.cc rename to code/Modules/Core/UnitTests/MemoryBufferTest.cc index 46f6c585f..b020c002c 100644 --- a/code/Modules/Core/UnitTests/BufferTest.cc +++ b/code/Modules/Core/UnitTests/MemoryBufferTest.cc @@ -1,16 +1,16 @@ //------------------------------------------------------------------------------ -// BufferTest.cc +// MemoryBufferTest.cc //------------------------------------------------------------------------------ #include "Pre.h" #include "UnitTest++/src/UnitTest++.h" -#include "Core/Containers/Buffer.h" +#include "Core/Containers/MemoryBuffer.h" #include using namespace Oryol; -TEST(BufferTest) { +TEST(MemoryBufferTest) { - Buffer buf0; + MemoryBuffer buf0; CHECK(buf0.Size() == 0); CHECK(buf0.Empty()); CHECK(buf0.Capacity() == 0); @@ -42,7 +42,7 @@ TEST(BufferTest) { CHECK(i+1 == buf0.Data()[i+sizeof(bla)]); } - Buffer buf1(std::move(buf0)); + MemoryBuffer buf1(std::move(buf0)); CHECK(buf1.Size() == 14); CHECK(!buf1.Empty()); CHECK(buf1.Capacity() == 14); @@ -54,7 +54,7 @@ TEST(BufferTest) { CHECK(buf0.Empty()); CHECK(buf0.Capacity() == 0); - Buffer buf2; + MemoryBuffer buf2; buf2 = std::move(buf1); CHECK(buf2.Size() == 14); CHECK(!buf2.Empty()); @@ -86,7 +86,7 @@ TEST(BufferTest) { CHECK(buf2.Capacity() == 21); CHECK(buf2.Spare() == 21); - Buffer buf3; + MemoryBuffer buf3; const char* str = "Hello wonderful world!"; buf3.Add((const uint8_t*)str, int(std::strlen(str))+1); CHECK(0 == buf3.Remove(0, 0)); @@ -106,7 +106,7 @@ TEST(BufferTest) { CHECK(buf3.Size() == 5); CHECK(std::strcmp((const char*)buf3.Data(), "worl") == 0); - Buffer buf4; + MemoryBuffer buf4; buf4.Add((const uint8_t*)str, int(std::strlen(str))+1); CHECK(6 == buf4.Remove(0, 6)); CHECK(std::strcmp((const char*)buf4.Data(), "wonderful world!") == 0); diff --git a/code/Modules/Core/private/ios/iosBridge.h b/code/Modules/Core/private/ios/iosBridge.h index 780190052..913b209c3 100644 --- a/code/Modules/Core/private/ios/iosBridge.h +++ b/code/Modules/Core/private/ios/iosBridge.h @@ -83,8 +83,6 @@ class iosBridge { void onDidBecomeActive(); /// called by app delegate when app is about to be killed void onWillTerminate(); - /// called per frame by display link, this will in turn call GLKView - void onDrawRequested(); /// called per frame by GLKView void onFrame(); diff --git a/code/Modules/Core/private/ios/iosBridge.mm b/code/Modules/Core/private/ios/iosBridge.mm index 0cba62e35..df58ada25 100644 --- a/code/Modules/Core/private/ios/iosBridge.mm +++ b/code/Modules/Core/private/ios/iosBridge.mm @@ -101,7 +101,7 @@ - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { @end #if ORYOL_METAL -@interface oryolViewDelegate : NSObject +@interface oryolViewDelegate : NSObject @end @implementation oryolViewDelegate diff --git a/code/Modules/Dbg/Dbg.cc b/code/Modules/Dbg/Dbg.cc index 29d38707b..daf55487d 100644 --- a/code/Modules/Dbg/Dbg.cc +++ b/code/Modules/Dbg/Dbg.cc @@ -18,10 +18,10 @@ namespace { //------------------------------------------------------------------------------ void -Dbg::Setup(const DbgSetup& setup) { +Dbg::Setup(const DbgDesc& desc) { o_assert(!IsValid()); state = Memory::New<_state>(); - state->debugTextRenderer.setup(setup); + state->debugTextRenderer.setup(desc); } //------------------------------------------------------------------------------ @@ -88,10 +88,10 @@ Dbg::TextColor(const float (&c)[4]) { } //------------------------------------------------------------------------------ void -Dbg::DrawTextBuffer() { +Dbg::DrawTextBuffer(int width, int height) { o_trace_scoped(Dbg_DrawTextBuffer); o_assert_dbg(IsValid()); - state->debugTextRenderer.drawTextBuffer(); + state->debugTextRenderer.drawTextBuffer(width, height); } } // namespace Oryol diff --git a/code/Modules/Dbg/Dbg.h b/code/Modules/Dbg/Dbg.h index 1d30adaf8..245e7afaf 100644 --- a/code/Modules/Dbg/Dbg.h +++ b/code/Modules/Dbg/Dbg.h @@ -16,7 +16,7 @@ namespace Oryol { class Dbg { public: /// setup the Debug module - static void Setup(const DbgSetup& setup=DbgSetup()); + static void Setup(const DbgDesc& desc=DbgDesc()); /// discard the Debug module static void Discard(); /// return true if Debug module is valid @@ -34,8 +34,8 @@ class Dbg { static void TextColor(float r, float g, float b, float a); /// add a color tag as float[4] array static void TextColor(const float (&c)[4]); - /// draw the debug text buffer (call one per frame) - static void DrawTextBuffer(); + /// draw the debug text buffer (call once per frame) + static void DrawTextBuffer(int width=0, int height=0); }; } // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Dbg/DbgTypes.h b/code/Modules/Dbg/DbgTypes.h index c1e34a405..cf6daa37c 100644 --- a/code/Modules/Dbg/DbgTypes.h +++ b/code/Modules/Dbg/DbgTypes.h @@ -5,19 +5,35 @@ @brief setup parameters for the Dbg module */ #include "Core/Types.h" +#include "Gfx/GfxTypes.h" namespace Oryol { -class DbgSetup { +class DbgDesc { public: - /// number of text columns on whole screen + /// number of text columns on screen int NumTextColumns = 120; - /// number of text lines on whole screen + /// number of text rows on screen int NumTextRows = 80; - /// initial text scale, x dimension + /// initial text scale, in x dimension float TextScaleX = 1.0f; - /// initial text scale, y dimension + /// initial text scale, in y dimension float TextScaleY = 1.0f; + /// color pixel format of render pass to render text to + PixelFormat::Code ColorFormat = PixelFormat::RGBA8; + /// depth pixel format of render pass to render text to + PixelFormat::Code DepthFormat = PixelFormat::DEPTHSTENCIL; + /// sample count of render pass to render to + int SampleCount = 1; + + /// setters with chaining + DbgDesc& SetNumTextColumns(int n) { NumTextColumns = n; return *this; } + DbgDesc& SetNumTextRows(int n) { NumTextRows = n; return *this; } + DbgDesc& SetTextScaleX(float s) { TextScaleX = s; return *this; } + DbgDesc& SetTextScaleY(float s) { TextScaleY = s; return *this; } + DbgDesc& SetColorFormat(PixelFormat::Code fmt) { ColorFormat = fmt; return *this; } + DbgDesc& SetDepthFormat(PixelFormat::Code fmt) { DepthFormat = fmt; return *this; } + DbgDesc& SetSampleCount(int c) { SampleCount = c; return *this; } }; } // namespace Oryol diff --git a/code/Modules/Dbg/private/debugTextRenderer.cc b/code/Modules/Dbg/private/debugTextRenderer.cc index 26ded68a7..e28dabed4 100644 --- a/code/Modules/Dbg/private/debugTextRenderer.cc +++ b/code/Modules/Dbg/private/debugTextRenderer.cc @@ -28,22 +28,21 @@ debugTextRenderer::~debugTextRenderer() { //------------------------------------------------------------------------------ void -debugTextRenderer::setup(const DbgSetup& s) { +debugTextRenderer::setup(const DbgDesc& desc) { o_assert_dbg(!this->valid); o_assert_dbg(nullptr == this->vertexData); - this->numColumns = s.NumTextColumns; - this->numRows = s.NumTextRows; - this->maxNumChars = s.NumTextColumns * s.NumTextRows; - this->textScaleX = s.TextScaleX; - this->textScaleY = s.TextScaleY; + this->numColumns = desc.NumTextColumns; + this->numRows = desc.NumTextRows; + this->maxNumChars = desc.NumTextColumns * desc.NumTextRows; + this->textScaleX = desc.TextScaleX; + this->textScaleY = desc.TextScaleY; this->maxNumVertices = this->maxNumChars * 6; this->stringBuilder.Reserve(this->maxNumChars * 2); this->curNumVertices = 0; this->vertexData = (Vertex*) Memory::Alloc(this->maxNumVertices * sizeof(Vertex)); Gfx::PushResourceLabel(); - this->setupMesh(); - this->setupFontTexture(); + this->setupResources(desc); this->resourceLabel = Gfx::PopResourceLabel(); this->valid = true; } @@ -83,8 +82,8 @@ debugTextRenderer::printf(const char* text, std::va_list args) { void debugTextRenderer::cursorPos(uint8_t x, uint8_t y) { SCOPED_LOCK; - this->stringBuilder.Append((char) 0x1B); // start ESC control sequence - this->stringBuilder.Append((char) 0x01); // set cursor + this->stringBuilder.Append(0x1B); // start ESC control sequence + this->stringBuilder.Append(0x01); // set cursor this->stringBuilder.Append((char)x); this->stringBuilder.Append((char)y); } @@ -103,15 +102,7 @@ debugTextRenderer::textColor(float r, float g, float b, float a) { //------------------------------------------------------------------------------ void -debugTextRenderer::drawTextBuffer() { - - // lazy-setup the pipeline-state-object (this is done deferred to - // initialize the pipeline with the right render pass params - if (!this->drawState.Pipeline.IsValid()) { - Gfx::PushResourceLabel(this->resourceLabel); - this->setupPipeline(); - Gfx::PopResourceLabel(); - } +debugTextRenderer::drawTextBuffer(int width, int height) { // get the currently accumulated string String str; @@ -130,21 +121,56 @@ debugTextRenderer::drawTextBuffer() { // FIXME: this would be wrong if rendering to a render target which // isn't the same size as the back buffer, there's no method yet // to query the current render target width/height - Gfx::UpdateVertices(this->drawState.Mesh[0], this->vertexData, this->curNumVertices * this->vertexLayout.ByteSize()); - Gfx::ApplyDrawState(this->drawState); + if (0 == width) { + width = Gfx::DisplayAttrs().Width; + } + if (0 == height) { + height = Gfx::DisplayAttrs().Height; + } + Gfx::UpdateBuffer(this->bind.VertexBuffers[0], this->vertexData, this->curNumVertices * this->vertexLayout.ByteSize()); + Gfx::ApplyPipeline(this->pipeline); + Gfx::ApplyBindings(this->bind); DbgTextShader::vsParams vsParams; - const float w = 8.0f / Gfx::PassAttrs().FramebufferWidth; // glyph is 8 pixels wide - const float h = 8.0f / Gfx::PassAttrs().FramebufferHeight; // glyph is 8 pixel tall + const float w = 8.0f / width; // glyph is 8 pixels wide + const float h = 8.0f / height; // glyph is 8 pixel tall vsParams.glyphSize = glm::vec2(w * this->textScaleX * 2.0f, h * this->textScaleY * 2.0f); - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(PrimitiveGroup(0, this->curNumVertices)); + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(0, this->curNumVertices); this->curNumVertices = 0; } } //------------------------------------------------------------------------------ void -debugTextRenderer::setupFontTexture() { +debugTextRenderer::setupResources(const DbgDesc& desc) { + o_assert_dbg(this->vertexLayout.Empty()); + o_assert_dbg((this->maxNumVertices > 0) && (this->maxNumVertices == this->maxNumChars*6)); + + // setup an empty mesh, only vertices + this->vertexLayout = { + { "position", VertexFormat::Float4 }, + { "color0", VertexFormat::UByte4N } + }; + const int vbufSize = this->maxNumVertices * this->vertexLayout.ByteSize(); + Id vbuf = Gfx::CreateBuffer(BufferDesc() + .SetSize(vbufSize) + .SetType(BufferType::VertexBuffer) + .SetUsage(Usage::Stream)); + o_assert_dbg(vbuf.IsValid()); + + // create pipeline object + this->pipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(DbgTextShader::Desc())) + .SetLayout(0, this->vertexLayout) + .SetDepthWriteEnabled(false) + .SetDepthCmpFunc(CompareFunc::Always) + .SetBlendEnabled(true) + .SetBlendSrcFactorRGB(BlendFactor::SrcAlpha) + .SetBlendDstFactorRGB(BlendFactor::OneMinusSrcAlpha) + .SetColorWriteMask(PixelChannel::RGB) + .SetColorFormat(desc.ColorFormat) + .SetDepthFormat(desc.DepthFormat) + .SetSampleCount(desc.SampleCount)); // convert the KC85/4 font into 8bpp image data const int numChars = 128; @@ -155,9 +181,9 @@ debugTextRenderer::setupFontTexture() { const int bytesPerChar = charWidth * charHeight; const int imgDataSize = numChars * bytesPerChar; o_assert_dbg((imgWidth * imgHeight) == imgDataSize); - + // setup a memory buffer and write font image data to it - Buffer data; + MemoryBuffer data; uint8_t* dstPtr = data.Add(imgDataSize); const char* srcPtr = kc85_4_Font; for (int charIndex = 0; charIndex < numChars; charIndex++) { @@ -171,56 +197,24 @@ debugTextRenderer::setupFontTexture() { } } } - - // setup texture, pixel format is 8bpp uncompressed - auto texSetup = TextureSetup::FromPixelData2D(imgWidth, imgHeight, 1, PixelFormat::L8); - texSetup.Sampler.MinFilter = TextureFilterMode::Nearest; - texSetup.Sampler.MagFilter = TextureFilterMode::Nearest; - texSetup.Sampler.WrapU = TextureWrapMode::ClampToEdge; - texSetup.Sampler.WrapV = TextureWrapMode::ClampToEdge; - texSetup.ImageData.Sizes[0][0] = imgDataSize; - Id tex = Gfx::CreateResource(texSetup, data); - o_assert_dbg(tex.IsValid()); - o_assert_dbg(Gfx::QueryResourceInfo(tex).State == ResourceState::Valid); - this->drawState.FSTexture[DbgTextShader::tex] = tex; -} -//------------------------------------------------------------------------------ -void -debugTextRenderer::setupMesh() { - o_assert_dbg(this->vertexLayout.Empty()); - o_assert_dbg((this->maxNumVertices > 0) && (this->maxNumVertices == this->maxNumChars*6)); - - // setup an empty mesh, only vertices - this->vertexLayout = { - { VertexAttr::Position, VertexFormat::Float4 }, - { VertexAttr::Color0, VertexFormat::UByte4N } - }; - MeshSetup setup = MeshSetup::Empty(this->maxNumVertices, Usage::Stream); - setup.Layout = this->vertexLayout; - this->drawState.Mesh[0] = Gfx::CreateResource(setup); - o_assert_dbg(this->drawState.Mesh[0].IsValid()); -} + // setup texture, pixel format is 8bpp uncompressed + Id tex = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::Texture2D) + .SetWidth(imgWidth) + .SetHeight(imgHeight) + .SetFormat(PixelFormat::L8) + .SetMinFilter(TextureFilterMode::Nearest) + .SetMagFilter(TextureFilterMode::Nearest) + .SetWrapU(TextureWrapMode::ClampToEdge) + .SetWrapV(TextureWrapMode::ClampToEdge) + .SetMipContent(0, 0, data.Data()) + .SetMipSize(0, 0, data.Size())); -//------------------------------------------------------------------------------ -void -debugTextRenderer::setupPipeline() { - // finally create pipeline object - Id shd = Gfx::CreateResource(DbgTextShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(this->vertexLayout, shd); - ps.DepthStencilState.DepthWriteEnabled = false; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::Always; - ps.BlendState.BlendEnabled = true; - ps.BlendState.SrcFactorRGB = BlendFactor::SrcAlpha; - ps.BlendState.DstFactorRGB = BlendFactor::OneMinusSrcAlpha; - ps.BlendState.ColorWriteMask = PixelChannel::RGB; - // NOTE: this is a bit naughty, we actually want 'dbg render contexts' - // for different render targets and quickly select them before - // text rendering - ps.BlendState.ColorFormat = Gfx::PassAttrs().ColorPixelFormat; - ps.BlendState.DepthFormat = Gfx::PassAttrs().DepthPixelFormat; - ps.RasterizerState.SampleCount = Gfx::PassAttrs().SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(ps); + // setup the resource bindings + this->bind = Bindings() + .SetVertexBuffer(0, vbuf) + .SetFSTexture(DbgTextShader::tex, tex); } //------------------------------------------------------------------------------ diff --git a/code/Modules/Dbg/private/debugTextRenderer.h b/code/Modules/Dbg/private/debugTextRenderer.h index 96163c0f8..0e939f13f 100644 --- a/code/Modules/Dbg/private/debugTextRenderer.h +++ b/code/Modules/Dbg/private/debugTextRenderer.h @@ -10,7 +10,7 @@ #include "Core/String/StringBuilder.h" #include "Gfx/Gfx.h" #include "Dbg/DbgTypes.h" -#include +#include namespace Oryol { namespace _priv { @@ -20,7 +20,7 @@ class debugTextRenderer { ~debugTextRenderer(); /// setup the text renderer - void setup(const DbgSetup& setup); + void setup(const DbgDesc& desc); /// discard the text renderer void discard(); /// return true if the text renderer has been setup @@ -37,13 +37,9 @@ class debugTextRenderer { /// change text color void textColor(float r, float g, float b, float a); /// draw the accumulated text - void drawTextBuffer(); - /// setup the font texture - void setupFontTexture(); + void drawTextBuffer(int width, int height); /// setup the text dynamic mesh - void setupMesh(); - /// setup the text pipeline state object (happens deferred) - void setupPipeline(); + void setupResources(const DbgDesc& desc); /// convert the provides string object into vertices, and return number of vertices void convertStringToVertices(const String& str); /// write one glyph vertex, returns next vertex index @@ -53,7 +49,8 @@ class debugTextRenderer { bool valid = false; VertexLayout vertexLayout; - DrawState drawState; + Id pipeline; + Bindings bind; StringBuilder stringBuilder; ResourceLabel resourceLabel; int numColumns = 0; diff --git a/code/Modules/Gfx/CMakeLists.txt b/code/Modules/Gfx/CMakeLists.txt index a50f81723..77f893211 100644 --- a/code/Modules/Gfx/CMakeLists.txt +++ b/code/Modules/Gfx/CMakeLists.txt @@ -3,120 +3,51 @@ # #------------------------------------------------------------------------------- -if (ORYOL_OPENGL) -# -# This option forces the use of glGetAttribLocation() instead of -# glBindAttribLocation() to bind vertex components to shader -# vertex attributes. This must be used on platforms where -# GL_MAX_VERTEX_ATTRS is less then 16 (the only platform with this -# restriction so far seems to be the Raspberry Pi). This option -# is mutually exclusive with vertex array objects. -# -option(ORYOL_GL_USE_GETATTRIBLOCATION "Use glGetAttribLocation instead of glBindAttribLocation" OFF) -if (ORYOL_GL_USE_GETATTRIBLOCATION OR FIPS_RASPBERRYPI) - add_definitions(-DORYOL_GL_USE_GETATTRIBLOCATION=1) -else() - add_definitions(-DORYOL_GL_USE_GETATTRIBLOCATION=0) -endif() - -endif() # ORYOL_OPENGL - fips_begin_module(Gfx) fips_vs_warning_level(3) fips_files( Gfx.cc Gfx.h GfxTypes.cc GfxTypes.h - GfxConfig.h - MeshLoaderBase.cc MeshLoaderBase.h - TextureLoaderBase.cc TextureLoaderBase.h ) fips_dir(private) fips_files( displayMgrBase.cc displayMgrBase.h displayMgr.h - renderer.h - gfxPointers.h - resourcePools.h - resourceBase.h resourceBase.cc - resource.h - gfxFactory.h - gfxFactoryBase.h gfxFactoryBase.cc - gfxResourceContainer.h gfxResourceContainer.cc ) - if (ORYOL_OPENGL) - fips_dir(private/gl) - fips_files( - glCaps.cc glCaps.h - glResource.cc glResource.h - glFactory.cc glFactory.h - glRenderer.cc glRenderer.h - glTypes.cc glTypes.h - gl_decl.h - gl_impl.h - ) - endif() - if (ORYOL_D3D11) - fips_dir(private/win) - fips_files( - win_decl.h - winDisplayMgr.cc winDisplayMgr.h - winInputDefs.h - ) + fips_dir(private/sokol) + fips_files(sokolGfxBackend.cc sokolGfxBackend.h sokol_gfx.h) + if (ORYOL_METAL) + fips_files(sokolImpl.mm) + else() + fips_files(sokolImpl.cc) endif() if (ORYOL_D3D11) fips_dir(private/d3d11) - fips_files( - d3d11_impl.h d3d11_decl.h - d3d11DisplayMgr.cc d3d11DisplayMgr.h - d3d11Types.cc d3d11Types.h - d3d11Resource.cc d3d11Resource.h - d3d11Factory.cc d3d11Factory.h - d3d11Renderer.cc d3d11Renderer.h - ) - endif() - if (ORYOL_METAL) - fips_dir(private/mtl) - fips_files( - mtl_decl.h mtl_impl.h - mtlDisplayMgr.h mtlDisplayMgr.mm - mtlTypes.h mtlTypes.mm - mtlResource.h mtlResource.mm - mtlFactory.h mtlFactory.mm - mtlRenderer.h mtlRenderer.mm - ) + fips_files(winDisplayMgr.cc winDisplayMgr.h) + fips_files(d3d11DisplayMgr.cc d3d11DisplayMgr.h) + fips_libs(d3d11) + elseif (ORYOL_METAL) + fips_dir(private/metal) + fips_files(mtlDisplayMgr.h mtlDisplayMgr.mm) fips_frameworks_osx(Metal QuartzCore) - endif() - if (FIPS_ANDROID) - fips_dir(private/egl) - fips_files(eglDisplayMgr.cc eglDisplayMgr.h) - fips_libs(GLESv3 EGL) - endif() - if (FIPS_EMSCRIPTEN) - fips_dir(private/emsc) - fips_files(emscDisplayMgr.cc emscDisplayMgr.h) - endif() - if (FIPS_IOS AND NOT ORYOL_METAL) - fips_dir(private/ios) - fips_files(iosDisplayMgr.mm iosDisplayMgr.h) - endif() - if (ORYOL_OPENGL) - if (FIPS_RASPBERRYPI) - fips_dir(private/egl) + elseif (ORYOL_OPENGL) + fips_dir(private/gl) + if (FIPS_ANDROID) + fips_files(eglDisplayMgr.cc eglDisplayMgr.h) + fips_libs(GLESv3 EGL) + elseif (FIPS_EMSCRIPTEN) + fips_files(emscDisplayMgr.cc emscDisplayMgr.h) + elseif (FIPS_IOS) + fips_files(iosDisplayMgr.mm iosDisplayMgr.h) + fips_frameworks_osx(OpenGLES GLKit) + elseif (FIPS_RASPBERRYPI) fips_files(eglDisplayMgr.cc eglDisplayMgr.h) fips_libs(GLESv2 EGL bcm_host) elseif (FIPS_MACOS OR FIPS_WINDOWS OR FIPS_LINUX) - fips_dir(private/glfw) fips_files(glfwDisplayMgr.cc glfwDisplayMgr.h) - fips_dir(private/flextgl) fips_files(flextGL.c flextGL.h) fips_deps(glfw3) endif() - if (FIPS_IOS) - fips_frameworks_osx(OpenGLES GLKit) - endif() - endif() - if (ORYOL_D3D11) - fips_libs(d3d11) endif() fips_deps(Resource Core) fips_end_module() @@ -124,22 +55,3 @@ if (FIPS_MSVC) # forcing value to bool target_compile_options(Gfx PRIVATE "/wd4800") endif() - -fips_begin_unittest(Gfx) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - DDSLoadTest.cc - MeshFactoryTest.cc - MeshSetupTest.cc - RenderEnumsTest.cc - RenderSetupTest.cc - TextureFactoryTest.cc - TextureSetupTest.cc - VertexLayoutTest.cc - glTypesTest.cc - ) - oryol_shader(TestShaderLibrary.glsl) - fips_deps(HttpFS Gfx Assets) -fips_end_unittest() - diff --git a/code/Modules/Gfx/Gfx.cc b/code/Modules/Gfx/Gfx.cc index 249e2a642..544049ad3 100644 --- a/code/Modules/Gfx/Gfx.cc +++ b/code/Modules/Gfx/Gfx.cc @@ -5,10 +5,7 @@ #include "Gfx.h" #include "Core/Core.h" #include "Core/Trace.h" -#include "Gfx/private/gfxPointers.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/gfxResourceContainer.h" -#include "Gfx/private/renderer.h" +#include "Gfx/private/gfxBackend.h" namespace Oryol { @@ -16,12 +13,10 @@ using namespace _priv; namespace { struct _state { - class GfxSetup gfxSetup; + GfxDesc gfxDesc; GfxFrameInfo gfxFrameInfo; RunLoop::Id runLoopId = RunLoop::InvalidId; - _priv::displayMgr displayManager; - class _priv::renderer renderer; - _priv::gfxResourceContainer resourceContainer; + _priv::gfxBackend backend; bool inPass = false; }; _state* state = nullptr; @@ -29,26 +24,14 @@ namespace { //------------------------------------------------------------------------------ void -Gfx::Setup(const class GfxSetup& setup) { +Gfx::Setup(const GfxDesc& desc) { o_assert_dbg(!IsValid()); state = Memory::New<_state>(); - state->gfxSetup = setup; - - gfxPointers pointers; - pointers.displayMgr = &state->displayManager; - pointers.renderer = &state->renderer; - pointers.resContainer = &state->resourceContainer; - pointers.meshPool = &state->resourceContainer.meshPool; - pointers.shaderPool = &state->resourceContainer.shaderPool; - pointers.texturePool = &state->resourceContainer.texturePool; - pointers.pipelinePool = &state->resourceContainer.pipelinePool; - pointers.renderPassPool = &state->resourceContainer.renderPassPool; - - state->displayManager.SetupDisplay(setup, pointers); - state->renderer.setup(setup, pointers); - state->resourceContainer.setup(setup, pointers); + state->gfxDesc = desc; + + state->backend.Setup(desc); state->runLoopId = Core::PreRunLoop()->Add([] { - state->displayManager.ProcessSystemEvents(); + state->backend.ProcessSystemEvents(); }); state->gfxFrameInfo = GfxFrameInfo(); } @@ -58,12 +41,8 @@ void Gfx::Discard() { o_assert_dbg(IsValid()); o_assert_dbg(!state->inPass); - state->resourceContainer.GarbageCollect(); - state->resourceContainer.Destroy(ResourceLabel::All); Core::PreRunLoop()->Remove(state->runLoopId); - state->renderer.discard(); - state->resourceContainer.discard(); - state->displayManager.DiscardDisplay(); + state->backend.Discard(); Memory::Delete(state); state = nullptr; } @@ -78,59 +57,56 @@ Gfx::IsValid() { bool Gfx::QuitRequested() { o_assert_dbg(IsValid()); - return state->displayManager.QuitRequested(); + return state->backend.QuitRequested(); } //------------------------------------------------------------------------------ -Gfx::EventHandlerId -Gfx::Subscribe(EventHandler handler) { +GfxEvent::HandlerId +Gfx::Subscribe(GfxEvent::Handler handler) { o_assert_dbg(IsValid()); - return state->displayManager.Subscribe(handler); + return state->backend.Subscribe(handler); } //------------------------------------------------------------------------------ void -Gfx::Unsubscribe(EventHandlerId id) { +Gfx::Unsubscribe(GfxEvent::HandlerId id) { o_assert_dbg(IsValid()); - state->displayManager.Unsubscribe(id); + state->backend.Unsubscribe(id); } //------------------------------------------------------------------------------ -const GfxSetup& -Gfx::GfxSetup() { +const GfxDesc& +Gfx::Desc() { o_assert_dbg(IsValid()); - return state->gfxSetup; + return state->gfxDesc; } //------------------------------------------------------------------------------ const DisplayAttrs& Gfx::DisplayAttrs() { o_assert_dbg(IsValid()); - return state->displayManager.GetDisplayAttrs(); + return state->backend.displayManager.GetDisplayAttrs(); } //------------------------------------------------------------------------------ -const DisplayAttrs& -Gfx::PassAttrs() { +int +Gfx::Width() { o_assert_dbg(IsValid()); - return state->renderer.renderPassAttrs(); + return state->backend.displayManager.GetDisplayAttrs().Width; } //------------------------------------------------------------------------------ -const GfxFrameInfo& -Gfx::FrameInfo() { +int +Gfx::Height() { o_assert_dbg(IsValid()); - return state->gfxFrameInfo; + return state->backend.displayManager.GetDisplayAttrs().Height; } //------------------------------------------------------------------------------ -void -Gfx::BeginPass() { +const GfxFrameInfo& +Gfx::FrameInfo() { o_assert_dbg(IsValid()); - o_assert_dbg(!state->inPass); - state->inPass = true; - state->gfxFrameInfo.NumPasses++; - state->renderer.beginPass(nullptr, &state->gfxSetup.DefaultPassAction); + return state->gfxFrameInfo; } //------------------------------------------------------------------------------ @@ -140,183 +116,102 @@ Gfx::BeginPass(const PassAction& action) { o_assert_dbg(!state->inPass); state->inPass = true; state->gfxFrameInfo.NumPasses++; - state->renderer.beginPass(nullptr, &action); + state->backend.BeginPass(Id::InvalidId(), &action); } //------------------------------------------------------------------------------ void -Gfx::BeginPass(const Id& id) { +Gfx::BeginPass(const Id& id, const PassAction& passAction) { o_assert_dbg(IsValid()); o_assert_dbg(!state->inPass); state->inPass = true; state->gfxFrameInfo.NumPasses++; - renderPass* pass = state->resourceContainer.lookupRenderPass(id); - o_assert_dbg(pass); - state->renderer.beginPass(pass, &pass->Setup.DefaultAction); + state->backend.BeginPass(id, &passAction); } //------------------------------------------------------------------------------ void -Gfx::BeginPass(const Id& id, const PassAction& passAction) { +Gfx::EndPass() { o_assert_dbg(IsValid()); - o_assert_dbg(!state->inPass); - state->inPass = true; - state->gfxFrameInfo.NumPasses++; - renderPass* pass = state->resourceContainer.lookupRenderPass(id); - o_assert_dbg(pass); - state->renderer.beginPass(pass, &passAction); + o_assert_dbg(state->inPass); + state->inPass = false; + state->backend.EndPass(); } //------------------------------------------------------------------------------ void -Gfx::EndPass() { +Gfx::ApplyPipeline(const Id& pipId) { + o_trace_scoped(Gfx_ApplyPipeline); o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); - state->inPass = false; - state->renderer.endPass(); + state->gfxFrameInfo.NumApplyPipeline++; + state->backend.ApplyPipeline(pipId); } //------------------------------------------------------------------------------ void -Gfx::ApplyDrawState(const DrawState& drawState) { - o_trace_scoped(Gfx_ApplyDrawState); +Gfx::ApplyBindings(const Bindings& bindings) { + o_trace_scoped(Gfx_ApplyBindings); o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); - o_assert_dbg(drawState.Pipeline.Type == GfxResourceType::Pipeline); - state->gfxFrameInfo.NumApplyDrawState++; - - // apply pipeline and meshes - pipeline* pip = state->resourceContainer.lookupPipeline(drawState.Pipeline); - o_assert_dbg(pip); - mesh* meshes[GfxConfig::MaxNumInputMeshes] = { }; - int numMeshes = 0; - for (; numMeshes < GfxConfig::MaxNumInputMeshes; numMeshes++) { - if (drawState.Mesh[numMeshes].IsValid()) { - meshes[numMeshes] = state->resourceContainer.lookupMesh(drawState.Mesh[numMeshes]); - } - else { - break; - } - } - #if ORYOL_DEBUG - validateMeshes(pip, meshes, numMeshes); - #endif - state->renderer.applyDrawState(pip, meshes, numMeshes); - - // apply vertex textures if any - texture* vsTextures[GfxConfig::MaxNumVertexTextures] = { }; - int numVSTextures = 0; - for (; numVSTextures < GfxConfig::MaxNumVertexTextures; numVSTextures++) { - const Id& texId = drawState.VSTexture[numVSTextures]; - if (texId.IsValid()) { - vsTextures[numVSTextures] = state->resourceContainer.lookupTexture(texId); - } - else { - break; - } - } - if (numVSTextures > 0) { - #if ORYOL_DEBUG - validateTextures(ShaderStage::VS, pip, vsTextures, numVSTextures); - #endif - state->renderer.applyTextures(ShaderStage::VS, vsTextures, numVSTextures); - } - - // apply fragment textures if any - texture* fsTextures[GfxConfig::MaxNumFragmentTextures] = { }; - int numFSTextures = 0; - for (; numFSTextures < GfxConfig::MaxNumFragmentTextures; numFSTextures++) { - const Id& texId = drawState.FSTexture[numFSTextures]; - if (texId.IsValid()) { - fsTextures[numFSTextures] = state->resourceContainer.lookupTexture(texId); - } - else { - break; - } - } - if (numFSTextures > 0) { - #if ORYOL_DEBUG - validateTextures(ShaderStage::FS, pip, fsTextures, numFSTextures); - #endif - state->renderer.applyTextures(ShaderStage::FS, fsTextures, numFSTextures); - } + state->gfxFrameInfo.NumApplyBindings++; + state->backend.ApplyBindings(bindings); } //------------------------------------------------------------------------------ bool Gfx::QueryFeature(GfxFeature::Code feat) { o_assert_dbg(IsValid()); - return state->renderer.queryFeature(feat); + return state->backend.QueryFeature(feat); } //------------------------------------------------------------------------------ -ResourceLabel -Gfx::PushResourceLabel() { +ShaderLang::Code +Gfx::QueryShaderLang() { o_assert_dbg(IsValid()); - return state->resourceContainer.PushLabel(); + return state->backend.QueryShaderLang(); } //------------------------------------------------------------------------------ -void -Gfx::PushResourceLabel(ResourceLabel label) { +ResourceState::Code +Gfx::QueryResourceState(const Id& id) { o_assert_dbg(IsValid()); - state->resourceContainer.PushLabel(label); + return state->backend.QueryResourceState(id); } //------------------------------------------------------------------------------ ResourceLabel -Gfx::PopResourceLabel() { - o_assert_dbg(IsValid()); - return state->resourceContainer.PopLabel(); -} - -//------------------------------------------------------------------------------ -Id -Gfx::LoadResource(const Ptr& loader) { - o_assert_dbg(IsValid()); - return state->resourceContainer.Load(loader); -} - -//------------------------------------------------------------------------------ -Id -Gfx::LookupResource(const Locator& locator) { +Gfx::PushResourceLabel() { o_assert_dbg(IsValid()); - return state->resourceContainer.Lookup(locator); + return state->backend.PushResourceLabel(); } //------------------------------------------------------------------------------ -int -Gfx::QueryFreeResourceSlots(GfxResourceType::Code resourceType) { +void +Gfx::PushResourceLabel(ResourceLabel label) { o_assert_dbg(IsValid()); - return state->resourceContainer.QueryFreeSlots(resourceType); + state->backend.PushResourceLabel(label); } //------------------------------------------------------------------------------ -ResourceInfo -Gfx::QueryResourceInfo(const Id& id) { +ResourceLabel +Gfx::PopResourceLabel() { o_assert_dbg(IsValid()); - return state->resourceContainer.QueryResourceInfo(id); + return state->backend.PopResourceLabel(); } //------------------------------------------------------------------------------ -ResourcePoolInfo -Gfx::QueryResourcePoolInfo(GfxResourceType::Code resType) { +Id +Gfx::LookupResource(const Locator& locator) { o_assert_dbg(IsValid()); - return state->resourceContainer.QueryPoolInfo(resType); + return state->backend.LookupResource(locator); } //------------------------------------------------------------------------------ void Gfx::DestroyResources(ResourceLabel label) { o_assert_dbg(IsValid()); - return state->resourceContainer.DestroyDeferred(label); -} - -//------------------------------------------------------------------------------ -_priv::gfxResourceContainer* -Gfx::resource() { - o_assert_dbg(IsValid()); - return &(state->resourceContainer); + return state->backend.DestroyResources(label); } //------------------------------------------------------------------------------ @@ -325,7 +220,7 @@ Gfx::ApplyViewPort(int x, int y, int width, int height, bool originTopLeft) { o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); state->gfxFrameInfo.NumApplyViewPort++; - state->renderer.applyViewPort(x, y, width, height, originTopLeft); + state->backend.ApplyViewPort(x, y, width, height, originTopLeft); } //------------------------------------------------------------------------------ @@ -334,7 +229,7 @@ Gfx::ApplyScissorRect(int x, int y, int width, int height, bool originTopLeft) { o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); state->gfxFrameInfo.NumApplyScissorRect++; - state->renderer.applyScissorRect(x, y, width, height, originTopLeft); + state->backend.ApplyScissorRect(x, y, width, height, originTopLeft); } //------------------------------------------------------------------------------ @@ -343,9 +238,7 @@ Gfx::CommitFrame() { o_trace_scoped(Gfx_CommitFrame); o_assert_dbg(IsValid()); o_assert_dbg(!state->inPass); - state->renderer.commitFrame(); - state->displayManager.Present(); - state->resourceContainer.GarbageCollect(); + state->backend.CommitFrame(); state->gfxFrameInfo = GfxFrameInfo(); } @@ -354,47 +247,35 @@ void Gfx::ResetStateCache() { o_trace_scoped(Gfx_ResetStateCache); o_assert_dbg(IsValid()); - state->renderer.resetStateCache(); + state->backend.ResetStateCache(); } //------------------------------------------------------------------------------ void -Gfx::UpdateVertices(const Id& id, const void* data, int numBytes) { - o_trace_scoped(Gfx_UpdateVertices); +Gfx::UpdateBuffer(const Id& id, const void* data, int numBytes) { + o_trace_scoped(Gfx_UpdateBuffer); o_assert_dbg(IsValid()); - state->gfxFrameInfo.NumUpdateVertices++; - mesh* msh = state->resourceContainer.lookupMesh(id); - state->renderer.updateVertices(msh, data, numBytes); + state->gfxFrameInfo.NumUpdateBuffer++; + state->backend.UpdateBuffer(id, data, numBytes); } //------------------------------------------------------------------------------ void -Gfx::UpdateIndices(const Id& id, const void* data, int numBytes) { - o_trace_scoped(Gfx_UpdateIndices); - o_assert_dbg(IsValid()); - state->gfxFrameInfo.NumUpdateIndices++; - mesh* msh = state->resourceContainer.lookupMesh(id); - state->renderer.updateIndices(msh, data, numBytes); -} - -//------------------------------------------------------------------------------ -void -Gfx::UpdateTexture(const Id& id, const void* data, const ImageDataAttrs& offsetsAndSizes) { +Gfx::UpdateTexture(const Id& id, const ImageContent& content) { o_trace_scoped(Gfx_UpdateTexture); o_assert_dbg(IsValid()); - state->gfxFrameInfo.NumUpdateTextures++; - texture* tex = state->resourceContainer.lookupTexture(id); - state->renderer.updateTexture(tex, data, offsetsAndSizes); + state->gfxFrameInfo.NumUpdateTexture++; + state->backend.UpdateTexture(id, content); } //------------------------------------------------------------------------------ void -Gfx::Draw(int primGroupIndex, int numInstances) { +Gfx::Draw(int baseElement, int numElements, int numInstances) { o_trace_scoped(Gfx_Draw); o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); state->gfxFrameInfo.NumDraw++; - state->renderer.draw(primGroupIndex, numInstances); + state->backend.Draw(baseElement, numElements, numInstances); } //------------------------------------------------------------------------------ @@ -404,295 +285,127 @@ Gfx::Draw(const PrimitiveGroup& primGroup, int numInstances) { o_assert_dbg(IsValid()); o_assert_dbg(state->inPass); state->gfxFrameInfo.NumDraw++; - state->renderer.draw(primGroup.BaseElement, primGroup.NumElements, numInstances); + state->backend.Draw(primGroup.BaseElement, primGroup.NumElements, numInstances); } //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validateMeshes(pipeline* pip, mesh** meshes, int num) { - - // checks that: - // - at least one input mesh must be attached, and it must be in slot 0 - // - all attached input meshes must be valid - // - PrimitivesGroups may only be attached to the mesh in slot 0 - // - if indexed rendering is used, the mesh in slot 0 must have an index buffer - // - the mesh in slot 0 cannot contain per-instance-data - // - no colliding vertex attributes across all input meshes - // - // this method should only be called in debug mode - - // FIXME FIXME FIXME: check for matching vertex layout!!! - - o_assert_dbg(meshes && (num > 0) && (num < GfxConfig::MaxNumInputMeshes)); - if (nullptr == meshes[0]) { - o_error("invalid mesh block: at least one input mesh must be provided, in slot 0!\n"); - } - if ((meshes[0]->indexBufferAttrs.Type != IndexType::None) && - (meshes[0]->indexBufferAttrs.NumIndices == 0)) { - o_error("invalid mesh block: the input mesh at slot 0 uses indexed rendering, but has no indices!\n"); - } - - StaticArray vertexAttrCounts; - vertexAttrCounts.Fill(0); - for (int mshIndex = 0; mshIndex < GfxConfig::MaxNumInputMeshes; mshIndex++) { - const meshBase* msh = meshes[mshIndex]; - if (msh) { - if (ResourceState::Valid != msh->State) { - o_error("invalid mesh block: input mesh at slot '%d' not valid!\n", mshIndex); - } - if ((mshIndex > 0) && (msh->indexBufferAttrs.Type != IndexType::None)) { - o_error("invalid drawState: input mesh at slot '%d' has indices, only allowed for slot 0!\n", mshIndex); - } - if ((mshIndex > 0) && (msh->numPrimGroups > 0)) { - o_error("invalid mesh block: input mesh at slot '%d' has primitive groups, only allowed for slot 0!\n", mshIndex); - } - const int numComps = msh->vertexBufferAttrs.Layout.NumComponents(); - for (int compIndex = 0; compIndex < numComps; compIndex++) { - const auto& comp = msh->vertexBufferAttrs.Layout.ComponentAt(compIndex); - vertexAttrCounts[comp.Attr]++; - if (vertexAttrCounts[comp.Attr] > 1) { - o_error("invalid mesh block: same vertex attribute declared in multiple input meshes!\n"); - } - } - } +Id +Gfx::CreateTexture(const TextureDesc& desc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(desc.Locator); + if (!resId.IsValid()) { + resId = state->backend.CreateTexture(desc); + state->backend.AddResource(desc.Locator, resId); } + return resId; } -#endif //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validateTextures(ShaderStage::Code stage, pipeline* pip, texture** textures, int numTextures) { - o_assert_dbg(pip); - - // check if provided texture types are compatible with the expections shader - const shader* shd = pip->shd; - o_assert_dbg(shd); - for (int slot = 0; slot < numTextures; slot++) { - int index = shd->Setup.TextureIndexByStageAndSlot(stage, slot); - if ((InvalidIndex != index) && textures[slot]) { - if (textures[slot]->textureAttrs.Type != shd->Setup.TexType(index)) { - o_error("Texture type mismatch at slot '%d'!\n", slot); - } - } - else if ((InvalidIndex == index) && textures[slot]) { - o_warn("Texture applied at slot '%d' which isn't expected by shader.\n", slot); - } - else if ((InvalidIndex != index) && !textures[slot]) { - o_error("No texture applied at slot '%d', but shader expects one!\n", slot); - } +Id +Gfx::CreateBuffer(const BufferDesc& desc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(desc.Locator); + if (!resId.IsValid()) { + resId = state->backend.CreateBuffer(desc); + state->backend.AddResource(desc.Locator, resId); } + return resId; } -#endif //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validateTextureSetup(const TextureSetup& setup, const void* data, int size) { - o_assert((setup.NumMipMaps > 0) && (setup.NumMipMaps <= GfxConfig::MaxNumTextureMipMaps)); - o_assert((setup.Width >= 1) && (setup.Height >= 1) && (setup.Depth >= 1)); - if (data) { - o_assert(size > 0); - o_assert(setup.TextureUsage == Usage::Immutable); - o_assert(setup.ImageData.NumMipMaps == setup.NumMipMaps); - if (setup.Type == TextureType::TextureCube) { - o_assert(setup.ImageData.NumFaces == 6); - } - else { - o_assert(setup.ImageData.NumFaces == 1); - } - } - if (setup.Type == TextureType::Texture2D) { - o_assert(setup.Depth == 1); - } - if (setup.Type == TextureType::TextureArray) { - o_assert(setup.Depth <= GfxConfig::MaxNumTextureArraySlices); - } - if (setup.Type == TextureType::Texture3D) { - o_assert(!setup.IsRenderTarget); - } - if (setup.IsRenderTarget) { - o_assert(setup.TextureUsage == Usage::Immutable); - o_assert(PixelFormat::IsValidRenderTargetColorFormat(setup.ColorFormat)); - if (setup.DepthFormat != PixelFormat::InvalidPixelFormat) { - o_assert(PixelFormat::IsValidRenderTargetDepthFormat(setup.DepthFormat)); - } - } - else { - o_assert(setup.SampleCount == 1); - o_assert(setup.DepthFormat == PixelFormat::InvalidPixelFormat); +Id +Gfx::CreateShader(const ShaderDesc& desc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(desc.Locator); + if (!resId.IsValid()) { + resId = state->backend.CreateShader(desc); + state->backend.AddResource(desc.Locator, resId); } + return resId; } -#endif //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validateMeshSetup(const MeshSetup& setup, const void* data, int size) { - o_assert(setup.ShouldSetupFullScreenQuad() || (setup.VertexUsage != Usage::InvalidUsage) || (setup.IndexUsage != Usage::InvalidUsage)); - if (setup.NumVertices > 0) { - o_assert(!setup.Layout.Empty()); - if (setup.VertexUsage == Usage::Immutable) { - o_assert(data && (size > 0)); - o_assert((setup.VertexDataOffset >= 0) && (setup.VertexDataOffset < size)); - } - } - if (setup.NumIndices > 0) { - o_assert((setup.IndicesType == IndexType::Index16) || (setup.IndicesType == IndexType::Index32)); - if (setup.IndexUsage == Usage::Immutable) { - o_assert(data && (size > 0)); - o_assert((setup.IndexDataOffset >= 0) && (setup.IndexDataOffset < size)); - } +Id +Gfx::CreatePipeline(const PipelineDesc& desc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(desc.Locator); + if (!resId.IsValid()) { + resId = state->backend.CreatePipeline(desc); + state->backend.AddResource(desc.Locator, resId); } + return resId; } -#endif //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validatePipelineSetup(const PipelineSetup& setup) { - o_assert(setup.PrimType != PrimitiveType::InvalidPrimitiveType); - o_assert(setup.Shader.IsValid()); - bool anyLayoutValid = false; - for (const auto& layout : setup.Layouts) { - if (!layout.Empty()) { - anyLayoutValid = true; - break; - } +Id +Gfx::CreatePass(const PassDesc& desc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(desc.Locator); + if (!resId.IsValid()) { + resId = state->backend.CreatePass(desc); + state->backend.AddResource(desc.Locator, resId); } - o_assert(anyLayoutValid); + return resId; } -#endif //------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validatePassSetup(const PassSetup& setup) { - // check that at least one color attachment texture is defined - // and that there are no 'holes' if there are multiple attachments - bool continuous = true; - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (setup.ColorAttachments[i].Texture.IsValid()) { - if (!continuous) { - o_error("invalid render pass: must have continuous color attachments!\n"); - } - } - else { - if (0 == i) { - o_error("invalid render pass: must have color attachment at slot 0!\n"); - } - continuous = false; - } - } - - // check that all render targets have the required params - const texture* t0 = state->resourceContainer.lookupTexture(setup.ColorAttachments[0].Texture); - o_assert(t0); - const int w = t0->textureAttrs.Width; - const int h = t0->textureAttrs.Height; - const int sampleCount = t0->textureAttrs.SampleCount; - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - const texture* tex = state->resourceContainer.lookupTexture(setup.ColorAttachments[i].Texture); - if (tex) { - const auto& attrs = tex->textureAttrs; - if ((attrs.Width != w) || (attrs.Height != h)) { - o_error("invalid render pass: all color attachments must have the same size!\n"); - } - if (attrs.SampleCount != sampleCount) { - o_error("invalid render pass: all color attachments must have same sample-count!\n"); - } - if (attrs.TextureUsage != Usage::Immutable) { - o_error("invalid render pass: color attachments must have immutable usage!\n"); - } - if (!tex->Setup.IsRenderTarget) { - o_error("invalid render pass: color attachment must have been setup as render target!\n"); - } - } - } - const texture* dsTex = state->resourceContainer.lookupTexture(setup.DepthStencilTexture); - if (dsTex) { - const auto& attrs = dsTex->textureAttrs; - if ((attrs.Width != w) || (attrs.Height != h)) { - o_error("invalid render pass: depth-stencil attachment must have same size as color attachments!\n"); - } - if (attrs.SampleCount != sampleCount) { - o_error("invalid render pass: depth-stencil attachment must have sample sample-count as color attachments!\n"); - } - if (attrs.TextureUsage != Usage::Immutable) { - o_error("invalid render pass: depth attachment must have immutable usage!\n"); - } - if (!dsTex->Setup.IsRenderTarget) { - o_error("invalid render pass: depth attachment must have been setup as render target!\n"); - } +Id +Gfx::AllocBuffer(const Locator& loc) { + o_assert_dbg(IsValid()); + Id resId = state->backend.LookupResource(loc); + if (!resId.IsValid()) { + resId = state->backend.AllocBuffer(); + state->backend.AddResource(loc, resId); } + return resId; } -#endif - -//------------------------------------------------------------------------------ -#if ORYOL_DEBUG -void -Gfx::validateShaderSetup(const ShaderSetup& setup) { - // hmm, FIXME -} -#endif //------------------------------------------------------------------------------ -template<> Id -Gfx::CreateResource(const TextureSetup& setup, const void* data, int size) { +Id +Gfx::AllocTexture(const Locator& loc) { o_assert_dbg(IsValid()); - #if ORYOL_DEBUG - validateTextureSetup(setup, data, size); - #endif - return state->resourceContainer.Create(setup, data, size); + Id resId = state->backend.LookupResource(loc); + if (!resId.IsValid()) { + resId = state->backend.AllocTexture(); + state->backend.AddResource(loc, resId); + } + return resId; } //------------------------------------------------------------------------------ -template<> Id -Gfx::CreateResource(const MeshSetup& setup, const void* data, int size) { +void +Gfx::InitBuffer(const Id& id, const BufferDesc& desc) { o_assert_dbg(IsValid()); - #if ORYOL_DEBUG - validateMeshSetup(setup, data, size); - #endif - return state->resourceContainer.Create(setup, data, size); + state->backend.InitBuffer(id, desc); } //------------------------------------------------------------------------------ -template<> Id -Gfx::CreateResource(const ShaderSetup& setup, const void* data, int size) { +void +Gfx::InitTexture(const Id& id, const TextureDesc& desc) { o_assert_dbg(IsValid()); - #if ORYOL_DEBUG - validateShaderSetup(setup); - #endif - return state->resourceContainer.Create(setup, nullptr, 0); + state->backend.InitTexture(id, desc); } //------------------------------------------------------------------------------ -template<> Id -Gfx::CreateResource(const PipelineSetup& setup, const void* data, int size) { +void +Gfx::FailBuffer(const Id& id) { o_assert_dbg(IsValid()); - #if ORYOL_DEBUG - validatePipelineSetup(setup); - #endif - return state->resourceContainer.Create(setup, nullptr, 0); + state->backend.FailBuffer(id); } //------------------------------------------------------------------------------ -template<> Id -Gfx::CreateResource(const PassSetup& setup, const void* data, int size) { +void +Gfx::FailTexture(const Id& id) { o_assert_dbg(IsValid()); - #if ORYOL_DEBUG - validatePassSetup(setup); - #endif - return state->resourceContainer.Create(setup, nullptr, 0); + state->backend.FailTexture(id); } //------------------------------------------------------------------------------ void -Gfx::applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t layoutHash, const uint8_t* ptr, int byteSize) { +Gfx::ApplyUniforms(ShaderStage::Code bindStage, int bindSlot, const void* ptr, int byteSize) { o_assert_dbg(IsValid()); - state->gfxFrameInfo.NumApplyUniformBlock++; - state->renderer.applyUniformBlock(bindStage, bindSlot, layoutHash, ptr, byteSize); + state->gfxFrameInfo.NumApplyUniforms++; + state->backend.ApplyUniforms(bindStage, bindSlot, ptr, byteSize); } } // namespace Oryol diff --git a/code/Modules/Gfx/Gfx.h b/code/Modules/Gfx/Gfx.h index 8293ea92b..f6d8816b6 100644 --- a/code/Modules/Gfx/Gfx.h +++ b/code/Modules/Gfx/Gfx.h @@ -11,24 +11,15 @@ #include "Core/RunLoop.h" #include "Gfx/GfxTypes.h" #include "Resource/ResourceLabel.h" -#include "Resource/ResourceLoader.h" -#include "Resource/SetupAndData.h" #include "Resource/ResourceInfo.h" #include "Resource/ResourcePoolInfo.h" namespace Oryol { -namespace _priv { -class gfxResourceContainer; -class pipeline; -class texture; -class mesh; -} - class Gfx { public: /// setup Gfx module - static void Setup(const GfxSetup& setup); + static void Setup(const GfxDesc& desc); /// discard Gfx module static void Discard(); /// check if Gfx module is setup @@ -37,21 +28,19 @@ class Gfx { /// test if the window system wants the application to quit static bool QuitRequested(); - /// event handler callback typedef - typedef std::function EventHandler; - /// event handler id typedef - typedef unsigned int EventHandlerId; /// subscribe to display events - static EventHandlerId Subscribe(EventHandler handler); + static GfxEvent::HandlerId Subscribe(GfxEvent::Handler handler); /// unsubscribe from display events - static void Unsubscribe(EventHandlerId id); + static void Unsubscribe(GfxEvent::HandlerId id); /// get the original render setup object - static const class GfxSetup& GfxSetup(); + static const GfxDesc& Desc(); /// get the default frame buffer attributes static const struct DisplayAttrs& DisplayAttrs(); - /// get the current render pass attributes (default or offscreen) - static const struct DisplayAttrs& PassAttrs(); + /// get current default framebuffer width + static int Width(); + /// get current default framebuffer height + static int Height(); /// get frame-render stats, gets reset in CommitFrame()! static const GfxFrameInfo& FrameInfo(); @@ -61,38 +50,47 @@ class Gfx { static void PushResourceLabel(ResourceLabel label); /// pop resource label from label stack static ResourceLabel PopResourceLabel(); - /// create a resource object without associated data - template static Id CreateResource(const SETUP& setup); - /// create a resource object with associated data - template static Id CreateResource(const SetupAndData& setupAndData); - /// create a resource object with associated data - template static Id CreateResource(const SETUP& setup, const Buffer& data); - /// create a resource object with raw pointer to associated data - template static Id CreateResource(const SETUP& setup, const void* data, int size); - /// asynchronously load resource object - static Id LoadResource(const Ptr& loader); + + /// create a buffer object without associated data + static Id CreateBuffer(const BufferDesc& desc); + /// create a texture object without associated data + static Id CreateTexture(const TextureDesc& desc); + /// create a shader object + static Id CreateShader(const ShaderDesc& desc); + /// create a pipeline object + static Id CreatePipeline(const PipelineDesc& desc); + /// create a render-pass object + static Id CreatePass(const PassDesc& desc); + /// lookup a resource Id by Locator static Id LookupResource(const Locator& locator); /// destroy one or several resources by matching label static void DestroyResources(ResourceLabel label); + /// allocate a buffer resource id (async resource creation) + static Id AllocBuffer(const Locator& loc); + /// initialize a buffer (async resource creation) + static void InitBuffer(const Id& id, const BufferDesc& desc); + /// set allocated buffer to failed resource state (async resource creation) + static void FailBuffer(const Id& id); + /// allocate a texture resource id (async resource creation) + static Id AllocTexture(const Locator& loc); + /// initialize a texture (async resource creation) + static void InitTexture(const Id& id, const TextureDesc& desc); + /// set allocated texture to failed resource state (async resource creation) + static void FailTexture(const Id& id); + /// test if an optional feature is supported static bool QueryFeature(GfxFeature::Code feat); - /// query number of free slots for resource type - static int QueryFreeResourceSlots(GfxResourceType::Code resourceType); - /// query resource info (fast) - static ResourceInfo QueryResourceInfo(const Id& id); - /// query resource pool info (slow) - static ResourcePoolInfo QueryResourcePoolInfo(GfxResourceType::Code resType); + /// get the supported shader language + static ShaderLang::Code QueryShaderLang(); + /// query the resource state of a resource + static ResourceState::Code QueryResourceState(const Id& id); - /// begin rendering to default render pass - static void BeginPass(); /// begin rendering to default render pass with override clear values - static void BeginPass(const PassAction& action); - /// begin offscreen rendering - static void BeginPass(const Id& passId); + static void BeginPass(const PassAction& action=PassAction()); /// begin offscreen rendering with override clear colors - static void BeginPass(const Id& passId, const PassAction& action); + static void BeginPass(const Id& passId, const PassAction& action=PassAction()); /// finish rendering to current pass static void EndPass(); @@ -100,78 +98,35 @@ class Gfx { static void ApplyViewPort(int x, int y, int width, int height, bool originTopLeft=false); /// apply scissor rect (must also be enabled in Pipeline object) static void ApplyScissorRect(int x, int y, int width, int height, bool originTopLeft=false); - /// apply draw state (Pipeline, Meshes and Textures) - static void ApplyDrawState(const DrawState& drawState); - /// apply a uniform block (call between ApplyDrawState and Draw) - template static void ApplyUniformBlock(const T& ub); - - /// update dynamic vertex data (complete replace) - static void UpdateVertices(const Id& id, const void* data, int numBytes); - /// update dynamic index data (complete replace) - static void UpdateIndices(const Id& id, const void* data, int numBytes); + /// apply pipeline state + static void ApplyPipeline(const Id& pipId); + /// apply resource bindings for next draw call (call between ApplyPipeline and Draw) + static void ApplyBindings(const Bindings& binding); + /// apply shader uniforms for next draw call (call between ApplyPipeline and Draw) + template static void ApplyUniforms(const T& ub); + /// 'raw form' of ApplyUniforms, with explicit shader state, uniform buffer slot and data ptr/size + static void ApplyUniforms(ShaderStage::Code stage, int slot, const void* data, int numBytes); + + /// update dynamic vertex or index data (complete replace) + static void UpdateBuffer(const Id& id, const void* data, int numBytes); /// update dynamic texture image data (complete replace) - static void UpdateTexture(const Id& id, const void* data, const ImageDataAttrs& offsetsAndSizes); + static void UpdateTexture(const Id& id, const ImageContent& content); - /// submit a draw call with primitive group index - static void Draw(int primGroupIndex=0, int numInstances=1); - /// submit a draw call with explicit primitve range + /// submit a draw call + static void Draw(int baseElement, int numElements, int numInstances=1); + /// submit a draw call with baseElement and numElements taken from PrimitiveGroup static void Draw(const PrimitiveGroup& primGroup, int numInstances=1); /// commit (and display) the current frame static void CommitFrame(); /// reset the native 3D-API state-cache static void ResetStateCache(); - - /// direct access to resource container (private interface for resource loaders) - static _priv::gfxResourceContainer* resource(); - -private: - #if ORYOL_DEBUG - /// validate texture setup params - static void validateTextureSetup(const TextureSetup& setup, const void* data, int size); - /// validate mesh setup params - static void validateMeshSetup(const MeshSetup& setup, const void* data, int size); - /// validate pipeline setup params - static void validatePipelineSetup(const PipelineSetup& setup); - /// validate render pass setup params - static void validatePassSetup(const PassSetup& setup); - /// validate shader setup params - static void validateShaderSetup(const ShaderSetup& setup); - /// validate mesh binding - static void validateMeshes(_priv::pipeline* pip, _priv::mesh** meshes, int numMeshes); - /// validate texture binding - static void validateTextures(ShaderStage::Code stage, _priv::pipeline* pip, _priv::texture** textures, int numTextures); - #endif - /// apply uniform block, non-template version - static void applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t layoutHash, const uint8_t* ptr, int byteSize); }; //------------------------------------------------------------------------------ template inline void -Gfx::ApplyUniformBlock(const T& ub) { - applyUniformBlock(T::_bindShaderStage, T::_bindSlotIndex, T::_layoutHash, (const uint8_t*)&ub, sizeof(ub)); -} - -//------------------------------------------------------------------------------ -template inline Id -Gfx::CreateResource(const SETUP& setup) { - o_assert_dbg(IsValid()); - return CreateResource(setup, nullptr, 0); -} - -//------------------------------------------------------------------------------ -template inline Id -Gfx::CreateResource(const SETUP& setup, const Buffer& data) { - o_assert_dbg(IsValid()); - o_assert_dbg(!data.Empty()); - return CreateResource(setup, data.Data(), data.Size()); -} - -//------------------------------------------------------------------------------ -template inline Id -Gfx::CreateResource(const SetupAndData& setupAndData) { - o_assert_dbg(IsValid()); - return CreateResource(setupAndData.Setup, setupAndData.Data); +Gfx::ApplyUniforms(const T& ub) { + ApplyUniforms(T::_bindShaderStage, T::_bindSlotIndex, (const uint8_t*)&ub, sizeof(ub)); } } // namespace Oryol diff --git a/code/Modules/Gfx/GfxConfig.h b/code/Modules/Gfx/GfxConfig.h deleted file mode 100644 index 20f825799..000000000 --- a/code/Modules/Gfx/GfxConfig.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::GfxConfig - @brief central configuration constants of the Gfx module -*/ -#include "Core/Types.h" - -namespace Oryol { - -class GfxConfig { -public: - /// default resource pool size - static const int DefaultResourcePoolSize = 128; - /// default uniform buffer size (only relevant on some platforms) - static const int DefaultGlobalUniformBufferSize = 4 * 1024 * 1024; - /// default maximum number of draw-calls per frame (only relevant on some platforms) - static const int DefaultMaxDrawCallsPerFrame = (1<<16); - /// default maximum number of Gfx::ApplyDrawState per frame (only relevant on some platforms) - static const int DefaultMaxApplyDrawStatesPerFrame = 4096; - /// max number of input meshes - static const int MaxNumInputMeshes = 4; - /// maximum number of primitive groups for one mesh - static const int MaxNumPrimGroups = 8; - /// max number of uniform blocks per stage - static const int MaxNumUniformBlocksPerStage = 4; - /// maximum number of textures on vertex shader stage - static const int MaxNumVertexTextures = 4; - /// maximum number of textures on fragment shader stage - static const int MaxNumFragmentTextures = 12; - /// max number of textures on any stage - static const int MaxNumShaderTextures = MaxNumVertexTextures>MaxNumFragmentTextures?MaxNumVertexTextures:MaxNumFragmentTextures; - /// max number of texture faces - static const int MaxNumTextureFaces = 6; - /// max number of texture array slices - static const int MaxNumTextureArraySlices = 128; - /// max number of texture mipmaps - static const int MaxNumTextureMipMaps = 12; - /// maximum number of components in vertex layout - static const int MaxNumVertexLayoutComponents = 16; - /// maximum number of in-flight frames (not used by all platforms) - static const int MaxInflightFrames = 2; - /// maximum number of render pass color attachments - static const int MaxNumColorAttachments = 4; -}; - -} // namespace Oryol diff --git a/code/Modules/Gfx/GfxTypes.cc b/code/Modules/Gfx/GfxTypes.cc index 1824491f3..d02d69086 100644 --- a/code/Modules/Gfx/GfxTypes.cc +++ b/code/Modules/Gfx/GfxTypes.cc @@ -3,6 +3,7 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "GfxTypes.h" +#include "Gfx.h" #include namespace Oryol { @@ -11,8 +12,8 @@ namespace Oryol { int IndexType::ByteSize(IndexType::Code c) { switch (c) { case None: return 0; - case Index16: return 2; - case Index32: return 4; + case UInt16: return 2; + case UInt32: return 4; default: o_error("IndexType::ByteSize() called with invalid type!\n"); return 0; @@ -316,68 +317,6 @@ int PixelFormat::ImagePitch(PixelFormat::Code fmt, int width, int height) { return pitch; } -//------------------------------------------------------------------------------ -const char* PrimitiveType::ToString(PrimitiveType::Code c) { - switch (c) { - case Points: return "Points"; - case Lines: return "Lines"; - case LineStrip: return "LineStrip"; - case Triangles: return "Triangles"; - case TriangleStrip: return "TriangleStrip"; - default: - o_error("PrimitiveType::ToString(): invalid value!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -const char* VertexAttr::ToString(Code c) { - switch (c) { - case Position: return "position"; - case Normal: return "normal"; - case TexCoord0: return "texcoord0"; - case TexCoord1: return "texcoord1"; - case TexCoord2: return "texcoord2"; - case TexCoord3: return "texcoord3"; - case Tangent: return "tangent"; - case Binormal: return "binormal"; - case Weights: return "weights"; - case Indices: return "indices"; - case Color0: return "color0"; - case Color1: return "color1"; - case Instance0: return "instance0"; - case Instance1: return "instance1"; - case Instance2: return "instance2"; - case Instance3: return "instance3"; - default: - o_error("VertexAttr::ToString(): invalid value!\n"); - return nullptr; - } -} - -//------------------------------------------------------------------------------ -VertexAttr::Code VertexAttr::FromString(const char* str) { - if (str) { - if (0 == std::strcmp("position", str)) return Position; - else if (0 == std::strcmp("normal", str)) return Normal; - else if (0 == std::strcmp("texcoord0", str)) return TexCoord0; - else if (0 == std::strcmp("texcoord1", str)) return TexCoord1; - else if (0 == std::strcmp("texcoord2", str)) return TexCoord2; - else if (0 == std::strcmp("texcoord3", str)) return TexCoord3; - else if (0 == std::strcmp("tangent", str)) return Tangent; - else if (0 == std::strcmp("binormal", str)) return Binormal; - else if (0 == std::strcmp("weights", str)) return Weights; - else if (0 == std::strcmp("indices", str)) return Indices; - else if (0 == std::strcmp("color0", str)) return Color0; - else if (0 == std::strcmp("color1", str)) return Color1; - else if (0 == std::strcmp("instance0", str)) return Instance0; - else if (0 == std::strcmp("instance1", str)) return Instance1; - else if (0 == std::strcmp("instance2", str)) return Instance2; - else if (0 == std::strcmp("instance3", str)) return Instance3; - } - return InvalidVertexAttr; -} - //------------------------------------------------------------------------------ int VertexFormat::ByteSize(Code c) { switch (c) { @@ -429,144 +368,135 @@ const char* VertexFormat::ToString(Code c) { } //------------------------------------------------------------------------------ -BlendState::BlendState() { - static_assert(sizeof(BlendState) == 8, "sizeof(BlendState) is not 8, bitfield packing problem?"); - this->Hash = 0; - this->BlendEnabled = false; - this->SrcFactorRGB = BlendFactor::One; - this->DstFactorRGB = BlendFactor::Zero; - this->OpRGB = BlendOperation::Add; - this->SrcFactorAlpha = BlendFactor::One; - this->DstFactorAlpha = BlendFactor::Zero; - this->OpAlpha = BlendOperation::Add; - this->ColorWriteMask = PixelChannel::RGBA; - this->ColorFormat = PixelFormat::RGBA8; - this->DepthFormat = PixelFormat::DEPTHSTENCIL; - this->MRTCount = 1; +PrimitiveGroup::PrimitiveGroup(int baseElm, int numElms): + BaseElement(baseElm), + NumElements(numElms) +{ } + +//------------------------------------------------------------------------------ +PrimitiveGroup& PrimitiveGroup::SetBaseElement(int baseElm) { + this->BaseElement = baseElm; + return *this; } //------------------------------------------------------------------------------ -StencilState::StencilState() { - static_assert(sizeof(StencilState) == 2, "sizeof(StencilState) is not 2, bitfield packing problem?"); - this->Hash = 0; - this->FailOp = StencilOp::Keep; - this->DepthFailOp = StencilOp::Keep; - this->PassOp = StencilOp::Keep; - this->CmpFunc = CompareFunc::Always; +PrimitiveGroup& PrimitiveGroup::SetNumElements(int numElms) { + this->NumElements = numElms; + return *this; } - + //------------------------------------------------------------------------------ -DepthStencilState::DepthStencilState() { - static_assert(sizeof(DepthStencilState) == 8, "sizeof(DepthStencilState) is not 8, bitfield packing problem?"); - this->Hash = 0; - this->DepthCmpFunc = CompareFunc::Always; - this->DepthWriteEnabled = false; - this->StencilEnabled = false; - this->StencilReadMask = 0xFF; - this->StencilWriteMask = 0xFF; - this->StencilRef = 0; +PassAction::PassAction() { + for (auto& c : this->Color) { + c = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); + } } //------------------------------------------------------------------------------ -bool DepthStencilState::operator==(const DepthStencilState& rhs) const { - return (this->Hash == rhs.Hash) && - (this->StencilFront == rhs.StencilFront) && - (this->StencilBack == rhs.StencilBack); +PassAction& PassAction::Load() { + this->Flags = LoadC0|LoadC1|LoadC2|LoadC3|LoadDS; + return *this; } //------------------------------------------------------------------------------ -bool DepthStencilState::operator!=(const DepthStencilState& rhs) const { - return (this->Hash != rhs.Hash) || - (this->StencilFront != rhs.StencilFront) || - (this->StencilBack != rhs.StencilBack); +PassAction& PassAction::DontCare() { + this->Flags = 0; + return *this; } //------------------------------------------------------------------------------ -RasterizerState::RasterizerState() { - static_assert(sizeof(RasterizerState) == 2, "sizeof(RasterizerState) is not 4, bitfield packing problem?"); - this->Hash = 0; - this->CullFaceEnabled = false; - this->ScissorTestEnabled = false; - this->DitherEnabled = true; - this->AlphaToCoverageEnabled = false; - this->CullFace = Face::Back; - this->SampleCount = 1; +PassAction& PassAction::ClearColor(int index, float r, float g, float b, float a) { + o_assert_range_dbg(index, GfxConfig::MaxNumColorAttachments); + this->Color[index] = glm::vec4(r, g, b, a); + this->Flags |= (ClearC0<Hash = 0; - this->WrapU = TextureWrapMode::Repeat; - this->WrapV = TextureWrapMode::Repeat; - this->WrapW = TextureWrapMode::Repeat; - this->MagFilter = TextureFilterMode::Nearest; - this->MinFilter = TextureFilterMode::Nearest; +PassAction& PassAction::ClearColor(int index, const glm::vec4& color) { + o_assert_range_dbg(index, GfxConfig::MaxNumColorAttachments); + this->Color[index] = color; + this->Flags |= (ClearC0<Color) { - c = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); +PassAction& PassAction::ClearColor(float r, float g, float b, float a) { + for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { + this->ClearColor(i, r, g, b, a); } + return *this; +} + +//------------------------------------------------------------------------------ +PassAction& PassAction::ClearColor(const glm::vec4& color) { + for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { + this->ClearColor(i, color); + } + return *this; } //------------------------------------------------------------------------------ -PassAction PassAction::Clear(const glm::vec4& c, float d, uint8_t s) { - PassAction p; - p.Color.Fill(c); - p.Depth = d; - p.Stencil = s; - return p; +PassAction& PassAction::ClearDepthStencil(float d, uint8_t s) { + this->Depth = d; + this->Stencil = s; + this->Flags |= ClearDS; + return *this; +} + +//------------------------------------------------------------------------------ +PassAction& PassAction::Clear(float r, float g, float b, float a, float d, uint8_t s) { + this->ClearColor(r, g, b, a); + this->ClearDepthStencil(d, s); + return *this; +} + +//------------------------------------------------------------------------------ +PassAction& PassAction::Clear(const glm::vec4& c, float d, uint8_t s) { + this->ClearColor(c); + this->ClearDepthStencil(d, s); + return *this; } //------------------------------------------------------------------------------ -PassAction PassAction::Clear(std::initializer_list colors, float d, uint8_t s) { - PassAction p; +PassAction& PassAction::Clear(std::initializer_list colors, float d, uint8_t s) { int i = 0; for (const auto& c : colors) { - p.Color[i++] = c; + this->ClearColor(i++, c); } - p.Depth = d; - p.Stencil = s; - return p; + this->ClearDepthStencil(d, s); + return *this; } //------------------------------------------------------------------------------ -PassAction PassAction::Load() { - PassAction p; - p.Flags = LoadC0|LoadC1|LoadC2|LoadC3|LoadDS; - return p; +PassAction& PassAction::LoadColor(int index) { + o_assert_range_dbg(index, GfxConfig::MaxNumColorAttachments); + this->Flags |= LoadC0<Flags |= LoadC0|LoadC1|LoadC2|LoadC3; + return *this; } //------------------------------------------------------------------------------ -PassAction& PassAction::ClearColor(int index, const glm::vec4& color) { - this->Color[index] = color; - this->Flags &= ~((ClearC0|LoadC0)<Flags |= ClearC0<Flags |= LoadDS; return *this; } //------------------------------------------------------------------------------ -PassAction& PassAction::ClearDepthStencil(float d, uint8_t s) { - this->Depth = d; - this->Stencil = s; - this->Flags &= ~(ClearDS|LoadDS); - this->Flags |= ClearDS; +PassAction& PassAction::DontCareColor(int index) { + o_assert_range_dbg(index, GfxConfig::MaxNumColorAttachments); + this->Flags &= ~(ClearC0|LoadC0<Flags &= ~((ClearC0|LoadC0)<Flags &= ~(ClearC0|ClearC1|ClearC2|ClearC3|LoadC0|LoadC1|LoadC2|LoadC3); return *this; } @@ -577,34 +507,48 @@ PassAction& PassAction::DontCareDepthStencil() { } //------------------------------------------------------------------------------ -PassAction& PassAction::LoadColor(int index) { - o_assert_range_dbg(index, GfxConfig::MaxNumColorAttachments); - this->Flags &= ~((ClearC0|LoadC0)<Flags |= LoadC0<VertexBufferOffsets.Fill(0); +} + +//------------------------------------------------------------------------------ +Bindings& Bindings::SetVertexBuffer(int slot, const Id& bufId) { + o_assert_dbg(!bufId.IsValid() || (bufId.Type == GfxResourceType::Buffer)); + this->VertexBuffers[slot] = bufId; return *this; } //------------------------------------------------------------------------------ -PassAction& PassAction::LoadDepthStencil() { - this->Flags &= ~(ClearDS|LoadDS); - this->Flags |= LoadDS; +Bindings& Bindings::SetVertexBufferOffset(int slot, int offset) { + this->VertexBufferOffsets[slot] = offset; return *this; } //------------------------------------------------------------------------------ -bool VertexLayout::Component::IsValid() const { - return (VertexAttr::InvalidVertexAttr != this->Attr); +Bindings& Bindings::SetIndexBuffer(const Id& bufId) { + o_assert_dbg(!bufId.IsValid() || (bufId.Type == GfxResourceType::Buffer)); + this->IndexBuffer = bufId; + return *this; } //------------------------------------------------------------------------------ -void VertexLayout::Component::Clear() { - this->Attr = VertexAttr::InvalidVertexAttr; - this->Format = VertexFormat::InvalidVertexFormat; +Bindings& Bindings::SetIndexBufferOffset(int offset) { + this->IndexBufferOffset = offset; + return *this; } //------------------------------------------------------------------------------ -int VertexLayout::Component::ByteSize() const { - return VertexFormat::ByteSize(this->Format); +Bindings& Bindings::SetVSTexture(int slot, const Id& texId) { + o_assert_dbg(!texId.IsValid() || (texId.Type == GfxResourceType::Texture)); + this->VSTexture[slot] = texId; + return *this; +} + +//------------------------------------------------------------------------------ +Bindings& Bindings::SetFSTexture(int slot, const Id& texId) { + o_assert_dbg(!texId.IsValid() || (texId.Type == GfxResourceType::Texture)); + this->FSTexture[slot] = texId; + return *this; } //------------------------------------------------------------------------------ @@ -626,8 +570,6 @@ VertexLayout& VertexLayout::Clear() { this->StepRate = 1; this->numComps = 0; this->byteSize = 0; - this->attrCompIndices.Fill(InvalidIndex); - this->byteOffsets.Fill(0); return *this; } @@ -639,19 +581,21 @@ bool VertexLayout::Empty() const { //------------------------------------------------------------------------------ VertexLayout& VertexLayout::Add(const Component& comp) { o_assert_dbg(this->numComps < GfxConfig::MaxNumVertexLayoutComponents); - o_assert_dbg(InvalidIndex == this->attrCompIndices[comp.Attr]); this->comps[this->numComps] = comp; - this->attrCompIndices[comp.Attr] = this->numComps; - this->byteOffsets[this->numComps] = this->byteSize; + this->comps[this->numComps].Offset = this->byteSize; this->byteSize += comp.ByteSize(); - o_assert_dbg(this->byteSize < 248); this->numComps++; return *this; } //------------------------------------------------------------------------------ -VertexLayout& VertexLayout::Add(VertexAttr::Code attr, VertexFormat::Code format) { - return this->Add(Component(attr, format)); +VertexLayout& VertexLayout::Add(VertexFormat::Code format) { + return this->Add(Component(format)); +} + +//------------------------------------------------------------------------------ +VertexLayout& VertexLayout::Add(const StringAtom& name, VertexFormat::Code format) { + return this->Add(Component(name, format)); } //------------------------------------------------------------------------------ @@ -680,8 +624,18 @@ const VertexLayout::Component& VertexLayout::ComponentAt(int index) const { } //------------------------------------------------------------------------------ -int VertexLayout::ComponentIndexByVertexAttr(VertexAttr::Code attr) const { - return this->attrCompIndices[attr]; +int VertexLayout::ComponentIndexByName(const StringAtom& name) const { + for (int i = 0; i < this->numComps; i++) { + if (this->comps[i].Name == name) { + return i; + } + } + return InvalidIndex; +} + +//------------------------------------------------------------------------------ +bool VertexLayout::Contains(const StringAtom& name) const { + return InvalidIndex != this->ComponentIndexByName(name); } //------------------------------------------------------------------------------ @@ -691,681 +645,633 @@ int VertexLayout::ByteSize() const { //------------------------------------------------------------------------------ int VertexLayout::ComponentByteOffset(int componentIndex) const { - return this->byteOffsets[componentIndex]; + o_assert_dbg(componentIndex < this->numComps); + return this->comps[componentIndex].Offset; } //------------------------------------------------------------------------------ -bool VertexLayout::Contains(VertexAttr::Code attr) const { - return InvalidIndex != this->ComponentIndexByVertexAttr(attr); +ImageContent::ImageContent() { + for (auto& ptr : this->Pointer) { + ptr.Fill(0); + } + for (auto& size : this->Size) { + size.Fill(0); + } } //------------------------------------------------------------------------------ -DisplayAttrs DisplayAttrs::FromTextureAttrs(const TextureAttrs& texAttrs) { - DisplayAttrs dispAttrs; - dispAttrs.WindowWidth = texAttrs.Width; - dispAttrs.WindowHeight = texAttrs.Height; - dispAttrs.WindowPosX = 0; - dispAttrs.WindowPosY = 0; - dispAttrs.FramebufferWidth = texAttrs.Width; - dispAttrs.FramebufferHeight = texAttrs.Height; - dispAttrs.ColorPixelFormat = texAttrs.ColorFormat; - dispAttrs.DepthPixelFormat = texAttrs.DepthFormat; - dispAttrs.SampleCount = texAttrs.SampleCount; - dispAttrs.Windowed = false; - dispAttrs.SwapInterval = 1; - return dispAttrs; +ImageContent& ImageContent::SetPointer(int faceIndex, int mipIndex, const void* ptr) { + this->Pointer[faceIndex][mipIndex] = ptr; + return *this; } //------------------------------------------------------------------------------ -ImageDataAttrs::ImageDataAttrs() { - for (auto& offsets : this->Offsets) { - offsets.Fill(0); - } - for (auto& sizes : this->Sizes) { - sizes.Fill(0); +ImageContent& ImageContent::SetSize(int faceIndex, int mipIndex, int size) { + this->Size[faceIndex][mipIndex] = size; + return *this; +} + +//------------------------------------------------------------------------------ +GfxDesc::GfxDesc() { + for (int i = 0; i < GfxResourceType::Num; i++) { + this->ResourcePoolSize[i] = GfxConfig::DefaultResourcePoolSize; } } //------------------------------------------------------------------------------ -int IndexBufferAttrs::ByteSize() const { - return NumIndices * IndexType::ByteSize(Type); +GfxDesc::GfxDesc(const GfxDesc& rhs) { + *this = rhs; } //------------------------------------------------------------------------------ -int VertexBufferAttrs::ByteSize() const { - return NumVertices * Layout.ByteSize(); +GfxDesc& GfxDesc::SetWidth(int w) { + this->Width = w; + return *this; } //------------------------------------------------------------------------------ -GfxSetup GfxSetup::Window(int width, int height, String windowTitle) { - o_assert_dbg((width > 0) && (height > 0)); - GfxSetup setup; - setup.Width = width; - setup.Height = height; - setup.Windowed = true; - setup.Title = windowTitle; - return setup; +GfxDesc& GfxDesc::SetHeight(int h) { + this->Height = h; + return *this; } //------------------------------------------------------------------------------ -GfxSetup GfxSetup::Fullscreen(int width, int height, String windowTitle) { - o_assert_dbg((width > 0) && (height > 0)); - GfxSetup setup; - setup.Width = width; - setup.Height = height; - setup.Windowed = false; - setup.Title = windowTitle; - return setup; +GfxDesc& GfxDesc::SetColorFormat(PixelFormat::Code fmt) { + this->ColorFormat = fmt; + return *this; } //------------------------------------------------------------------------------ -GfxSetup GfxSetup::WindowMSAA4(int width, int height, String windowTitle) { - GfxSetup setup = Window(width, height, windowTitle); - setup.SampleCount = 4; - return setup; +GfxDesc& GfxDesc::SetDepthFormat(PixelFormat::Code fmt) { + this->DepthFormat = fmt; + return *this; } //------------------------------------------------------------------------------ -GfxSetup GfxSetup::FullscreenMSAA4(int width, int height, String windowTitle) { - GfxSetup setup = Fullscreen(width, height, windowTitle); - setup.SampleCount = 4; - return setup; +GfxDesc& GfxDesc::SetSampleCount(int c) { + this->SampleCount = c; + return *this; } //------------------------------------------------------------------------------ -DisplayAttrs GfxSetup::GetDisplayAttrs() const { - DisplayAttrs attrs; - attrs.WindowWidth = this->Width; - attrs.WindowHeight = this->Height; - attrs.WindowPosX = 0; - attrs.WindowPosY = 0; - attrs.FramebufferWidth = this->Width; - attrs.FramebufferHeight = this->Height; - attrs.ColorPixelFormat = this->ColorFormat; - attrs.DepthPixelFormat = this->DepthFormat; - attrs.SampleCount = this->SampleCount; - attrs.Windowed = this->Windowed; - attrs.WindowTitle = this->Title; - attrs.SwapInterval = this->SwapInterval; - return attrs; +GfxDesc& GfxDesc::SetWindowed(bool b) { + this->Windowed = b; + return *this; } //------------------------------------------------------------------------------ -GfxSetup::GfxSetup() { - for (int i = 0; i < GfxResourceType::NumResourceTypes; i++) { - ResourcePoolSize[i] = GfxConfig::DefaultResourcePoolSize; - ResourceThrottling[i] = 0; // unthrottled - } +GfxDesc& GfxDesc::SetSwapInterval(int i) { + this->SwapInterval = i; + return *this; } //------------------------------------------------------------------------------ -MeshSetup MeshSetup::FromFile(const class Locator& loc, Id placeholder) { - MeshSetup setup; - setup.VertexUsage = Usage::Immutable; - setup.IndexUsage = Usage::Immutable; - setup.Locator = loc; - setup.Placeholder = placeholder; - setup.setupFromFile = true; - return setup; +GfxDesc& GfxDesc::SetTitle(const StringAtom& t) { + this->Title = t; + return *this; } //------------------------------------------------------------------------------ -MeshSetup MeshSetup::FromData(Usage::Code vertexUsage, Usage::Code indexUsage) { - MeshSetup setup; - setup.VertexUsage = vertexUsage; - setup.IndexUsage = indexUsage; - setup.setupFromData = true; - return setup; +GfxDesc& GfxDesc::SetHighDPI(bool b) { + this->HighDPI = b; + return *this; } //------------------------------------------------------------------------------ -MeshSetup MeshSetup::FromData(const MeshSetup& blueprint) { - MeshSetup setup(blueprint); - setup.setupFromData = true; - return setup; +GfxDesc& GfxDesc::SetHtmlTrackElementSize(bool b) { + this->HtmlTrackElementSize = b; + return *this; } //------------------------------------------------------------------------------ -MeshSetup MeshSetup::Empty(int numVertices, Usage::Code vertexUsage, IndexType::Code indexType, int numIndices, Usage::Code indexUsage) { - o_assert_dbg(numVertices > 0); - MeshSetup setup; - setup.setupEmpty = true; - setup.VertexUsage = vertexUsage; - setup.IndexUsage = indexUsage; - setup.NumVertices = numVertices; - setup.NumIndices = numIndices; - setup.IndicesType = indexType; - setup.VertexDataOffset = InvalidIndex; - setup.IndexDataOffset = InvalidIndex; - return setup; +GfxDesc& GfxDesc::SetHtmlElement(const StringAtom& e) { + this->HtmlElement = e; + return *this; } //------------------------------------------------------------------------------ -MeshSetup MeshSetup::FullScreenQuad(bool flipV) { - MeshSetup setup; - setup.setupFullScreenQuad = true; - setup.FullScreenQuadFlipV = flipV; - setup.Layout.Add(VertexAttr::Position, VertexFormat::Float3); - setup.Layout.Add(VertexAttr::TexCoord0, VertexFormat::Float2); - return setup; +GfxDesc& GfxDesc::SetResourcePoolSize(GfxResourceType::Code type, int size) { + this->ResourcePoolSize[type] = size; + return *this; } //------------------------------------------------------------------------------ -bool MeshSetup::ShouldSetupFromFile() const { - return this->setupFromFile; +GfxDesc& GfxDesc::SetResourceLabelStackCapacity(int c) { + this->ResourceLabelStackCapacity = c; + return *this; } //------------------------------------------------------------------------------ -bool MeshSetup::ShouldSetupFromData() const { - return this->setupFromData; +GfxDesc& GfxDesc::SetResourceRegistryCapacity(int c) { + this->ResourceRegistryCapacity = c; + return *this; } //------------------------------------------------------------------------------ -bool MeshSetup::ShouldSetupEmpty() const { - return this->setupEmpty; +GfxDesc& GfxDesc::SetGlobalUniformBufferSize(int s) { + this->GlobalUniformBufferSize = s; + return *this; } //------------------------------------------------------------------------------ -bool MeshSetup::ShouldSetupFullScreenQuad() const { - return this->setupFullScreenQuad; +BufferDesc::BufferDesc() { + this->NativeBuffers.Fill(0); } //------------------------------------------------------------------------------ -void MeshSetup::AddPrimitiveGroup(const class PrimitiveGroup& primGroup) { - o_assert(this->setupEmpty || this->setupFromData); - o_assert(this->numPrimGroups < GfxConfig::MaxNumPrimGroups); - this->primGroups[this->numPrimGroups++] = primGroup; +BufferDesc::BufferDesc(const BufferDesc& rhs) { + *this = rhs; } //------------------------------------------------------------------------------ -int MeshSetup::NumPrimitiveGroups() const { - return this->numPrimGroups; +BufferDesc& BufferDesc::SetLocator(const class Locator& l) { + this->Locator = l; + return *this; } //------------------------------------------------------------------------------ -const class PrimitiveGroup& MeshSetup::PrimitiveGroup(int index) const { - o_assert_range(index, GfxConfig::MaxNumPrimGroups); - return this->primGroups[index]; +BufferDesc& BufferDesc::SetType(BufferType::Code t) { + this->Type = t; + return *this; } //------------------------------------------------------------------------------ -PipelineSetup PipelineSetup::FromShader(const Id& shd) { - o_assert_dbg(shd.IsValid()); - PipelineSetup setup; - setup.Shader = shd; - return setup; +BufferDesc& BufferDesc::SetUsage(Usage::Code u) { + this->Usage = u; + return *this; } //------------------------------------------------------------------------------ -PipelineSetup PipelineSetup::FromLayoutAndShader(const VertexLayout& layout, const Id& shd) { - o_assert_dbg(!layout.Empty() && shd.IsValid()); - PipelineSetup setup; - setup.Layouts[0] = layout; - setup.Shader = shd; - return setup; +BufferDesc& BufferDesc::SetSize(int s) { + this->Size = s; return *this; } //------------------------------------------------------------------------------ -PassSetup PassSetup::From(Id colorTexture, Id depthStencilTexture) { - PassSetup setup; - setup.ColorAttachments[0].Texture = colorTexture; - setup.DepthStencilTexture = depthStencilTexture; - return setup; +BufferDesc& BufferDesc::SetContent(const void* c) { + this->Content = c; + return *this; } //------------------------------------------------------------------------------ -PassSetup PassSetup::From(std::initializer_list colorTextures, Id depthStencilTexture) { - PassSetup setup; - int i = 0; - for (const auto& id : colorTextures) { - setup.ColorAttachments[i++].Texture = id; +BufferDesc& BufferDesc::SetNativeBuffer(int index, intptr_t buf) { + this->NativeBuffers[index] = buf; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc::PipelineDesc(const PipelineDesc& rhs) { + *this = rhs; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetLocator(const class Locator& loc) { + this->Locator = loc; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetShader(const Id& shd) { + this->Shader = shd; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetLayout(int slotIndex, const VertexLayout& layout) { + this->Layouts[slotIndex] = layout; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetPrimitiveType(PrimitiveType::Code t) { + this->PrimType = t; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetIndexType(IndexType::Code t) { + this->IndexType = t; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetDepthCmpFunc(CompareFunc::Code f) { + this->DepthCmpFunc = f; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetDepthWriteEnabled(bool b) { + this->DepthWriteEnabled = b; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilEnabled(bool b) { + this->StencilEnabled = b; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilReadMask(uint8_t m) { + this->StencilReadMask = m; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilWriteMask(uint8_t m) { + this->StencilWriteMask = m; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilRef(uint8_t r) { + this->StencilRef = r; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilFailOp(Face::Code face, StencilOp::Code op) { + if (Face::Front & face) { + this->StencilFrontFailOp = op; + } + if (Face::Back & face) { + this->StencilBackFailOp = op; } - setup.DepthStencilTexture = depthStencilTexture; - return setup; + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::SetProgramFromSources(ShaderLang::Code slang, const String& vsSource, const String& fsSource) { - o_assert_dbg(vsSource.IsValid() && fsSource.IsValid()); - this->program.vsSources[slang] = vsSource; - this->program.fsSources[slang] = fsSource; +PipelineDesc& PipelineDesc::SetStencilDepthFailOp(Face::Code face, StencilOp::Code op) { + if (Face::Front & face) { + this->StencilFrontDepthFailOp = op; + } + if (Face::Back & face) { + this->StencilBackDepthFailOp = op; + } + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::SetProgramFromByteCode(ShaderLang::Code slang, const uint8_t* vsByteCode, uint32_t vsNumBytes, const uint8_t* fsByteCode, uint32_t fsNumBytes, const char* vsFunc, const char* fsFunc) { - o_assert_dbg(vsByteCode && (vsNumBytes > 0)); - o_assert_dbg(fsByteCode && (fsNumBytes > 0)); - this->program.vsByteCode[slang].ptr = vsByteCode; - this->program.vsByteCode[slang].size = vsNumBytes; - this->program.fsByteCode[slang].ptr = fsByteCode; - this->program.fsByteCode[slang].size = fsNumBytes; - if (vsFunc) { - this->program.vsFuncs[slang] = vsFunc; +PipelineDesc& PipelineDesc::SetStencilPassOp(Face::Code face, StencilOp::Code op) { + if (Face::Front & face) { + this->StencilFrontPassOp = op; } - else { - this->program.vsFuncs[slang].Clear(); + if (Face::Back & face) { + this->StencilBackPassOp = op; } - if (fsFunc) { - this->program.fsFuncs[slang] = fsFunc; + return *this; +} + +//------------------------------------------------------------------------------ +PipelineDesc& PipelineDesc::SetStencilCmpFunc(Face::Code face, CompareFunc::Code fn) { + if (Face::Front & face) { + this->StencilFrontCmpFunc = fn; } - else { - this->program.fsFuncs[slang].Clear(); + if (Face::Back & face) { + this->StencilBackCmpFunc = fn; } + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::SetInputLayout(const VertexLayout& vsInputLayout) { - this->program.vsInputLayout = vsInputLayout; +PipelineDesc& PipelineDesc::SetBlendEnabled(bool b) { + this->BlendEnabled = b; + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::AddUniformBlock(const StringAtom& type, const StringAtom& name, uint32_t typeHash, uint32_t byteSize, ShaderStage::Code bindStage, int32_t bindSlot) { - o_assert_dbg(type.IsValid()); - o_assert_dbg(bindSlot >= 0); - uniformBlockEntry& entry = this->uniformBlocks[this->numUniformBlocks++]; - entry.type = type; - entry.name = name; - entry.typeHash = typeHash; - entry.byteSize = byteSize; - entry.bindStage = bindStage; - entry.bindSlot = bindSlot; +PipelineDesc& PipelineDesc::SetBlendSrcFactor(BlendFactor::Code f) { + this->BlendSrcFactorRGB = f; + this->BlendSrcFactorAlpha = f; + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::AddTexture(const StringAtom& name, TextureType::Code type, ShaderStage::Code bindStage, int32_t bindSlot) { - o_assert_dbg(name.IsValid()); - o_assert_dbg(bindSlot >= 0); - textureEntry& entry = this->textures[this->numTextures++]; - entry.name = name; - entry.type = type; - entry.bindStage = bindStage; - entry.bindSlot = bindSlot; +PipelineDesc& PipelineDesc::SetBlendSrcFactorRGB(BlendFactor::Code f) { + this->BlendSrcFactorRGB = f; + return *this; } //------------------------------------------------------------------------------ -const VertexLayout& ShaderSetup::InputLayout() const { - return this->program.vsInputLayout; +PipelineDesc& PipelineDesc::SetBlendSrcFactorAlpha(BlendFactor::Code f) { + this->BlendSrcFactorAlpha = f; + return *this; } //------------------------------------------------------------------------------ -const String& ShaderSetup::VertexShaderSource(ShaderLang::Code slang) const { - return this->program.vsSources[slang]; +PipelineDesc& PipelineDesc::SetBlendDstFactor(BlendFactor::Code f) { + this->BlendDstFactorRGB = f; + this->BlendDstFactorAlpha = f; + return *this; } //------------------------------------------------------------------------------ -const String& ShaderSetup::FragmentShaderSource(ShaderLang::Code slang) const { - return this->program.fsSources[slang]; +PipelineDesc& PipelineDesc::SetBlendDstFactorRGB(BlendFactor::Code f) { + this->BlendDstFactorRGB = f; + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::VertexShaderByteCode(ShaderLang::Code slang, const void*& outPtr, uint32_t& outSize) const { - outPtr = this->program.vsByteCode[slang].ptr; - outSize = this->program.vsByteCode[slang].size; +PipelineDesc& PipelineDesc::SetBlendDstFactorAlpha(BlendFactor::Code f) { + this->BlendDstFactorAlpha = f; + return *this; } //------------------------------------------------------------------------------ -void ShaderSetup::FragmentShaderByteCode(ShaderLang::Code slang, const void*& outPtr, uint32_t& outSize) const { - outPtr = this->program.fsByteCode[slang].ptr; - outSize = this->program.fsByteCode[slang].size; +PipelineDesc& PipelineDesc::SetBlendOp(BlendOperation::Code op) { + this->BlendOpRGB = op; + this->BlendOpAlpha = op; + return *this; } //------------------------------------------------------------------------------ -const StringAtom& ShaderSetup::VertexShaderFunc(ShaderLang::Code slang) const { - o_assert_dbg(ShaderLang::Metal == slang); - return this->program.vsFuncs[slang]; +PipelineDesc& PipelineDesc::SetBlendOpRGB(BlendOperation::Code op) { + this->BlendOpRGB = op; + return *this; } //------------------------------------------------------------------------------ -const StringAtom& ShaderSetup::FragmentShaderFunc(ShaderLang::Code slang) const { - o_assert_dbg(ShaderLang::Metal == slang); - return this->program.fsFuncs[slang]; +PipelineDesc& PipelineDesc::SetBlendOpAlpha(BlendOperation::Code op) { + this->BlendOpAlpha = op; + return *this; } //------------------------------------------------------------------------------ -int ShaderSetup::NumUniformBlocks() const { - return this->numUniformBlocks; +PipelineDesc& PipelineDesc::SetColorWriteMask(PixelChannel::Mask m) { + this->ColorWriteMask = m; + return *this; } //------------------------------------------------------------------------------ -int ShaderSetup::UniformBlockIndexByStageAndSlot(ShaderStage::Code bindStage, int bindSlot) const { - for (int i = 0; i < this->numUniformBlocks; i++) { - const auto& entry = this->uniformBlocks[i]; - if ((entry.bindStage == bindStage) && (entry.bindSlot == bindSlot)) { - return i; - } - } - return InvalidIndex; +PipelineDesc& PipelineDesc::SetColorFormat(PixelFormat::Code fmt) { + this->ColorFormat = fmt; + return *this; } //------------------------------------------------------------------------------ -const StringAtom& ShaderSetup::UniformBlockName(int index) const { - return this->uniformBlocks[index].name; +PipelineDesc& PipelineDesc::SetDepthFormat(PixelFormat::Code fmt) { + this->DepthFormat = fmt; + return *this; } //------------------------------------------------------------------------------ -const StringAtom& ShaderSetup::UniformBlockType(int index) const { - return this->uniformBlocks[index].type; +PipelineDesc& PipelineDesc::SetSampleCount(int c) { + this->SampleCount = c; + return *this; } //------------------------------------------------------------------------------ -uint32_t ShaderSetup::UniformBlockTypeHash(int index) const { - return this->uniformBlocks[index].typeHash; +PipelineDesc& PipelineDesc::SetMRTCount(int c) { + this->MRTCount = c; + return *this; } //------------------------------------------------------------------------------ -uint32_t ShaderSetup::UniformBlockByteSize(int index) const { - return this->uniformBlocks[index].byteSize; +PipelineDesc& PipelineDesc::SetBlendColor(const glm::vec4& c) { + this->BlendColor = c; + return *this; } //------------------------------------------------------------------------------ -ShaderStage::Code ShaderSetup::UniformBlockBindStage(int index) const { - return this->uniformBlocks[index].bindStage; +PipelineDesc& PipelineDesc::SetCullFaceEnabled(bool b) { + this->CullFaceEnabled = b; + return *this; } //------------------------------------------------------------------------------ -int ShaderSetup::UniformBlockBindSlot(int index) const { - return this->uniformBlocks[index].bindSlot; +PipelineDesc& PipelineDesc::SetCullFace(Face::Code f) { + this->CullFace = f; + return *this; } //------------------------------------------------------------------------------ -int ShaderSetup::NumTextures() const { - return this->numTextures; +PipelineDesc& PipelineDesc::SetAlphaToCoverageEnabled(bool b) { + this->AlphaToCoverageEnabled = b; + return *this; } //------------------------------------------------------------------------------ -int ShaderSetup::TextureIndexByStageAndSlot(ShaderStage::Code bindStage, int bindSlot) const { - for (int i = 0; i < this->numTextures; i++) { - const auto& entry = this->textures[i]; - if ((entry.bindStage == bindStage) && (entry.bindSlot == bindSlot)) { - return i; - } - } - return InvalidIndex; +PipelineDesc& PipelineDesc::SetDepthBias(float f) { + this->DepthBias = f; + return *this; } //------------------------------------------------------------------------------ -const StringAtom& ShaderSetup::TexName(int index) const { - return this->textures[index].name; +PipelineDesc& PipelineDesc::SetDepthBiasSlopeScale(float f) { + this->DepthBiasSlopeScale = f; + return *this; } //------------------------------------------------------------------------------ -TextureType::Code ShaderSetup::TexType(int index) const { - return this->textures[index].type; +PipelineDesc& PipelineDesc::SetDepthBiasClamp(float f) { + this->DepthBiasClamp = f; + return *this; } //------------------------------------------------------------------------------ -ShaderStage::Code ShaderSetup::TexBindStage(int index) const { - return this->textures[index].bindStage; +ShaderDesc::ShaderDesc(const ShaderDesc& rhs) { + *this = rhs; } //------------------------------------------------------------------------------ -int ShaderSetup::TexBindSlot(int index) const { - return this->textures[index].bindSlot; +ShaderDesc& ShaderDesc::SetLocator(const class Locator& loc) { + this->Locator = loc; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromFile(const class Locator& loc, Id placeholder) { - TextureSetup setup; - setup.setupFromFile = true; - setup.Locator = loc; - setup.Placeholder = placeholder; - return setup; +ShaderDesc& ShaderDesc::SetSource(ShaderStage::Code stg, const char* src) { + this->Stage[stg].Source = src; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromFile(const class Locator& loc, const TextureSetup& blueprint, Id placeholder) { - TextureSetup setup(blueprint); - setup.setupFromFile = true; - setup.Locator = loc; - setup.Placeholder = placeholder; - return setup; +ShaderDesc& ShaderDesc::SetByteCode(ShaderStage::Code stg, const uint8_t* ptr, int size) { + this->Stage[stg].ByteCode = ptr; + this->Stage[stg].ByteCodeSize = size; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromPixelData2D(int w, int h, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupFromPixelData = true; - setup.Type = TextureType::Texture2D; - setup.Width = w; - setup.Height = h; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.ImageData.NumFaces = 1; - setup.ImageData.NumMipMaps = numMipMaps; - return setup; +ShaderDesc& ShaderDesc::SetEntry(ShaderStage::Code stg, const char* entry) { + this->Stage[stg].Entry = entry; return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromPixelDataCube(int w, int h, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupFromPixelData = true; - setup.Type = TextureType::TextureCube; - setup.Width = w; - setup.Height = h; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.ImageData.NumFaces = 6; - setup.ImageData.NumMipMaps = numMipMaps; - return setup; -} - +ShaderDesc& ShaderDesc::SetAttr(const StringAtom& name, VertexFormat::Code fmt) { + this->Layout.Add(name, fmt); + return *this; +} + +//------------------------------------------------------------------------------ +ShaderDesc& ShaderDesc::SetUniformBlock(ShaderStage::Code stg, int slot, const char* name, const char* type, int size) { + auto& ubSlot = this->Stage[stg].UniformBlocks[slot]; + ubSlot.Name = name; + ubSlot.Type = type; + ubSlot.Size = size; + return *this; +} + +//------------------------------------------------------------------------------ +ShaderDesc& ShaderDesc::SetTexture(ShaderStage::Code stg, int slot, const char* name, TextureType::Code type) { + auto& texSlot = this->Stage[stg].Textures[slot]; + texSlot.Name = name; + texSlot.Type = type; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc::TextureDesc() { + this->NativeTextures.Fill(0); +} + +//------------------------------------------------------------------------------ +TextureDesc::TextureDesc(const TextureDesc& rhs) { + *this = rhs; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetLocator(const class Locator& loc) { + this->Locator = loc; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetType(TextureType::Code t) { + this->Type = t; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetRenderTarget(bool b) { + this->RenderTarget = b; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetWidth(int w) { + this->Width = w; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetHeight(int h) { + this->Height = h; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetDepth(int d) { + this->Depth = d; + return *this; +} + //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromPixelData3D(int w, int h, int d, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0) && (d > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupFromPixelData = true; - setup.Type = TextureType::Texture3D; - setup.Width = w; - setup.Height = h; - setup.Depth = d; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.ImageData.NumFaces = 1; - setup.ImageData.NumMipMaps = numMipMaps; - return setup; +TextureDesc& TextureDesc::SetLayers(int l) { + // not a bug + this->Depth = l; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromPixelDataArray(int w, int h, int layers, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0) && (layers > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupFromPixelData = true; - setup.Type = TextureType::TextureArray; - setup.Width = w; - setup.Height = h; - setup.Depth = layers; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.ImageData.NumFaces = 1; - setup.ImageData.NumMipMaps = numMipMaps; - return setup; -} - -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::Empty2D(int w, int h, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupEmpty = true; - setup.Type = TextureType::Texture2D; - setup.Width = w; - setup.Height = h; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.TextureUsage = usage; - return setup; -} - -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::EmptyCube(int w, int h, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupEmpty = true; - setup.Type = TextureType::TextureCube; - setup.Width = w; - setup.Height = h; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.TextureUsage = usage; - return setup; -} - -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::Empty3D(int w, int h, int d, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0) && (d > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupEmpty = true; - setup.Type = TextureType::Texture3D; - setup.Width = w; - setup.Height = h; - setup.Depth = d; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.TextureUsage = usage; - return setup; -} - -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::EmptyArray(int w, int h, int layers, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint) { - o_assert_dbg((w > 0) && (h > 0) && (layers > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert_dbg((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - TextureSetup setup(blueprint); - setup.setupEmpty = true; - setup.Type = TextureType::TextureArray; - setup.Width = w; - setup.Height = h; - setup.Depth = layers; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.TextureUsage = usage; - return setup; -} - -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::RenderTarget2D(int w, int h, PixelFormat::Code colorFmt, PixelFormat::Code depthFmt) { - o_assert_dbg((w > 0) && (h > 0)); - TextureSetup setup; - setup.Type = TextureType::Texture2D; - setup.IsRenderTarget = true; - setup.Width = w; - setup.Height = h; - setup.ColorFormat = colorFmt; - setup.DepthFormat = depthFmt; - setup.Sampler.WrapU = TextureWrapMode::ClampToEdge; - setup.Sampler.WrapV = TextureWrapMode::ClampToEdge; - return setup; -} +TextureDesc& TextureDesc::SetNumMipMaps(int n) { + this->NumMipMaps = n; + return *this; +} -//------------------------------------------------------------------------------ -TextureSetup TextureSetup::RenderTargetCube(int w, int h, PixelFormat::Code colorFmt, PixelFormat::Code depthFmt) { - o_assert_dbg((w > 0) && (h > 0)); - TextureSetup setup; - setup.Type = TextureType::TextureCube; - setup.IsRenderTarget = true; - setup.Width = w; - setup.Height = h; - setup.ColorFormat = colorFmt; - setup.DepthFormat = depthFmt; - setup.Sampler.WrapU = TextureWrapMode::ClampToEdge; - setup.Sampler.WrapV = TextureWrapMode::ClampToEdge; - return setup; +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetUsage(Usage::Code u) { + this->Usage = u; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::RenderTarget3D(int w, int h, int d, PixelFormat::Code colorFmt, PixelFormat::Code depthFmt) { - o_assert_dbg((w > 0) && (h > 0)); - TextureSetup setup; - setup.Type = TextureType::Texture3D; - setup.IsRenderTarget = true; - setup.Width = w; - setup.Height = h; - setup.Depth = d; - setup.ColorFormat = colorFmt; - setup.DepthFormat = depthFmt; - setup.Sampler.WrapU = TextureWrapMode::ClampToEdge; - setup.Sampler.WrapV = TextureWrapMode::ClampToEdge; - return setup; +TextureDesc& TextureDesc::SetFormat(PixelFormat::Code fmt) { + this->Format = fmt; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::RenderTargetArray(int w, int h, int layers, PixelFormat::Code colorFmt, PixelFormat::Code depthFmt) { - o_assert_dbg((w > 0) && (h > 0)); - TextureSetup setup; - setup.Type = TextureType::TextureArray; - setup.IsRenderTarget = true; - setup.Width = w; - setup.Height = h; - setup.Depth = layers; - setup.ColorFormat = colorFmt; - setup.DepthFormat = depthFmt; - setup.Sampler.WrapU = TextureWrapMode::ClampToEdge; - setup.Sampler.WrapV = TextureWrapMode::ClampToEdge; - return setup; +TextureDesc& TextureDesc::SetSampleCount(int c) { + this->SampleCount = c; + return *this; } //------------------------------------------------------------------------------ -TextureSetup TextureSetup::FromNativeTexture(int w, int h, int numMipMaps, TextureType::Code type, PixelFormat::Code fmt, Usage::Code usage, intptr_t h0, intptr_t h1) { - o_assert_dbg((w > 0) && (h > 0)); - o_assert_dbg(PixelFormat::IsValidTextureColorFormat(fmt)); - o_assert((numMipMaps > 0) && (numMipMaps < GfxConfig::MaxNumTextureMipMaps)); - o_assert_dbg(h0 != 0); - TextureSetup setup; - setup.setupFromNativeHandle = true; - setup.Type = type; - setup.Width = w; - setup.Height = h; - setup.NumMipMaps = numMipMaps; - setup.ColorFormat = fmt; - setup.TextureUsage = usage; - setup.NativeHandle[0] = h0; - setup.NativeHandle[1] = h1; - return setup; +TextureDesc& TextureDesc::SetMagFilter(TextureFilterMode::Code f) { + this->MagFilter = f; + return *this; } //------------------------------------------------------------------------------ -bool TextureSetup::ShouldSetupFromFile() const { - return this->setupFromFile; +TextureDesc& TextureDesc::SetMinFilter(TextureFilterMode::Code f) { + this->MinFilter = f; + return *this; } //------------------------------------------------------------------------------ -bool TextureSetup::ShouldSetupFromPixelData() const { - return this->setupFromPixelData; +TextureDesc& TextureDesc::SetWrapU(TextureWrapMode::Code m) { + this->WrapU = m; + return *this; } //------------------------------------------------------------------------------ -bool TextureSetup::ShouldSetupFromNativeTexture() const { - return this->setupFromNativeHandle; +TextureDesc& TextureDesc::SetWrapV(TextureWrapMode::Code m) { + this->WrapV = m; + return *this; } //------------------------------------------------------------------------------ -bool TextureSetup::ShouldSetupEmpty() const { - return this->setupEmpty; +TextureDesc& TextureDesc::SetWrapW(TextureWrapMode::Code m) { + this->WrapW = m; + return *this; } //------------------------------------------------------------------------------ -bool TextureSetup::HasDepth() const { - return this->DepthFormat != PixelFormat::InvalidPixelFormat; +TextureDesc& TextureDesc::SetNativeTexture(int index, intptr_t tex) { + this->NativeTextures[index] = tex; + return *this; } //------------------------------------------------------------------------------ -TextureSetup::TextureSetup() { - NativeHandle.Fill(0); +TextureDesc& TextureDesc::SetMipSize(int faceIndex, int mipIndex, int size) { + this->Content.Size[faceIndex][mipIndex] = size; + return *this; +} + +//------------------------------------------------------------------------------ +TextureDesc& TextureDesc::SetMipContent(int faceIndex, int mipIndex, const void* ptr) { + this->Content.Pointer[faceIndex][mipIndex] = ptr; + return *this; +} + +//------------------------------------------------------------------------------ +PassDesc::PassDesc(const PassDesc& rhs) { + *this = rhs; +} + +//------------------------------------------------------------------------------ +PassDesc& PassDesc::SetLocator(const class Locator& loc) { + this->Locator = loc; + return *this; +} + +//------------------------------------------------------------------------------ +PassDesc& PassDesc::SetColorAttachment(int slotIndex, const Id& tex, int mipLevel, int faceLayerSlice) { + auto& att = this->ColorAttachments[slotIndex]; + att.Texture = tex; + att.MipLevel = mipLevel; + att.Face = faceLayerSlice; + return *this; +} + +//------------------------------------------------------------------------------ +PassDesc& PassDesc::SetDepthStencilAttachment(const Id& tex, int mipLevel, int faceLayerSlice) { + auto& att = this->DepthStencilAttachment; + att.Texture = tex; + att.MipLevel = mipLevel; + att.Face = faceLayerSlice; + return *this; } } // namespace Oryol diff --git a/code/Modules/Gfx/GfxTypes.h b/code/Modules/Gfx/GfxTypes.h index ac09ce69c..8ba7cbe9f 100644 --- a/code/Modules/Gfx/GfxTypes.h +++ b/code/Modules/Gfx/GfxTypes.h @@ -2,32 +2,151 @@ //------------------------------------------------------------------------------ #include "Core/Types.h" #include "Core/Assertion.h" -#include "Core/String/String.h" #include "Core/String/StringAtom.h" #include "Resource/Id.h" #include "Resource/Locator.h" #include "Core/Containers/StaticArray.h" -#include "Gfx/GfxConfig.h" +#include "Core/Containers/MemoryBuffer.h" #include "glm/vec4.hpp" #include +#include namespace Oryol { +//------------------------------------------------------------------------------ +/** + @class Oryol::GfxConfig + @ingroup Gfx + @brief central configuration constants of the Gfx module +*/ +namespace GfxConfig { + /// default resource pool size + constexpr int DefaultResourcePoolSize = 128; + /// default uniform buffer size (only relevant on some platforms) + constexpr int DefaultGlobalUniformBufferSize = 4 * 1024 * 1024; + /// max number of input vertex buffers + constexpr int MaxNumVertexBuffers = 4; + /// maximum number of textures on vertex shader stage + constexpr int MaxNumVertexTextures = 4; + /// maximum number of textures on fragment shader stage + constexpr int MaxNumFragmentTextures = 12; + /// max number of uniform blocks per stage + constexpr int MaxNumUniformBlocksPerStage = 4; + /// max number of textures on any stage + constexpr int MaxNumShaderTextures = MaxNumVertexTextures>MaxNumFragmentTextures?MaxNumVertexTextures:MaxNumFragmentTextures; + /// max number of texture faces + constexpr int MaxNumTextureFaces = 6; + /// max number of texture mipmaps + constexpr int MaxNumTextureMipMaps = 12; + /// maximum number of components in vertex layout + constexpr int MaxNumVertexLayoutComponents = 16; + /// maximum number of in-flight frames (not used by all platforms) + constexpr int MaxInflightFrames = 2; + /// maximum number of render pass color attachments + constexpr int MaxNumColorAttachments = 4; +}; + +//------------------------------------------------------------------------------ +/** + @class Oryol::GfxFeature + @ingroup Gfx + @brief optional rendering features +*/ +struct GfxFeature { + enum Code { + TextureCompressionDXT = 0, ///< GPU supports DXT compressed textures + TextureCompressionPVRTC, ///< GPU supports PVRTC compressed textures + TextureCompressionATC, ///< GPU supports ATC compressed textures + TextureCompressionETC2, ///< GPU supports ETC2 compressed textures (OpenGLES3) + TextureFloat, ///< support for float textures + TextureHalfFloat, ///< support for half-float textures + Instancing, ///< supports hardware-instanced rendering + OriginBottomLeft, ///< image space origin is bottom-left (GL-style) + OriginTopLeft, ///< image space origin is top-left (D3D-style) + MSAARenderTargets, ///< MSAA support in offscreen-render-targets + PackedVertexFormat_10_2, ///< support for 10.10.10.2 bit packed vertex formats + MultipleRenderTarget, ///< support for MRT offscreen rendering + Texture3D, ///< support for 3D textures + TextureArray, ///< support for array textures + + Num, + Invalid + }; +}; + +//------------------------------------------------------------------------------ +/** + @class Oryol::GfxResourceType + @ingroup Gfx + @brief Gfx module resource types + + These types are used for the type in Id for Gfx module + resources. +*/ +struct GfxResourceType { + enum Code { + Texture, ///< a texture + Buffer, ///< a vertex- or index-buffer + Shader, ///< a shader + Pipeline, ///< a pipeline state object + Pass, ///< a render-pass object + + Num, + Invalid, + }; +}; + +//------------------------------------------------------------------------------ +/** + @class Oryol::Usage + @ingroup Gfx + @brief graphics resource usage types + + - Immutable: requires initialization data + - Dynamic: update infrequently + - Stream: changed every frame +*/ +struct Usage { + enum Code { + Immutable = 0, + Dynamic, + Stream, + + Num, + Invalid, + }; +}; + +//------------------------------------------------------------------------------ +/** + @class Oryol::BufferType + @ingroup Gfx + @brief whether a Buffer contains vertex- or index-data +*/ +struct BufferType { + enum Code { + VertexBuffer = 0, + IndexBuffer, + + Num, + Invalid + }; +}; + //------------------------------------------------------------------------------ /** @class Oryol::IndexType @ingroup Gfx @brief selects 16- or 32-bit indices */ -class IndexType { -public: +struct IndexType { enum Code { None = 0, - Index16, - Index32, + UInt16, + UInt32, - NumIndexTypes, - InvalidIndexType = 0xFFFFFFFF + Num, + Invalid }; /// get byte size of index type static int ByteSize(IndexType::Code c); @@ -39,18 +158,17 @@ class IndexType { @ingroup Gfx @brief RGBA/Depth/Stencil channel bits and combinations */ -class PixelChannel { -public: - typedef uint64_t Mask; +struct PixelChannel { + typedef uint8_t Mask; enum Bits { None = 0, - Stencil = (1<<5), - Depth = (1<<4), - Red = (1<<3), - Green = (1<<2), - Blue = (1<<1), - Alpha = (1<<0), + Red = (1<<0), + Green = (1<<1), + Blue = (1<<2), + Alpha = (1<<3), + Stencil = (1<<4), + Depth = (1<<5), DepthStencil = Depth|Stencil, DS = DepthStencil, @@ -86,13 +204,8 @@ class PixelChannel { @ingroup Gfx @brief enum of pixel formats */ -class PixelFormat { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint64_t { - #else +struct PixelFormat { enum Code { - #endif RGBA8, ///< 32-bit wide, 4 channels @ 8-bit RGB8, ///< 24-bit wide, 3 channels @ 8-bit RGBA4, ///< 16-bit wide, 4 channels @ 4-bit @@ -117,9 +230,9 @@ class PixelFormat { ETC2_RGB8, ///< ETC2 compressed format (RGB8) ETC2_SRGB8, ///< ETC2 compressed format (SRGB8) - NumPixelFormats, ///< number of pixel formats - InvalidPixelFormat, ///< invalid pixel format value - None = InvalidPixelFormat, ///< special "none" type + Num, ///< number of pixel formats + Invalid, ///< invalid pixel format value + None = Invalid, ///< special "none" type }; /// return true for valid render target color formats @@ -160,8 +273,7 @@ class PixelFormat { @ingroup Gfx @brief primitive type enum (triangle strips, lists, etc...) */ -class PrimitiveType { -public: +struct PrimitiveType { /// primitive type enum (don't change order, append to end!) enum Code { Points = 0, @@ -170,52 +282,42 @@ class PrimitiveType { Triangles, TriangleStrip, - NumPrimitiveTypes, - InvalidPrimitiveType = 0xFFFFFFFF, + Num, + Invalid }; - /// convert primitive type to string - static const char* ToString(PrimitiveType::Code c); }; //------------------------------------------------------------------------------ /** - @class Oryol::GfxResourceType + @class Oryol::ShaderStage @ingroup Gfx - @brief Gfx module resource types - - These types are used for the type in Id for Gfx module - resources. + @brief the shader stages (vertex shader, fragment shader) */ -class GfxResourceType { -public: - /// type enum +struct ShaderStage { enum Code { - Texture, ///< a texture - Mesh, ///< a mesh - Shader, ///< a shader - Pipeline, ///< a pipeline state object - RenderPass, ///< a render-pass object + VS = 0, + FS, - NumResourceTypes, - InvalidResourceType = 0xFFFF, + Num, + Invalid }; }; //------------------------------------------------------------------------------ /** - @class Oryol::ShaderStage + @class Oryol::TextureType @ingroup Gfx - @brief the shader stages (vertex shader, fragment shader) + @brief texture type (2D, 3D, Cube) */ -class ShaderStage { -public: - /// shader stages enum +struct TextureType { enum Code { - VS = 0, - FS, + Texture2D = 0, + TextureCube, + Texture3D, + TextureArray, - NumShaderStages, - InvalidShaderStage = 0xFFFFFFFF, + Num, + Invalid, }; }; @@ -225,40 +327,17 @@ class ShaderStage { @ingroup Gfx @brief texture sampling filter mode */ -class TextureFilterMode { -public: - /// filtering modes - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint16_t { - #else +struct TextureFilterMode { enum Code { - #endif - Nearest, + Nearest = 0, Linear, NearestMipmapNearest, NearestMipmapLinear, LinearMipmapNearest, LinearMipmapLinear, - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::TextureType - @ingroup Gfx - @brief texture type (2D, 3D, Cube) -*/ -class TextureType { -public: - /// texture type enum - enum Code { - Texture2D = 0, - TextureCube, - Texture3D, - TextureArray, - NumTextureTypes, - InvalidTextureType = 0xFFFFFFFF, + Num, + Invalid, }; }; @@ -268,84 +347,15 @@ class TextureType { @ingroup Gfx @brief texture coordinate wrapping modes */ -class TextureWrapMode { -public: - /// wrap modes - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint16_t { - #else +struct TextureWrapMode { enum Code { - #endif ClampToEdge, Repeat, MirroredRepeat, - }; -}; -//------------------------------------------------------------------------------ -/** - @class Oryol::Usage - @ingroup Gfx - @brief graphics resource usage types - - - Immutable: requires initialization data - - Dynamic: update infrequently - - Stream: changed every frame -*/ -class Usage { -public: - /// usage enum - enum Code { - Immutable = 0, - Dynamic, - Stream, - - NumUsages, - InvalidUsage = 0xFFFFFFFF, - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::VertexAttr - @ingroup Gfx - @brief vertex attribute enum (position, texcoord, ...) - - The VertexAttr definitions don't have a hardwired meaning, they just - exist to make the binding of vertex components (living in vertex buffers) - to vertex attribute definition in vertex shaders easier to understand. - The maximum number of vertex attributes should not exceed 16 - (this is the GL_MAX_VERTEX_ATTRIBS value). -*/ -class VertexAttr { -public: - /// vertex attribute enum - enum Code : uint8_t { - Position = 0, ///< "position" - Normal, ///< "normal" - TexCoord0, ///< "texcoord0" - TexCoord1, ///< "texcoord1" - TexCoord2, ///< "texcoord2" - TexCoord3, ///< "texcoord3" - Tangent, ///< "tangent - Binormal, ///< "binormal" - Weights, ///< "weights" (skin weights) - Indices, ///< "indices" (skin indices) - Color0, ///< "color0" - Color1, ///< "color1" - Instance0, ///< "instance0" - Instance1, ///< "instance1" - Instance2, ///< "instance2" - Instance3, ///< "instance3" - - NumVertexAttrs, - InvalidVertexAttr, + Num, + Invalid, }; - - /// convert to string - static const char* ToString(Code c); - /// convert from string - static Code FromString(const char* str); }; //------------------------------------------------------------------------------ @@ -359,10 +369,9 @@ class VertexAttr { GLES2 and D3D11! GLES2 needs to read those as float vec, but D3D11 can only read them as int vec! */ -class VertexFormat { -public: +struct VertexFormat { /// format enum (don't change order, and append to end!) - enum Code : uint8_t { + enum Code { Float, ///< single component float, expanded to (x, 0, 0, 1) Float2, ///< 2-component float, expanded to (x, y, 0, 1) Float3, ///< 3-component float, expanded to (x, y, z, 1) @@ -377,8 +386,8 @@ class VertexFormat { Short4N, ///< 4-component float (-1.0f..+1.0f) mapped to short (-32768..+32767) UInt10_2N, ///< 4-component packed, normalized 10-bit XYZ, 2-bit W (0.0 .. 1.0) - NumVertexFormats, ///< number of vertex formats - InvalidVertexFormat, ///< the invalid vertex format value + Num, ///< number of vertex formats + Invalid, ///< the invalid vertex format value }; /// get the byte size of a vertex format code @@ -393,8 +402,7 @@ class VertexFormat { @ingroup Gfx @brief shader language syntax */ -class ShaderLang { -public: +struct ShaderLang { enum Code { GLSL100 = 0, ///< OpenGLES 2.0 / WebGL 1.0 GLSL330, ///< OpenGL 3.3 @@ -402,38 +410,8 @@ class ShaderLang { HLSL5, ///< D3D11 HLSL Metal, ///< Metal shader language - NumShaderLangs, - InvalidShaderLang - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::GfxFeature - @ingroup Gfx - @brief optional rendering features -*/ -class GfxFeature { -public: - enum Code { - TextureCompressionDXT = 0, ///< GPU supports DXT compressed textures - TextureCompressionPVRTC, ///< GPU supports PVRTC compressed textures - TextureCompressionATC, ///< GPU supports ATC compressed textures - TextureCompressionETC2, ///< GPU supports ETC2 compressed textures (OpenGLES3) - TextureFloat, ///< support for float textures - TextureHalfFloat, ///< support for half-float textures - Instancing, ///< supports hardware-instanced rendering - OriginBottomLeft, ///< image space origin is bottom-left (GL-style) - OriginTopLeft, ///< image space origin is top-left (D3D-style) - MSAARenderTargets, ///< MSAA support in offscreen-render-targets - PackedVertexFormat_10_2, ///< support for 10.10.10.2 bit packed vertex formats - MultipleRenderTarget, ///< support for MRT offscreen rendering - Texture3D, ///< support for 3D textures - TextureArray, ///< support for array textures - NativeTexture, ///< can work with externally created texture objects - - NumFeatures, - InvalidFeature + Num, + Invalid }; }; @@ -443,20 +421,13 @@ class GfxFeature { @ingroup Gfx @brief polygon face side (front, back, both) */ -class Face { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint16_t { - #else +struct Face { enum Code { - #endif - Front = 0, - Back, - Both, + Front = (1<<0), + Back = (1<<1), + Both = Front|Back }; - static const int NumFaceCodes = 3; - static const int NumSides = 2; - static const int InvalidFace = 0xFF; + static constexpr int NumSides = 2; }; //------------------------------------------------------------------------------ @@ -465,13 +436,8 @@ class Face { @ingroup Gfx @brief comparison modes for depth and stencil state */ -class CompareFunc { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint16_t { - #else +struct CompareFunc { enum Code { - #endif Never = 0, Less, Equal, @@ -479,10 +445,11 @@ class CompareFunc { Greater, NotEqual, GreaterEqual, - Always + Always, + + Num, + Invalid }; - static const int NumCompareFuncs = 8; - static const int InvalidCompareFunc = 0xFF; }; //------------------------------------------------------------------------------ @@ -491,14 +458,9 @@ class CompareFunc { @ingroup Gfx @brief stencil operations */ -class StencilOp { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint16_t { - #else +struct StencilOp { enum Code { - #endif - Keep, + Keep = 0, Zero, Replace, IncrClamp, @@ -506,9 +468,10 @@ class StencilOp { Invert, IncrWrap, DecrWrap, + + Num, + Invalid }; - static const int NumStencilOperations = 8; - static const int InvalidStencilOperation = 0xff; }; //------------------------------------------------------------------------------ @@ -517,13 +480,8 @@ class StencilOp { @ingroup Gfx @brief blending factors */ -class BlendFactor { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint64_t { - #else +struct BlendFactor { enum Code { - #endif Zero = 0, One, SrcColor, @@ -539,9 +497,10 @@ class BlendFactor { OneMinusBlendColor, BlendAlpha, OneMinusBlendAlpha, + + Num, + Invalid }; - static const int NumBlendFactors = 15; - static const int InvalidBlendFactor = 0xFF; }; //------------------------------------------------------------------------------ @@ -550,19 +509,15 @@ class BlendFactor { @ingroup Gfx @brief blending operations */ -class BlendOperation { -public: - #ifdef _MSC_VER // for correct bitfield packing, enum must be typed on MSVC - enum Code : uint64_t { - #else +struct BlendOperation { enum Code { - #endif Add = 0, Subtract, ReverseSubtract, + + Num, + Invalid }; - static const int NumBlendOperations = 3; - static const int InvalidBlendOperation = 0xff; }; //------------------------------------------------------------------------------ @@ -571,9 +526,8 @@ class BlendOperation { @ingroup Gfx @brief classify vertices in a buffer as per-vertex or per-instance data */ -class VertexStepFunction { -public: - enum Code : uint8_t { +struct VertexStepFunction { + enum Code { PerVertex = 0, PerInstance = 1, }; @@ -588,196 +542,17 @@ class VertexStepFunction { A PrimitiveGroup object describes a range of primitive elements in a mesh, where elements are either vertices or indices. */ -class PrimitiveGroup { -public: +struct PrimitiveGroup { + /// index of first vertex or index int BaseElement = 0; + /// number of vertices or indices int NumElements = 0; - /// default constructor + /// setters with chaining PrimitiveGroup() {}; - /// construct for indexed or non-indexed - PrimitiveGroup(int baseElement, int numElements) : - BaseElement(baseElement), - NumElements(numElements) { } -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::BlendState - @ingroup Gfx - @brief describe alpha blending state -*/ -class BlendState { -public: - union { - #pragma pack(push,1) - struct { - uint64_t BlendEnabled:1; - BlendFactor::Code SrcFactorRGB:5; - BlendFactor::Code DstFactorRGB:5; - BlendOperation::Code OpRGB:3; - BlendFactor::Code SrcFactorAlpha:5; - BlendFactor::Code DstFactorAlpha:5; - BlendOperation::Code OpAlpha:3; - PixelChannel::Mask ColorWriteMask:4; - PixelFormat::Code ColorFormat : 5; - PixelFormat::Code DepthFormat : 5; - uint64_t MRTCount : 3; - }; - #pragma pack(pop) - /// hash code from merged state - uint64_t Hash; - }; - - /// constructor - BlendState(); - /// equality - bool operator==(const BlendState& rhs) const { - return this->Hash == rhs.Hash; - }; - /// inequality - bool operator!=(const BlendState& rhs) const { - return this->Hash != rhs.Hash; - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::StencilState - @ingroup Gfx - @brief holds stencil-buffer render state for one face side -*/ -class StencilState { -public: - union { - #pragma pack(push, 1) - struct { - StencilOp::Code FailOp : 4; - StencilOp::Code DepthFailOp : 4; - StencilOp::Code PassOp : 4; - CompareFunc::Code CmpFunc : 4; - }; - #pragma pack(pop) - uint16_t Hash; - }; - /// constructor - StencilState(); - /// equality - bool operator==(const StencilState& rhs) const { - return this->Hash == rhs.Hash; - }; - /// inequality - bool operator!=(const StencilState& rhs) const { - return this->Hash != rhs.Hash; - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::DepthStencilState - @ingroup Gfx - @brief holds the complete depth and stencil render state -*/ -class DepthStencilState { -public: - /// front-side stencil state - StencilState StencilFront; - /// back-side stencil state - StencilState StencilBack; - /// common depth-stencil state - union { - struct { - /// depth compare-function - CompareFunc::Code DepthCmpFunc:5; - /// depth write enabled flag - uint16_t DepthWriteEnabled:1; - /// stencil-enabled flag - uint16_t StencilEnabled:1; - /// stencil read-mask - uint16_t StencilReadMask : 8; - /// stencil write-mask - uint16_t StencilWriteMask : 8; - /// stencil-ref value - uint16_t StencilRef : 8; - }; - uint32_t Hash; - }; - /// constructor - DepthStencilState(); - /// equality - bool operator==(const DepthStencilState& rhs) const; - /// inequality - bool operator!=(const DepthStencilState& rhs) const; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::RasterizerState - @ingroup Gfx - @brief rasterizer state flags -*/ -class RasterizerState { -public: - union { - #pragma pack(push,1) - struct { - uint16_t CullFaceEnabled : 1; - uint16_t ScissorTestEnabled : 1; - uint16_t DitherEnabled : 1; - uint16_t AlphaToCoverageEnabled : 1; - Face::Code CullFace : 3; - uint16_t SampleCount : 4; - }; - #pragma pack(pop) - uint16_t Hash; - }; - /// constructor - RasterizerState(); - /// equality - bool operator==(const RasterizerState& rhs) const { - return this->Hash == rhs.Hash; - }; - /// inequality - bool operator!=(const RasterizerState& rhs) const { - return this->Hash != rhs.Hash; - }; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::SamplerState - @ingroup Gfx - @brief wrap texture sampler state -*/ -class SamplerState { -public: - union { - #pragma pack(push, 1) - struct { - /// texture-wrap mode for u-axis - TextureWrapMode::Code WrapU : 2; - /// texture-wrap mode for v-axis - TextureWrapMode::Code WrapV : 2; - /// texture-wrap mode for w-axis - TextureWrapMode::Code WrapW : 2; - /// magnification filter - TextureFilterMode::Code MagFilter : 3; - /// minification filter - TextureFilterMode::Code MinFilter : 3; - }; - #pragma pack(pop) - uint16_t Hash; - }; - /// constructor - SamplerState(); - /// equality - bool operator==(const SamplerState& rhs) const { - return this->Hash == rhs.Hash; - }; - /// inequality - bool operator!=(const SamplerState& rhs) const { - return this->Hash != rhs.Hash; - }; + PrimitiveGroup(int baseElement, int numElements); + PrimitiveGroup& SetBaseElement(int val); + PrimitiveGroup& SetNumElements(int val); }; //------------------------------------------------------------------------------ @@ -788,31 +563,6 @@ class SamplerState { */ class PassAction { public: - /// default constructor, set all actions to 'clear with default values' - PassAction(); - /// clear all surfaces with given values - static PassAction Clear(const glm::vec4& color=glm::vec4(0.0f, 0.0f, 0.0f, 1.0f), float depth=1.0f, uint8_t stencil=0); - /// clear all surfaces with individual colors - static PassAction Clear(std::initializer_list colors, float depth=1.0f, uint8_t stencil=0); - /// load previous content - static PassAction Load(); - /// discard previous content - static PassAction DontCare(); - - /// FIXME: these methods are confusing, since some are static, some are not! - /// clear a single surface to a color - PassAction& ClearColor(int index, const glm::vec4& color); - /// clear depth-stencil surface - PassAction& ClearDepthStencil(float depth=1.0f, uint8_t stencil=0); - /// set a color surface to 'dont care' (initial content is undefined) - PassAction& DontCareColor(int index); - /// set depth-stencil initial state to 'dont care' - PassAction& DontCareDepthStencil(); - /// initialize color surface with its previus content - PassAction& LoadColor(int index); - /// initialize depth-stencil surface with its previous content - PassAction& LoadDepthStencil(); - /// override clear colors StaticArray Color; /// override clear depth value @@ -836,48 +586,81 @@ class PassAction { LoadDS = (1<<9), }; uint16_t Flags = ClearC0|ClearC1|ClearC2|ClearC3|ClearDS; + + /// default constructor + PassAction(); + /// clear all attachments + PassAction& Clear(float r, float g, float b, float a, float depth=1.0f, uint8_t stencil=0); + /// clear all attachments with color as glm::vec4 + PassAction& Clear(const glm::vec4& color, float depth=1.0f, uint8_t stencil=0); + /// clear all attachments with separate colors + PassAction& Clear(std::initializer_list colors, float depth=1.0f, uint8_t stencil=0); + /// load all attachments with previous content + PassAction& Load(); + /// leave content of all attachments undefined + PassAction& DontCare(); + /// clear all color attachments with the same color + PassAction& ClearColor(float r, float g, float b, float a); + /// clear all color attachments with the same color as glm::vec4 + PassAction& ClearColor(const glm::vec4& color); + /// clear one of the color attachments + PassAction& ClearColor(int index, float r, float g, float b, float a); + /// clear of of the color attachments with color as glm::vec4 + PassAction& ClearColor(int index, const glm::vec4& c); + /// load all color attachments with previous content + PassAction& LoadColor(); + /// load one of the color attachments with previous content + PassAction& LoadColor(int index); + /// leave all color attachments at undefined state + PassAction& DontCareColor(); + /// leave one of the color attachments at undefined state + PassAction& DontCareColor(int index); + + /// clear the depth-stencil attachment + PassAction& ClearDepthStencil(float depth=1.0f, uint8_t stencil=0); + /// load the depth-stencil attachment with previous content + PassAction& LoadDepthStencil(); + /// leave content of depth-stencil attachment undefined + PassAction& DontCareDepthStencil(); }; //------------------------------------------------------------------------------ /** - @class Oryol::DrawState - @brief state required to issue draw calls + @class Oryol::Bindings + @brief describe resource bindings for the next draw call - The DrawState struct contains state required to issue draw calls - with the exception of shader uniforms: - - - 1 pipeline state object - - 1..4 mesh objects + The Bindings struct describes the resource bindings for the + next draw calls: + + - 1..4 vertex buffers (and optional offsets into those buffers) + - 1..4 byte-offsets into vertex buffers + - 0..1 index buffer (and optional offset) + - optional start-offset into index buffer - 0..N textures for the vertex shader stage - 0..N textures for the fragment shader stage */ -struct DrawState { - /// the pipeline state object - Id Pipeline; - /// input meshes - StaticArray Mesh; +struct Bindings { + /// vertex buffer slots + StaticArray VertexBuffers; + /// optional vertex buffer offsets + StaticArray VertexBufferOffsets; + /// optional index buffer + Id IndexBuffer; + /// optional index buffer offsets + int IndexBufferOffset = 0; /// vertex shader stage textures StaticArray VSTexture; /// fragment shader stage textures StaticArray FSTexture; -}; -//------------------------------------------------------------------------------ -/** - @class Oryol::GfxFrameInfo - @brief per-frame stats of the Gfx module -*/ -struct GfxFrameInfo { - int NumPasses = 0; - int NumApplyViewPort = 0; - int NumApplyScissorRect = 0; - int NumApplyDrawState = 0; - int NumApplyUniformBlock = 0; - int NumUpdateVertices = 0; - int NumUpdateIndices = 0; - int NumUpdateTextures = 0; - int NumDraw = 0; - int NumDrawInstanced = 0; + /// setters with chaining + Bindings(); + Bindings& SetVertexBuffer(int slot, const Id& bufId); + Bindings& SetVertexBufferOffset(int slot, int offset); + Bindings& SetIndexBuffer(const Id& bufId); + Bindings& SetIndexBufferOffset(int offset); + Bindings& SetVSTexture(int slot, const Id& texId); + Bindings& SetFSTexture(int slot, const Id& texId); }; //------------------------------------------------------------------------------ @@ -885,28 +668,37 @@ struct GfxFrameInfo { @class Oryol::VertexLayout @ingroup Gfx @brief describes the data layout of a vertex in a vertex buffer + + FIXME: support vertex components with gaps (manually defined offset and stride) */ class VertexLayout { public: /// a component in a vertex layout - #pragma pack(push,1) class Component { public: /// default constructor - Component() {}; - /// construct from vertex attr and format - Component(VertexAttr::Code attr, VertexFormat::Code fmt) : Attr(attr), Format(fmt) { } + Component() { } + /// construct from format (no attr name) + Component(VertexFormat::Code fmt): Format(fmt) { } + /// construct from vertex attr name and format + Component(const StringAtom& name, VertexFormat::Code fmt): Name(name), Format(fmt) { } /// return true if valid (attr and format set) - bool IsValid() const; + bool IsValid() const { + return this->Format != VertexFormat::Invalid; + } /// clear the component (unset attr and format) - void Clear(); + void Clear() { + *this = Component(); + } /// get byte size of component - int ByteSize() const; + int ByteSize() const { + return VertexFormat::ByteSize(this->Format); + } - VertexAttr::Code Attr = VertexAttr::InvalidVertexAttr; - VertexFormat::Code Format = VertexFormat::InvalidVertexFormat; + StringAtom Name; + VertexFormat::Code Format = VertexFormat::Invalid; + int Offset = 0; // offset will be written in VertexLayout::Add }; - #pragma pack(pop) /// the vertex step function, used for instancing, default is 'PerVertex' VertexStepFunction::Code StepFunction = VertexStepFunction::PerVertex; @@ -923,8 +715,10 @@ class VertexLayout { bool Empty() const; /// add a component VertexLayout& Add(const Component& comp); - /// add component by name and format - VertexLayout& Add(VertexAttr::Code attr, VertexFormat::Code format); + /// add an unnamed component + VertexLayout& Add(VertexFormat::Code format); + /// add a named component + VertexLayout& Add(const StringAtom& name, VertexFormat::Code format); /// add multiple components via initializer list VertexLayout& Add(std::initializer_list l); /// enable layout for instancing, set StepFunction to PerInstance and StepRate to 1 @@ -933,55 +727,18 @@ class VertexLayout { int NumComponents() const; /// get component at index const Component& ComponentAt(int index) const; - /// get component index by vertex attribute, return InvalidIndex if layout doesn't include attr - int ComponentIndexByVertexAttr(VertexAttr::Code attr) const; + /// find component index by name, return InvalidIndex if not found + int ComponentIndexByName(const StringAtom& name) const; + /// test if the layout contains a specific vertex attribute by name + bool Contains(const StringAtom& name) const; /// get byte size of vertex (aka stride) int ByteSize() const; /// get byte offset of a component int ComponentByteOffset(int componentIndex) const; - /// test if the layout contains a specific vertex attribute - bool Contains(VertexAttr::Code attr) const; private: StaticArray comps; - StaticArray byteOffsets; - StaticArray attrCompIndices; // maps vertex attributes to component indices - int8_t numComps = 0; - uint8_t byteSize = 0; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::TextureAttrs - @ingroup Gfx - @brief holds the public attributes of a Texture object - - @todo: describe TextureAttrs -*/ -struct TextureAttrs { - /// texture locator (usually the URL of the texture file) - class Locator Locator; - /// the texture type (2D, 3D, cube...) - TextureType::Code Type = TextureType::InvalidTextureType; - /// the RGBA pixel format of texture data - PixelFormat::Code ColorFormat = PixelFormat::InvalidPixelFormat; - /// optional depth format (only used for render target textures) - PixelFormat::Code DepthFormat = PixelFormat::InvalidPixelFormat; - /// optional sample count (only used for MSAA render target textures) - int SampleCount = 1; - /// texture usage hint - Usage::Code TextureUsage = Usage::InvalidUsage; - /// width of top-level mipmap in pixels - int Width = 0; - /// height of top-level mipmap in pixels - int Height = 0; - /// depth of top-level mipmap in pixels (only used for 3D textures) - int Depth = 0; - /// number of mipmaps (1 for 'no child mipmaps') - int NumMipMaps = 1; - /// true if this is a render target texture - bool IsRenderTarget = false; - /// true if this render target texture has an attached depth buffer - bool HasDepthBuffer = false; + int numComps = 0; + int byteSize = 0; }; //------------------------------------------------------------------------------ @@ -994,33 +751,20 @@ struct TextureAttrs { different from the display setup parameters. */ struct DisplayAttrs { - /// window width (including window chrome) - int WindowWidth = 0; - /// window height (including window chrome) - int WindowHeight = 0; - /// x-position of window - int WindowPosX = 0; - /// y-position of window - int WindowPosY = 0; /// width of framebuffer associated with window - int FramebufferWidth = 0; + int Width = 0; /// height of framebuffer associated with window - int FramebufferHeight = 0; + int Height = 0; /// framebuffer pixel format - PixelFormat::Code ColorPixelFormat = PixelFormat::RGBA8; + PixelFormat::Code ColorFormat = PixelFormat::RGBA8; /// depth buffer pixel format (PixelFormat::None if no depth buffer) - PixelFormat::Code DepthPixelFormat = PixelFormat::DEPTHSTENCIL; + PixelFormat::Code DepthFormat = PixelFormat::DEPTHSTENCIL; /// number of multisample-anti-aliasing samples int SampleCount = 1; /// indicates windowed or fullscreen mode bool Windowed = true; /// vsync swap interval (0 means: no vsync) int SwapInterval = 1; - /// window title as UTF-8 - String WindowTitle; - - /// init a DisplayAttrs object from a TextureAttrs object - static DisplayAttrs FromTextureAttrs(const TextureAttrs& texAttrs); }; //------------------------------------------------------------------------------ @@ -1032,78 +776,47 @@ struct DisplayAttrs { */ class GfxEvent { public: + /// handler function typedef + typedef std::function Handler; + /// id for an event handler subscription + typedef uint32_t HandlerId; /// event types - enum Type { + enum EventType { DisplayModified, NumTypes, InvalidType }; + enum EventType Type = InvalidType; + struct DisplayAttrs DisplayAttrs; + /// default constructor GfxEvent(); /// constructor with arguments - GfxEvent(Type type, const DisplayAttrs& attrs) : Type(type), DisplayAttrs(attrs) { } - - enum Type Type = InvalidType; - struct DisplayAttrs DisplayAttrs; + GfxEvent(EventType type, const struct DisplayAttrs& attrs) : Type(type), DisplayAttrs(attrs) { } }; //------------------------------------------------------------------------------ /** - @class Oryol::ImageDataAttrs - @brief describe offsets and sizes of image surfaces + @class Oryol::ImageContent + @brief describe content of image surfaces */ -class ImageDataAttrs { +class ImageContent { public: - /// constructor - ImageDataAttrs(); - /// number of faces - int NumFaces = 0; - /// number of mipmaps - int NumMipMaps = 0; - /// pixel data mipmap image offsets - StaticArray, GfxConfig::MaxNumTextureFaces> Offsets; - /// pixel data mipmap image sizes - StaticArray, GfxConfig::MaxNumTextureFaces> Sizes; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::IndexBufferAttrs - @ingroup Gfx - @brief attributes of an index buffer -*/ -struct IndexBufferAttrs { - /// number of indices in the index buffer - int NumIndices = 0; - /// type of indices (16-bit or 32-bit) - IndexType::Code Type = IndexType::InvalidIndexType; - /// buffer usage hint - Usage::Code BufferUsage = Usage::InvalidUsage; - /// computes the byte size of index buffer data - int ByteSize() const; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::VertexBufferAttrs - @ingroup Gfx - @brief attributes of one vertex buffer -*/ -struct VertexBufferAttrs { - /// number of vertices in the vertex buffer - int NumVertices = 0; - /// describes the vertex layout of a vertex in the buffer - VertexLayout Layout; - /// buffer usage hint - Usage::Code BufferUsage = Usage::InvalidUsage; - /// computes the byte size of the contained vertex buffer data - int ByteSize() const; + /// mipmap surface data pointers + StaticArray, GfxConfig::MaxNumTextureFaces> Pointer; + /// mipmap surface data sizes (in bytes) + StaticArray, GfxConfig::MaxNumTextureFaces> Size; + + /// setters with chaining + ImageContent(); + ImageContent& SetPointer(int faceIndex, int mipIndex, const void* ptr); + ImageContent& SetSize(int faceIndex, int mipIndex, int size); }; //------------------------------------------------------------------------------ /** - @class Oryol::GfxSetup + @class Oryol::GfxDesc @ingroup Gfx @brief Gfx module setup parameters @@ -1114,370 +827,289 @@ struct VertexBufferAttrs { @see Gfx, DisplayAttrs */ -class GfxSetup { -public: - /// shortcut for windowed mode (with RGBA8, 24+8 stencil/depth, no MSAA) - static GfxSetup Window(int width, int height, String windowTitle); - /// shortcut for fullscreen mode (with RGBA8, 24+8 stencil/depth, no MSAA) - static GfxSetup Fullscreen(int width, int height, String windowTitle); - /// shortcut for windowed mode with 4xMSAA (with RGBA8, 24+8 stencil/depth) - static GfxSetup WindowMSAA4(int width, int height, String windowTitle); - /// shortcut for fullscreen mode with 4xMSAA (with RGBA8, 24+8 stencil/depth) - static GfxSetup FullscreenMSAA4(int width, int height, String windowTitle); - /// canvas width +struct GfxDesc { int Width = 640; - /// canvas height int Height = 400; - /// color pixel format PixelFormat::Code ColorFormat = PixelFormat::RGBA8; - /// depth pixel format PixelFormat::Code DepthFormat = PixelFormat::DEPTHSTENCIL; - /// MSAA samples (2, 4, 8... no MSAA: 1) int SampleCount = 1; - /// windowed vs Fullscreen bool Windowed = true; - /// swap interval (0 => no vsync, default is 1) int SwapInterval = 1; - /// window title - String Title = "Oryol"; - /// enable to render full-res on HighDPI displays (not supported on all platforms) + StringAtom Title = "Oryol"; bool HighDPI = false; - /// default clear values (or dont care) - PassAction DefaultPassAction; - /// if true, ignore own size and instead track size of an HTML element (emscripten only) bool HtmlTrackElementSize = false; - /// name of the HTML element to track (default: #canvas) - String HtmlElement = "#canvas"; - /// resource pool size by resource type - StaticArray ResourcePoolSize; - /// resource creation throttling (max resources created async per frame) - StaticArray ResourceThrottling; - /// initial resource label stack capacity + StringAtom HtmlElement = "canvas"; + StaticArray ResourcePoolSize; int ResourceLabelStackCapacity = 256; - /// initial resource registry capacity int ResourceRegistryCapacity = 256; - /// size of the global uniform buffer (only relevant on some platforms) int GlobalUniformBufferSize = GfxConfig::DefaultGlobalUniformBufferSize; - /// max number of drawcalls per frame (only relevant on some platforms) - int MaxDrawCallsPerFrame = GfxConfig::DefaultMaxDrawCallsPerFrame; - /// max number of ApplyDrawState per frame (only relevant on some platforms) - int MaxApplyDrawStatesPerFrame = GfxConfig::DefaultMaxApplyDrawStatesPerFrame; - /// get DisplayAttrs object initialized to setup values - DisplayAttrs GetDisplayAttrs() const; - /// default constructor - GfxSetup(); -}; -//------------------------------------------------------------------------------ -/** - @class Oryol::MeshSetup - @ingroup Gfx - @brief setup attributes for meshes -*/ -class MeshSetup { -public: - /// asynchronously load from file - static MeshSetup FromFile(const class Locator& loc, Id placeholder=Id::InvalidId()); - /// setup from from data in memory - static MeshSetup FromData(Usage::Code vertexUsage=Usage::Immutable, Usage::Code indexUsage=Usage::Immutable); - /// setup from data in memory with blueprint - static MeshSetup FromData(const MeshSetup& blueprint); - /// setup empty mesh (mostly for dynamic streaming) - static MeshSetup Empty(int numVertices, Usage::Code vertexUsage, IndexType::Code indexType=IndexType::None, int numIndices=0, Usage::Code indexUsage=Usage::InvalidUsage); - /// setup a fullscreen quad mesh - static MeshSetup FullScreenQuad(bool flipV=false); - /// check if should load asynchronously - bool ShouldSetupFromFile() const; - /// check if should setup from data in memory - bool ShouldSetupFromData() const; - /// check if should setup empty mesh - bool ShouldSetupEmpty() const; - /// check if should setup fullscreen quad mesh - bool ShouldSetupFullScreenQuad() const; - /// add a primitive group (required for CreateEmpty) - void AddPrimitiveGroup(const PrimitiveGroup& primGroup); - /// get number of primitive groups - int NumPrimitiveGroups() const; - /// get primitive group at index - const class PrimitiveGroup& PrimitiveGroup(int index) const; - /// vertex-data usage - Usage::Code VertexUsage = Usage::InvalidUsage; - /// index-data usage - Usage::Code IndexUsage = Usage::InvalidUsage; - /// vertex layout - VertexLayout Layout; - /// number of vertices - int NumVertices = 0; - /// number of indices - int NumIndices = 0; - /// index type - IndexType::Code IndicesType = IndexType::None; - /// flip v coordinates for fullscreen quad (so that origin is top-left) - bool FullScreenQuadFlipV = false; - /// resource locator - class Locator Locator = Locator::NonShared(); - /// placeholder Id - Id Placeholder; - /// vertex data byte offset in data (default: 0, set to InvalidIndex if no vertex data provided) - int VertexDataOffset = 0; - /// index data byte offset in data (default: InvalidIndex, no index data provided) - int IndexDataOffset = 0; -private: - int numPrimGroups = 0; - class PrimitiveGroup primGroups[GfxConfig::MaxNumPrimGroups]; - bool setupFromFile = false; - bool setupFromData = false; - bool setupEmpty = false; - bool setupFullScreenQuad = false; + /// setters with chaining + GfxDesc(); + GfxDesc(const GfxDesc& rhs); + GfxDesc& SetWidth(int w); + GfxDesc& SetHeight(int h); + GfxDesc& SetColorFormat(PixelFormat::Code fmt); + GfxDesc& SetDepthFormat(PixelFormat::Code fmt); + GfxDesc& SetSampleCount(int c); + GfxDesc& SetWindowed(bool b); + GfxDesc& SetSwapInterval(int i); + GfxDesc& SetTitle(const StringAtom& t); + GfxDesc& SetHighDPI(bool b); + GfxDesc& SetHtmlTrackElementSize(bool b); + GfxDesc& SetHtmlElement(const StringAtom& e); + GfxDesc& SetResourcePoolSize(GfxResourceType::Code type, int size); + GfxDesc& SetResourceLabelStackCapacity(int c); + GfxDesc& SetResourceRegistryCapacity(int c); + GfxDesc& SetGlobalUniformBufferSize(int s); }; //------------------------------------------------------------------------------ /** - @class Oryol::PipelineSetup + @class Oryol::BufferDesc @ingroup Gfx - @brief setup object for pipeline resources + @brief creation attributes for vertex- and index-buffers */ -class PipelineSetup { -public: - /// construct from shader - static PipelineSetup FromShader(const Id& shd); - /// construct from vertex layout and shader - static PipelineSetup FromLayoutAndShader(const VertexLayout& layout, const Id& shd); - /// resource locator +struct BufferDesc { class Locator Locator = Locator::NonShared(); - /// blend state (GLES3.0 doesn't allow separate MRT blend state - class BlendState BlendState; - /// blend color - glm::vec4 BlendColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - /// depth-stencil state - class DepthStencilState DepthStencilState; - /// rasterizer state - class RasterizerState RasterizerState; - /// input vertex layouts (one per mesh slot) - StaticArray Layouts; - /// primitive type - PrimitiveType::Code PrimType = PrimitiveType::Triangles; - /// shader - Id Shader; + BufferType::Code Type = BufferType::VertexBuffer; + Oryol::Usage::Code Usage = Usage::Immutable; + int Size = 0; + const void* Content = nullptr; + StaticArray NativeBuffers; + + /// setters with chaining + BufferDesc(); + BufferDesc(const BufferDesc& rhs); + BufferDesc& SetLocator(const class Locator& l); + BufferDesc& SetType(BufferType::Code t); + BufferDesc& SetUsage(Usage::Code u); + BufferDesc& SetSize(int s); + BufferDesc& SetContent(const void* c); + BufferDesc& SetNativeBuffer(int index, intptr_t buf); }; //------------------------------------------------------------------------------ /** - @class Oryol::PassSetup + @class Oryol::PipelineDesc @ingroup Gfx - @brief setup attributes for render pass resource + @brief creation attribute for pipeline state objects */ -class PassSetup { -public: - /// construct from single render target textures, and option depth-stencil texture - static PassSetup From(Id colorTexture, Id depthStencilTexture=Id::InvalidId()); - /// construct from MRT render target textures, and option depth-stencil texture - static PassSetup From(std::initializer_list colorTextures, Id depthStencilTexture=Id::InvalidId()); - /// resource locator +struct PipelineDesc { class Locator Locator = Locator::NonShared(); - /// 1..N color attachments - struct ColorAttachment { - Id Texture; - uint16_t MipLevel = 0; ///< mipmap-level - uint16_t Slice = 0; ///< 2D-array-slice, 3D-depth-slice or cubemap face - }; - StaticArray ColorAttachments; - /// optional depth-stencil attachment - Id DepthStencilTexture; - /// default pass action, if no PassAction provided in BeginPass - PassAction DefaultAction; + Id Shader; + StaticArray Layouts; + PrimitiveType::Code PrimType = PrimitiveType::Triangles; + Oryol::IndexType::Code IndexType = IndexType::None; + CompareFunc::Code DepthCmpFunc = CompareFunc::Always; + bool DepthWriteEnabled = false; + bool StencilEnabled = false; + uint8_t StencilReadMask = 0xFF; + uint8_t StencilWriteMask = 0xFF; + uint8_t StencilRef = 0x00; + StencilOp::Code StencilFrontFailOp = StencilOp::Keep; + StencilOp::Code StencilFrontDepthFailOp = StencilOp::Keep; + StencilOp::Code StencilFrontPassOp = StencilOp::Keep; + CompareFunc::Code StencilFrontCmpFunc = CompareFunc::Always; + StencilOp::Code StencilBackFailOp = StencilOp::Keep; + StencilOp::Code StencilBackDepthFailOp = StencilOp::Keep; + StencilOp::Code StencilBackPassOp = StencilOp::Keep; + CompareFunc::Code StencilBackCmpFunc = CompareFunc::Always; + bool BlendEnabled = false; + BlendFactor::Code BlendSrcFactorRGB = BlendFactor::One; + BlendFactor::Code BlendDstFactorRGB = BlendFactor::Zero; + BlendOperation::Code BlendOpRGB = BlendOperation::Add; + BlendFactor::Code BlendSrcFactorAlpha = BlendFactor::One; + BlendFactor::Code BlendDstFactorAlpha = BlendFactor::Zero; + BlendOperation::Code BlendOpAlpha = BlendOperation::Add; + PixelChannel::Mask ColorWriteMask = PixelChannel::RGBA; + PixelFormat::Code ColorFormat = PixelFormat::RGBA8; + PixelFormat::Code DepthFormat = PixelFormat::DEPTHSTENCIL; + int MRTCount = 1; + glm::vec4 BlendColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + bool CullFaceEnabled = false; + bool AlphaToCoverageEnabled = false; + Face::Code CullFace = Face::Back; + int SampleCount = 1; + float DepthBias = 0.0f; + float DepthBiasSlopeScale = 0.0f; + float DepthBiasClamp = 0.0f; + + /// setters with chaining + PipelineDesc() { }; + PipelineDesc(const PipelineDesc& rhs); + PipelineDesc& SetLocator(const class Locator& loc); + PipelineDesc& SetShader(const Id& shd); + PipelineDesc& SetLayout(int slotIndex, const VertexLayout& layout); + PipelineDesc& SetPrimitiveType(PrimitiveType::Code t); + PipelineDesc& SetIndexType(IndexType::Code t); + PipelineDesc& SetDepthCmpFunc(CompareFunc::Code f); + PipelineDesc& SetDepthWriteEnabled(bool b); + PipelineDesc& SetStencilEnabled(bool b); + PipelineDesc& SetStencilReadMask(uint8_t m); + PipelineDesc& SetStencilWriteMask(uint8_t m); + PipelineDesc& SetStencilRef(uint8_t r); + PipelineDesc& SetStencilFailOp(Face::Code face, StencilOp::Code op); + PipelineDesc& SetStencilDepthFailOp(Face::Code face, StencilOp::Code op); + PipelineDesc& SetStencilPassOp(Face::Code face, StencilOp::Code op); + PipelineDesc& SetStencilCmpFunc(Face::Code face, CompareFunc::Code fn); + PipelineDesc& SetBlendEnabled(bool b); + PipelineDesc& SetBlendSrcFactor(BlendFactor::Code f); + PipelineDesc& SetBlendSrcFactorRGB(BlendFactor::Code f); + PipelineDesc& SetBlendSrcFactorAlpha(BlendFactor::Code f); + PipelineDesc& SetBlendDstFactor(BlendFactor::Code f); + PipelineDesc& SetBlendDstFactorRGB(BlendFactor::Code f); + PipelineDesc& SetBlendDstFactorAlpha(BlendFactor::Code f); + PipelineDesc& SetBlendOp(BlendOperation::Code op); + PipelineDesc& SetBlendOpRGB(BlendOperation::Code op); + PipelineDesc& SetBlendOpAlpha(BlendOperation::Code op); + PipelineDesc& SetColorWriteMask(PixelChannel::Mask m); + PipelineDesc& SetColorFormat(PixelFormat::Code fmt); + PipelineDesc& SetDepthFormat(PixelFormat::Code fmt); + PipelineDesc& SetSampleCount(int c); + PipelineDesc& SetMRTCount(int c); + PipelineDesc& SetBlendColor(const glm::vec4& c); + PipelineDesc& SetCullFaceEnabled(bool b); + PipelineDesc& SetCullFace(Face::Code f); + PipelineDesc& SetAlphaToCoverageEnabled(bool b); + PipelineDesc& SetDepthBias(float f); + PipelineDesc& SetDepthBiasSlopeScale(float f); + PipelineDesc& SetDepthBiasClamp(float f); }; //------------------------------------------------------------------------------ /** - @class Oryol::ShaderSetup + @class Oryol::ShaderDesc @ingroup Gfx - @brief setup class for shaders + @brief creation attributes for shaders */ -class ShaderSetup { -public: - /// default constructor - ShaderSetup() { } - /// construct with resource locator - ShaderSetup(const Locator& loc) : Locator(loc) { } - /// the resource locator +struct ShaderDesc { class Locator Locator = Locator::NonShared(); - /// set shader program from vertex- and fragment-shader sources - void SetProgramFromSources(ShaderLang::Code slang, const String& vsSource, const String& fsSource); - /// set shader program from precompiled shader byte code - void SetProgramFromByteCode(ShaderLang::Code slang, const uint8_t* vsByteCode, uint32_t vsNumBytes, const uint8_t* fsByteCode, uint32_t fsNumBytes, const char* vsFunc=nullptr, const char* fsFunc=nullptr); - /// set vertex shader input layout - void SetInputLayout(const VertexLayout& vsInputLayout); - /// add a uniform block - void AddUniformBlock(const StringAtom& type, const StringAtom& name, uint32_t typeHash, uint32_t byteSize, ShaderStage::Code bindStage, int32_t bindSlot); - /// add a texture declaration - void AddTexture(const StringAtom& name, TextureType::Code type, ShaderStage::Code bindStage, int32_t bindSlot); - /// get the vertex shader input layout - const VertexLayout& InputLayout() const; - /// get program vertex shader source (only valid if setup from sources) - const String& VertexShaderSource(ShaderLang::Code slang) const; - /// get program fragment shader source (only valid if setup from sources) - const String& FragmentShaderSource(ShaderLang::Code slang) const; - /// get program vertex shader byte code, returns nullptr if no byte code exists - void VertexShaderByteCode(ShaderLang::Code slang, const void*& outPtr, uint32_t& outSize) const; - /// get program fragment shader byte code, returns nullptr if no byte code exists - void FragmentShaderByteCode(ShaderLang::Code slang, const void*& outPtr, uint32_t& outSize) const; - /// get vertex shader name (if using metal-style shader library - const StringAtom& VertexShaderFunc(ShaderLang::Code slang) const; - /// get fragment shader name (if using metal-style shader library - const StringAtom& FragmentShaderFunc(ShaderLang::Code slang) const; - /// get number of uniform blocks - int NumUniformBlocks() const; - /// find uniform block index by bind stage and slot (return InvalidIndex if not found) - int UniformBlockIndexByStageAndSlot(ShaderStage::Code bindStage, int bindSlot) const; - /// get uniform block type at index - const StringAtom& UniformBlockType(int index) const; - /// get uniform block name at index - const StringAtom& UniformBlockName(int index) const; - /// get uniform block type hash - uint32_t UniformBlockTypeHash(int index) const; - /// get uniform block byte size - uint32_t UniformBlockByteSize(int index) const; - /// get uniform block shader stage at index - ShaderStage::Code UniformBlockBindStage(int index) const; - /// get uniform block bind slot at index - int UniformBlockBindSlot(int index) const; - /// get number of textures - int NumTextures() const; - /// find texture index by bind stage and slot (return InvalidIndex if not found) - int TextureIndexByStageAndSlot(ShaderStage::Code bindStage, int bindSlot) const; - /// get texture name at index - const StringAtom& TexName(int index) const; - /// get texture type at index - TextureType::Code TexType(int index) const; - /// get texture bind stage - ShaderStage::Code TexBindStage(int index) const; - /// get texture bind slot - int TexBindSlot(int index) const; -private: - struct programEntry { - StaticArray vsSources; - StaticArray fsSources; - StaticArray vsFuncs; - StaticArray fsFuncs; - struct byteCodeEntry { - const void* ptr = nullptr; - uint32_t size = 0; - }; - StaticArray vsByteCode; - StaticArray fsByteCode; - VertexLayout vsInputLayout; + VertexLayout Layout; + struct UniformBlockDesc { + const char* Name = nullptr; + const char* Type = nullptr; + int Size = 0; }; - struct uniformBlockEntry { - StringAtom type; - StringAtom name; - uint32_t typeHash = 0; - uint32_t byteSize = 0; - ShaderStage::Code bindStage = ShaderStage::InvalidShaderStage; - int bindSlot = InvalidIndex; + struct TextureDesc { + const char* Name = nullptr; + TextureType::Code Type = TextureType::Invalid; }; - struct textureEntry { - StringAtom name; - TextureType::Code type = TextureType::InvalidTextureType; - ShaderStage::Code bindStage = ShaderStage::InvalidShaderStage; - int bindSlot = InvalidIndex; + struct StageDesc { + const char* Source = nullptr; + const uint8_t* ByteCode = nullptr; + int ByteCodeSize = 0; + const char* Entry = nullptr; + StaticArray UniformBlocks; + StaticArray Textures; }; - static const int MaxNumUniformBlocks = ShaderStage::NumShaderStages * GfxConfig::MaxNumUniformBlocksPerStage; - static const int MaxNumTextures = GfxConfig::MaxNumVertexTextures + GfxConfig::MaxNumFragmentTextures; - programEntry program; - int numUniformBlocks = 0; - StaticArray uniformBlocks; - int numTextures = 0; - StaticArray textures; + StaticArray Stage; + + /// setters with chaining + ShaderDesc() { }; + ShaderDesc(const ShaderDesc& rhs); + ShaderDesc& SetLocator(const class Locator& loc); + ShaderDesc& SetSource(ShaderStage::Code stg, const char* src); + ShaderDesc& SetByteCode(ShaderStage::Code stg, const uint8_t* ptr, int size); + ShaderDesc& SetEntry(ShaderStage::Code stg, const char* entry); + ShaderDesc& SetAttr(const StringAtom& name, VertexFormat::Code fmt); + ShaderDesc& SetUniformBlock(ShaderStage::Code stg, int slot, const char* name, const char* type, int size); + ShaderDesc& SetTexture(ShaderStage::Code stg, int slot, const char* name, TextureType::Code type); }; //------------------------------------------------------------------------------ /** - @class Oryol::TextureSetup + @class Oryol::TextureDesc @ingroup Gfx @brief setup object for textures and render targets */ -class TextureSetup { -public: - /// asynchronously load from file - static TextureSetup FromFile(const Locator& loc, Id placeholder=Id::InvalidId()); - /// asynchronously load from file - static TextureSetup FromFile(const Locator& loc, const TextureSetup& blueprint=TextureSetup(), Id placeholder=Id::InvalidId()); - /// setup 2D texture from raw pixel data - static TextureSetup FromPixelData2D(int w, int h, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint=TextureSetup()); - /// setup cube texture from raw pixel data - static TextureSetup FromPixelDataCube(int w, int h, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint=TextureSetup()); - //// setup 3D texture from raw pixel data - static TextureSetup FromPixelData3D(int w, int h, int d, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint=TextureSetup()); - /// setup array texture from raw pixel data - static TextureSetup FromPixelDataArray(int w, int h, int layers, int numMipMaps, PixelFormat::Code fmt, const TextureSetup& blueprint=TextureSetup()); - /// setup empty 2D texture - static TextureSetup Empty2D(int w, int h, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint=TextureSetup()); - /// setup empty cube texture - static TextureSetup EmptyCube(int w, int h, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint=TextureSetup()); - /// setup empty 3D texture - static TextureSetup Empty3D(int w, int h, int d, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint=TextureSetup()); - /// setup empty array texture - static TextureSetup EmptyArray(int w, int h, int layers, int numMipMaps, PixelFormat::Code fmt, Usage::Code usage, const TextureSetup& blueprint=TextureSetup()); - /// setup as 2D render target - static TextureSetup RenderTarget2D(int w, int h, PixelFormat::Code colorFmt=PixelFormat::RGBA8, PixelFormat::Code depthFmt=PixelFormat::None); - /// setup as cube render target - static TextureSetup RenderTargetCube(int w, int h, PixelFormat::Code colorFmt=PixelFormat::RGBA8, PixelFormat::Code depthFmt=PixelFormat::None); - /// setup as 3D render target - static TextureSetup RenderTarget3D(int w, int h, int d, PixelFormat::Code colorFmt=PixelFormat::RGBA8, PixelFormat::Code depthFmt=PixelFormat::None); - /// setup as array render target - static TextureSetup RenderTargetArray(int w, int h, int layers, PixelFormat::Code colorFmt=PixelFormat::RGBA8, PixelFormat::Code depthFmt=PixelFormat::None); - /// setup texture from existing native texture(s) (needs GfxFeature::NativeTexture) - static TextureSetup FromNativeTexture(int w, int h, int numMipMaps, TextureType::Code type, PixelFormat::Code fmt, Usage::Code usage, intptr_t h0, intptr_t h1=0); - /// return true if texture should be setup from a file - bool ShouldSetupFromFile() const; - /// return true if texture should be setup from raw pixel data - bool ShouldSetupFromPixelData() const; - /// return true if texture should be setup from native texture handles - bool ShouldSetupFromNativeTexture() const; - /// return true if texture should be created empty - bool ShouldSetupEmpty() const; - /// return true if render target has depth - bool HasDepth() const; - /// intended usage - Usage::Code TextureUsage = Usage::Immutable; - /// texture type +struct TextureDesc { + class Locator Locator = Locator::NonShared(); TextureType::Code Type = TextureType::Texture2D; - /// use as render target? - bool IsRenderTarget = false; - /// width in pixels + bool RenderTarget = false; int Width = 1; - /// height in pixels int Height = 1; - /// depth/layers in pixels (for 3D and Array textures) int Depth = 1; - /// number of mipmaps (default is 1, only for FromPixelData) int NumMipMaps = 1; - /// the color pixel format - PixelFormat::Code ColorFormat = PixelFormat::RGBA8; - /// the depth pixel format (only if render target, PixelFormat::None if render target should not have depth buffer) - PixelFormat::Code DepthFormat = PixelFormat::None; - /// MSAA samples (2, 4, 8... no MSAA: 1), check MSAARenderTargets feature availability! + Oryol::Usage::Code Usage = Usage::Immutable; + PixelFormat::Code Format = PixelFormat::RGBA8; int SampleCount = 1; - /// sampler state - SamplerState Sampler; - /// resource locator + TextureFilterMode::Code MagFilter = TextureFilterMode::Nearest; + TextureFilterMode::Code MinFilter = TextureFilterMode::Nearest; + TextureWrapMode::Code WrapU = TextureWrapMode::Repeat; + TextureWrapMode::Code WrapV = TextureWrapMode::Repeat; + TextureWrapMode::Code WrapW = TextureWrapMode::Repeat; + StaticArray NativeTextures; + ImageContent Content; + + /// setters with chaining + TextureDesc(); + TextureDesc(const TextureDesc& rhs); + TextureDesc& SetLocator(const class Locator& loc); + TextureDesc& SetType(TextureType::Code t); + TextureDesc& SetRenderTarget(bool b); + TextureDesc& SetWidth(int w); + TextureDesc& SetHeight(int h); + TextureDesc& SetDepth(int d); + TextureDesc& SetLayers(int l); + TextureDesc& SetNumMipMaps(int n); + TextureDesc& SetUsage(Usage::Code u); + TextureDesc& SetFormat(PixelFormat::Code fmt); + TextureDesc& SetSampleCount(int c); + TextureDesc& SetMagFilter(TextureFilterMode::Code f); + TextureDesc& SetMinFilter(TextureFilterMode::Code f); + TextureDesc& SetWrapU(TextureWrapMode::Code m); + TextureDesc& SetWrapV(TextureWrapMode::Code m); + TextureDesc& SetWrapW(TextureWrapMode::Code m); + TextureDesc& SetNativeTexture(int index, intptr_t tex); + TextureDesc& SetMipSize(int faceIndex, int mipIndex, int size); + TextureDesc& SetMipContent(int faceIndex, int mipIndex, const void* ptr); +}; + +//------------------------------------------------------------------------------ +/** + @class Oryol::PassDesc + @ingroup Gfx + @brief creation attributes for render pass resource +*/ +struct PassDesc { class Locator Locator = Locator::NonShared(); - /// resource placeholder - Id Placeholder; - /// optional: native texture handle (only on platforms which support GfxFeature::NativeTextures) - static const int MaxNumNativeHandles = 2; - StaticArray NativeHandle; - /// optional image surface offsets and sizes - ImageDataAttrs ImageData; - /// default constructor - TextureSetup(); -private: - bool setupFromFile = false; - bool setupFromPixelData = false; - bool setupFromNativeHandle = false; - bool setupEmpty = false; - bool hasMipMaps = false; + struct Attachment { + Id Texture; + int MipLevel = 0; + union { + int Face = 0; + int Layer; + int Slice; + }; + }; + StaticArray ColorAttachments; + Attachment DepthStencilAttachment; + + /// setters with chaining + PassDesc() { }; + PassDesc(const PassDesc& rhs); + PassDesc& SetLocator(const class Locator& loc); + PassDesc& SetColorAttachment(int slotIndex, const Id& tex, int mipLevel=0, int faceLayerSlice=0); + PassDesc& SetDepthStencilAttachment(const Id& tex, int mipLevel=0, int faceLayerSlice=0); }; - + +//------------------------------------------------------------------------------ +/** + @class Oryol::GfxFrameInfo + @brief per-frame stats of the Gfx module +*/ +struct GfxFrameInfo { + int NumPasses = 0; + int NumApplyViewPort = 0; + int NumApplyScissorRect = 0; + int NumApplyPipeline = 0; + int NumApplyBindings = 0; + int NumApplyUniforms = 0; + int NumUpdateBuffer = 0; + int NumUpdateTexture = 0; + int NumDraw = 0; + int NumDrawInstanced = 0; +}; + } // namespace Oryol diff --git a/code/Modules/Gfx/MeshLoaderBase.cc b/code/Modules/Gfx/MeshLoaderBase.cc deleted file mode 100644 index da910b3cd..000000000 --- a/code/Modules/Gfx/MeshLoaderBase.cc +++ /dev/null @@ -1,28 +0,0 @@ -//------------------------------------------------------------------------------ -// MeshLoaderBase.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "MeshLoaderBase.h" - -namespace Oryol { - -//------------------------------------------------------------------------------ -MeshLoaderBase::MeshLoaderBase(const MeshSetup& setup_) : -setup(setup_) { - // empty -} - -//------------------------------------------------------------------------------ -MeshLoaderBase::MeshLoaderBase(const MeshSetup& setup_, LoadedFunc loadedFunc) : -setup(setup_), -onLoaded(loadedFunc) { - // empty -} - -//------------------------------------------------------------------------------ -class Locator -MeshLoaderBase::Locator() const { - return this->setup.Locator; -} - -} // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Gfx/MeshLoaderBase.h b/code/Modules/Gfx/MeshLoaderBase.h deleted file mode 100644 index 60e93971e..000000000 --- a/code/Modules/Gfx/MeshLoaderBase.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::MeshLoaderBase - @ingroup Gfx - @brief base class for Gfx mesh loaders -*/ -#include "Resource/ResourceLoader.h" -#include "Gfx/GfxTypes.h" -#include - -namespace Oryol { - -class MeshLoaderBase : public ResourceLoader { - OryolClassDecl(MeshLoaderBase); -public: - /// optional callback when loading has succeeded - typedef std::function LoadedFunc; - - /// constructor - MeshLoaderBase(const MeshSetup& setup); - /// constructor with success callback - MeshLoaderBase(const MeshSetup& setup, LoadedFunc onLoaded); - /// return resource locator - virtual class Locator Locator() const override; - -protected: - MeshSetup setup; - std::function onLoaded; -}; - -} // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Gfx/README.md b/code/Modules/Gfx/README.md index 1ab602904..3a1112e97 100644 --- a/code/Modules/Gfx/README.md +++ b/code/Modules/Gfx/README.md @@ -10,14 +10,14 @@ management. ### Platform/API support matrix -Platform |GL3.3|GLES3|Metal|D3D11 ------------|-----|-----|-----|----- -OSX 10.11+ |YES |--- |YES |--- -iOS 9.x+ |--- |YES |YES |--- -Window7+ |YES |--- |--- |YES -Linux |YES |--- |--- |--- -Android |--- |YES |--- |--- -HTML5 |--- |YES |--- |--- +Platform |GL3.3|GLES2|GLES3|Metal|D3D11 +-----------|-----|-----|-----|-----|----- +OSX 10.11+ |YES |--- |--- |YES |--- +iOS 9.x+ |--- |YES |YES |YES |--- +Window7+ |YES |--- |--- |--- |YES +Linux |YES |--- |--- |--- |--- +Android |--- |YES |YES |--- |--- +HTML5 |--- |YES |YES |--- |--- RaspberryPi|--- |YES |--- |--- ### Sample Code diff --git a/code/Modules/Gfx/TextureLoaderBase.cc b/code/Modules/Gfx/TextureLoaderBase.cc deleted file mode 100644 index 6bb5f0aff..000000000 --- a/code/Modules/Gfx/TextureLoaderBase.cc +++ /dev/null @@ -1,29 +0,0 @@ -//------------------------------------------------------------------------------ -// TextureLoaderBase.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "TextureLoaderBase.h" - -namespace Oryol { - -//------------------------------------------------------------------------------ -TextureLoaderBase::TextureLoaderBase(const TextureSetup& setup_) : -setup(setup_) { - // empty -} - -//------------------------------------------------------------------------------ -TextureLoaderBase::TextureLoaderBase(const TextureSetup& setup_, LoadedFunc loadedFunc) : -setup(setup_), -onLoaded(loadedFunc) -{ - // empty -} - -//------------------------------------------------------------------------------ -class Locator -TextureLoaderBase::Locator() const { - return this->setup.Locator; -} - -} // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Gfx/TextureLoaderBase.h b/code/Modules/Gfx/TextureLoaderBase.h deleted file mode 100644 index bd15d1950..000000000 --- a/code/Modules/Gfx/TextureLoaderBase.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::TextureLoaderBase - @ingroup Gfx - @brief base class for Gfx texture loaders -*/ -#include "Resource/ResourceLoader.h" -#include "Gfx/GfxTypes.h" -#include - -namespace Oryol { - -class TextureLoaderBase : public ResourceLoader { - OryolClassDecl(TextureLoaderBase); -public: - /// optional callback when loading has succeeded - typedef std::function LoadedFunc; - - /// constructor - TextureLoaderBase(const TextureSetup& setup); - /// constructor - TextureLoaderBase(const TextureSetup& setup, LoadedFunc onLoaded); - /// return resource locator - virtual class Locator Locator() const override; - -protected: - TextureSetup setup; - std::function onLoaded; -}; - -} // namespace Oryol - diff --git a/code/Modules/Gfx/UnitTests/DDSLoadTest.cc b/code/Modules/Gfx/UnitTests/DDSLoadTest.cc deleted file mode 100644 index ccd77bcc0..000000000 --- a/code/Modules/Gfx/UnitTests/DDSLoadTest.cc +++ /dev/null @@ -1,146 +0,0 @@ -//------------------------------------------------------------------------------ -// DDSLoadTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Core/Core.h" -#include "Core/RunLoop.h" -#include "HttpFS/HTTPFileSystem.h" -#include "IO/IO.h" -#define GLIML_ASSERT o_assert -#include "gliml.h" -#include -#include - -using namespace Oryol; - -TEST(DDSLoadTest) { -#if !ORYOL_EMSCRIPTEN && !ORYOL_UNITTESTS_HEADLESS - Core::Setup(); - - // setup an IO facade, and associate http: with the HTTPFileSystem - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - IO::Setup(ioSetup); - - // DXT1 - Ptr req = IO::LoadFile("http://floooh.github.com/oryol/data/lok_dxt1.dds"); - while (!req->Handled) { - Core::PreRunLoop()->Run(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - CHECK(req->Status == IOStatus::OK); - if (req->Status == IOStatus::OK) { - - // parse DDS data with gliml - const int size = req->Data.Size(); - const void* data = req->Data.Data(); - CHECK(gliml::is_dds(data, size)); - gliml::context ctx; - ctx.enable_dxt(true); - CHECK(ctx.load_dds(data, size)); - CHECK(ctx.error() == GLIML_SUCCESS); - CHECK(ctx.texture_target() == GLIML_GL_TEXTURE_2D); - CHECK(ctx.is_compressed()); - CHECK(ctx.is_2d()); - CHECK(!ctx.is_3d()); - CHECK(ctx.num_faces() == 1); - CHECK(ctx.num_mipmaps(0) == 9); - CHECK(ctx.image_target(0) == GLIML_GL_TEXTURE_2D); - CHECK(ctx.image_internal_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); - CHECK(ctx.image_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); - CHECK(ctx.image_type() == 0); - int w = 256, h = 256; - for (int i = 0; i < 9; i++) { - CHECK(ctx.image_width(0, i) == (w >> i)); - CHECK(ctx.image_height(0, i) == (h >> i)); - CHECK(ctx.image_depth(0, i) == 1); - int mipSize = (w>>(2+i)) * (h>>(2+i)) * 8; - if (mipSize < 8) mipSize = 8; - CHECK(ctx.image_size(0, i) == mipSize); - } - } - - // DXT3 - req = IO::LoadFile("http://floooh.github.com/oryol/data/lok_dxt3.dds"); - while (!req->Handled) { - Core::PreRunLoop()->Run(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - CHECK(req->Status == IOStatus::OK); - if (req->Status == IOStatus::OK) { - - // parse DDS data with gliml - const int size = req->Data.Size(); - const void* data = req->Data.Data(); - CHECK(gliml::is_dds(data, size)); - gliml::context ctx; - ctx.enable_dxt(true); - CHECK(ctx.load_dds(data, size)); - CHECK(ctx.error() == GLIML_SUCCESS); - CHECK(ctx.texture_target() == GLIML_GL_TEXTURE_2D); - CHECK(ctx.is_compressed()); - CHECK(ctx.is_2d()); - CHECK(!ctx.is_3d()); - CHECK(ctx.num_faces() == 1); - CHECK(ctx.num_mipmaps(0) == 9); - CHECK(ctx.image_target(0) == GLIML_GL_TEXTURE_2D); - CHECK(ctx.image_internal_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); - CHECK(ctx.image_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); - CHECK(ctx.image_type() == 0); - int w = 256, h = 256; - for (int i = 0; i < 9; i++) { - CHECK(ctx.image_width(0, i) == (w >> i)); - CHECK(ctx.image_height(0, i) == (h >> i)); - CHECK(ctx.image_depth(0, i) == 1); - int mipSize = (w>>(2+i)) * (h>>(2+i)) * 16; - if (mipSize < 8) mipSize = 16; - CHECK(ctx.image_size(0, i) == mipSize); - } - } - - // DXT5 - req = IO::LoadFile("http://floooh.github.com/oryol/data/lok_dxt5.dds"); - while (!req->Handled) { - Core::PreRunLoop()->Run(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - CHECK(req->Status == IOStatus::OK); - if (req->Status == IOStatus::OK) { - - // parse DDS data with gliml - const int size = req->Data.Size(); - const void* data = req->Data.Data(); - CHECK(gliml::is_dds(data, size)); - gliml::context ctx; - ctx.enable_dxt(true); - CHECK(ctx.load_dds(data, size)); - CHECK(ctx.error() == GLIML_SUCCESS); - CHECK(ctx.texture_target() == GLIML_GL_TEXTURE_2D); - CHECK(ctx.is_compressed()); - CHECK(ctx.is_2d()); - CHECK(!ctx.is_3d()); - CHECK(ctx.num_faces() == 1); - CHECK(ctx.num_mipmaps(0) == 9); - CHECK(ctx.image_target(0) == GLIML_GL_TEXTURE_2D); - CHECK(ctx.image_internal_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); - CHECK(ctx.image_format() == GLIML_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); - CHECK(ctx.image_type() == 0); - int w = 256, h = 256; - for (int i = 0; i < 9; i++) { - CHECK(ctx.image_width(0, i) == (w >> i)); - CHECK(ctx.image_height(0, i) == (h >> i)); - CHECK(ctx.image_depth(0, i) == 1); - int mipSize = (w>>(2+i)) * (h>>(2+i)) * 16; - if (mipSize < 8) mipSize = 16; - CHECK(ctx.image_size(0, i) == mipSize); - } - } - - // FIXME: RGBA, LUM - - req.invalidate(); - IO::Discard(); - Core::Discard(); -#endif -} diff --git a/code/Modules/Gfx/UnitTests/MeshFactoryTest.cc b/code/Modules/Gfx/UnitTests/MeshFactoryTest.cc deleted file mode 100644 index 58d59bc9b..000000000 --- a/code/Modules/Gfx/UnitTests/MeshFactoryTest.cc +++ /dev/null @@ -1,116 +0,0 @@ -//------------------------------------------------------------------------------ -// MeshFactoryTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/private/renderer.h" -#include "Gfx/private/gfxFactory.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/displayMgr.h" -#include "Assets/Gfx/MeshBuilder.h" - -#if ORYOL_OPENGL -#include "Gfx/private/gl/gl_impl.h" -#endif - -using namespace Oryol; -using namespace _priv; - -// NOTE: this is should not be treated as sample code on how -// to initialize a mesh! -TEST(MeshFactoryTest) { - - #if !ORYOL_UNITTESTS_HEADLESS - // setup a GL context - auto gfxSetup = GfxSetup::Window(400, 300, "Oryol Test"); - displayMgr displayManager; - class renderer renderer; - texturePool texPool; - meshPool meshPool; - - gfxPointers ptrs; - ptrs.displayMgr = &displayManager; - ptrs.renderer = &renderer; - ptrs.texturePool = &texPool; - ptrs.meshPool = &meshPool; - - displayManager.SetupDisplay(gfxSetup, ptrs); - - // setup a meshFactory object - renderer.setup(gfxSetup, ptrs); - gfxFactory factory; - factory.setup(ptrs); - - // setup a MeshBuilder and create mesh geometry - MeshBuilder mb; - mb.NumVertices = 4; - mb.NumIndices = 6; - mb.Layout - .Add(VertexAttr::Position, VertexFormat::Float3) - .Add(VertexAttr::TexCoord0, VertexFormat::Float2); - mb.PrimitiveGroups.Add(0, 6); - mb.Begin() - .Vertex(0, VertexAttr::Position, 0.0f, 0.0f, 0.0f) // top-left - .Vertex(1, VertexAttr::Position, 1.0f, 0.0f, 0.0f) // top-right - .Vertex(2, VertexAttr::Position, 1.0f, 1.0f, 0.0f) // bottom-right - .Vertex(3, VertexAttr::Position, 0.0f, 1.0f, 0.0f) // bottom-left - .Vertex(0, VertexAttr::TexCoord0, 0.0f, 0.0f) - .Vertex(1, VertexAttr::TexCoord0, 1.0f, 0.0f) - .Vertex(2, VertexAttr::TexCoord0, 1.0f, 1.0f) - .Vertex(3, VertexAttr::TexCoord0, 0.0f, 1.0f) - .Triangle(0, 0, 1, 2) - .Triangle(1, 0, 2, 3); - auto buildResult = mb.Build(); - - // setup the mesh - mesh mesh; - mesh.Setup = buildResult.Setup; - const void* data = buildResult.Data.Data(); - const int size = buildResult.Data.Size(); - - factory.initMesh(mesh, data, size); - CHECK(!mesh.Id.IsValid()); - CHECK(mesh.Setup.Locator == Locator::NonShared()); - CHECK(mesh.vertexBufferAttrs.NumVertices == 4); - CHECK(mesh.vertexBufferAttrs.BufferUsage == Usage::Immutable); - CHECK(mesh.vertexBufferAttrs.Layout.NumComponents() == 2); - CHECK(mesh.vertexBufferAttrs.Layout.ByteSize() == 20); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentByteOffset(0) == 0); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentByteOffset(1) == 12); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(0).IsValid()); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(0).Attr == VertexAttr::Position); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(0).Format == VertexFormat::Float3); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(0).ByteSize() == 12); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(1).IsValid()); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(1).Attr == VertexAttr::TexCoord0); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(1).Format == VertexFormat::Float2); - CHECK(mesh.vertexBufferAttrs.Layout.ComponentAt(1).ByteSize() == 8); - CHECK(mesh.indexBufferAttrs.NumIndices == 6); - CHECK(mesh.indexBufferAttrs.Type == IndexType::Index16); - CHECK(mesh.indexBufferAttrs.BufferUsage == Usage::Immutable); - CHECK(mesh.indexBufferAttrs.ByteSize() == 12); - CHECK(mesh.numPrimGroups == 1); - CHECK(mesh.primGroups[0].BaseElement == 0); - CHECK(mesh.primGroups[0].NumElements == 6); - #if ORYOL_OPENGL - CHECK(mesh.buffers[mesh::vb].glBuffers[0] != 0); - CHECK(mesh.buffers[mesh::ib].glBuffers[0] != 0); - #endif - - factory.destroyMesh(mesh); - CHECK(!mesh.Id.IsValid()); - CHECK(mesh.vertexBufferAttrs.NumVertices == 0); - CHECK(mesh.vertexBufferAttrs.BufferUsage == Usage::InvalidUsage); - CHECK(mesh.vertexBufferAttrs.Layout.NumComponents() == 0); - CHECK(mesh.vertexBufferAttrs.Layout.ByteSize() == 0); - CHECK(mesh.indexBufferAttrs.NumIndices == 0); - CHECK(mesh.indexBufferAttrs.Type == IndexType::InvalidIndexType); - CHECK(mesh.indexBufferAttrs.BufferUsage == Usage::InvalidUsage); - CHECK(mesh.numPrimGroups == 0); - factory.discard(); - renderer.discard(); - displayManager.DiscardDisplay(); - - #endif -} diff --git a/code/Modules/Gfx/UnitTests/MeshSetupTest.cc b/code/Modules/Gfx/UnitTests/MeshSetupTest.cc deleted file mode 100644 index bd0d28878..000000000 --- a/code/Modules/Gfx/UnitTests/MeshSetupTest.cc +++ /dev/null @@ -1,104 +0,0 @@ -//------------------------------------------------------------------------------ -// MeshSetupTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/GfxTypes.h" - -using namespace Oryol; - -TEST(MeshSetupTest) { - - const MeshSetup s0 = MeshSetup::FromFile("s0"); - CHECK(s0.ShouldSetupFromFile()); - CHECK(!s0.ShouldSetupFromData()); - CHECK(!s0.ShouldSetupEmpty()); - CHECK(s0.Locator == "s0"); - CHECK(s0.VertexUsage == Usage::Immutable); - CHECK(s0.IndexUsage == Usage::Immutable); - CHECK(s0.NumVertices == 0); - CHECK(s0.NumIndices == 0); - CHECK(s0.Layout.Empty()); - CHECK(s0.IndicesType == IndexType::None); - - const MeshSetup s1 = MeshSetup::FromFile("s1"); - CHECK(s1.ShouldSetupFromFile()); - CHECK(!s1.ShouldSetupFromData()); - CHECK(!s1.ShouldSetupEmpty()); - CHECK(s1.Locator == "s1"); - CHECK(s1.VertexUsage == Usage::Immutable); - CHECK(s1.IndexUsage == Usage::Immutable); - - /* FIXME: needs blueprint setup - const MeshSetup s2 = MeshSetup::FromFile("s2", s1); - CHECK(s2.ShouldSetupFromFile()); - CHECK(!s2.ShouldSetupFromStream()); - CHECK(!s2.ShouldSetupEmpty()); - CHECK(s2.Locator == "s2"); - CHECK(s2.VertexUsage == Usage::Stream); - CHECK(s2.IndexUsage == Usage::Dynamic); - */ - - const MeshSetup s3 = MeshSetup::FromData(); - CHECK(!s3.ShouldSetupFromFile()); - CHECK(!s3.ShouldSetupEmpty()); - CHECK(s3.ShouldSetupFromData()); - CHECK(s3.Locator == Locator::NonShared()); - CHECK(s3.VertexUsage == Usage::Immutable); - CHECK(s3.IndexUsage == Usage::Immutable); - - const MeshSetup s4 = MeshSetup::FromData(Usage::Stream, Usage::Dynamic); - CHECK(!s4.ShouldSetupFromFile()); - CHECK(!s4.ShouldSetupEmpty()); - CHECK(s4.ShouldSetupFromData()); - CHECK(s4.Locator == Locator::NonShared()); - CHECK(s4.VertexUsage == Usage::Stream); - CHECK(s4.IndexUsage == Usage::Dynamic); - - const MeshSetup s5 = MeshSetup::FromData(s4); - CHECK(!s5.ShouldSetupFromFile()); - CHECK(!s5.ShouldSetupEmpty()); - CHECK(s5.ShouldSetupFromData()); - CHECK(s5.Locator == Locator::NonShared()); - CHECK(s5.VertexUsage == Usage::Stream); - CHECK(s5.IndexUsage == Usage::Dynamic); - - MeshSetup s6 = MeshSetup::Empty(128, Usage::Stream); - s6.Layout - .Add(VertexAttr::Position, VertexFormat::Float3) - .Add(VertexAttr::TexCoord0, VertexFormat::Float2); - CHECK(!s6.ShouldSetupFromFile()); - CHECK(!s6.ShouldSetupFromData()); - CHECK(s6.ShouldSetupEmpty()); - CHECK(s6.Locator == Locator::NonShared()); - CHECK(s6.VertexUsage == Usage::Stream); - CHECK(s6.IndexUsage == Usage::InvalidUsage); - CHECK(s6.NumVertices == 128); - CHECK(s6.NumIndices == 0); - CHECK(s6.IndicesType == IndexType::None); - CHECK(s6.Layout.NumComponents() == 2); - CHECK(s6.Layout.ComponentAt(0).Attr == VertexAttr::Position); - CHECK(s6.Layout.ComponentAt(0).Format == VertexFormat::Float3); - CHECK(s6.Layout.ComponentAt(1).Attr == VertexAttr::TexCoord0); - CHECK(s6.Layout.ComponentAt(1).Format == VertexFormat::Float2); - - MeshSetup s7 = MeshSetup::Empty(256, Usage::Dynamic, IndexType::Index16, 512, Usage::Stream); - s7.Layout - .Add(VertexAttr::Position, VertexFormat::Float3) - .Add(VertexAttr::TexCoord0, VertexFormat::Float2); - s7.AddPrimitiveGroup(PrimitiveGroup(0, 64)); - CHECK(!s7.ShouldSetupFromFile()); - CHECK(!s7.ShouldSetupFromData()); - CHECK(s7.ShouldSetupEmpty()); - CHECK(s7.Locator == Locator::NonShared()); - CHECK(s7.VertexUsage == Usage::Dynamic); - CHECK(s7.IndexUsage == Usage::Stream); - CHECK(s7.NumVertices == 256); - CHECK(s7.NumIndices == 512); - CHECK(s7.IndicesType == IndexType::Index16); - CHECK(s7.Layout.NumComponents() == 2); - CHECK(s7.NumPrimitiveGroups() == 1); - CHECK(s7.PrimitiveGroup(0).BaseElement == 0); - CHECK(s7.PrimitiveGroup(0).NumElements == 64); -} - diff --git a/code/Modules/Gfx/UnitTests/RenderEnumsTest.cc b/code/Modules/Gfx/UnitTests/RenderEnumsTest.cc deleted file mode 100644 index 7d2cf205f..000000000 --- a/code/Modules/Gfx/UnitTests/RenderEnumsTest.cc +++ /dev/null @@ -1,204 +0,0 @@ -//------------------------------------------------------------------------------ -// RenderEnumsTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Core/String/String.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/gl/gl_impl.h" -#include - -using namespace Oryol; - -//------------------------------------------------------------------------------ -TEST(PixelFormatChannelBitsTest) { - - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Red) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Green) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Blue) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Alpha) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA8, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Red) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Green) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Blue) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGB8, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Red) == 5); - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Green) == 6); - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Blue) == 5); - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R5G6B5, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Red) == 5); - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Green) == 5); - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Blue) == 5); - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Alpha) == 1); - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R5G5B5A1, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Red) == 4); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Green) == 4); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Blue) == 4); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Alpha) == 4); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA4, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Red) == 8); - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Green) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Blue) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::L8, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Red) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Green) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Blue) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Depth) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTH, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Red) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Green) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Blue) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Depth) == 24); - CHECK(PixelFormat::NumBits(PixelFormat::DEPTHSTENCIL, PixelChannel::Stencil) == 8); - - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Red) == 32); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Green) == 32); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Blue) == 32); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Alpha) == 32); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA32F, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Red) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Green) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Blue) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Alpha) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::RGBA16F, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Red) == 32); - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Green) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Blue) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R32F, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Red) == 16); - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Green) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Blue) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Alpha) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R16F, PixelChannel::Stencil) == 0); - - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Red) == 10); - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Green) == 10); - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Blue) == 10); - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Alpha) == 2); - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Depth) == 0); - CHECK(PixelFormat::NumBits(PixelFormat::R10G10B10A2, PixelChannel::Stencil) == 0); - - // all other pixel formats must return 0 for all channels - for (uint32_t pf = 0; pf < PixelFormat::NumPixelFormats; pf++) { - if ((pf != PixelFormat::RGBA8) && - (pf != PixelFormat::RGB8) && - (pf != PixelFormat::R5G6B5) && - (pf != PixelFormat::R5G5B5A1) && - (pf != PixelFormat::RGBA4) && - (pf != PixelFormat::L8) && - (pf != PixelFormat::DEPTH) && - (pf != PixelFormat::DEPTHSTENCIL) && - (pf != PixelFormat::RGBA32F) && - (pf != PixelFormat::RGBA16F) && - (pf != PixelFormat::R16F) && - (pf != PixelFormat::R32F) && - (pf != PixelFormat::R10G10B10A2)) { - std::array channels = { {PixelChannel::Red, PixelChannel::Green, PixelChannel::Blue, PixelChannel::Alpha, PixelChannel::Depth, PixelChannel::Stencil} }; - for (PixelChannel::Bits chn : channels) { - CHECK(PixelFormat::NumBits((PixelFormat::Code)pf, chn) == 0); - } - } - } -} - -//------------------------------------------------------------------------------ -TEST(PixelFormatByteSizeTest) { - CHECK(PixelFormat::ByteSize(PixelFormat::RGBA8) == 4); - CHECK(PixelFormat::ByteSize(PixelFormat::RGB8) == 3); - CHECK(PixelFormat::ByteSize(PixelFormat::R5G6B5) == 2); - CHECK(PixelFormat::ByteSize(PixelFormat::R5G5B5A1) == 2); - CHECK(PixelFormat::ByteSize(PixelFormat::RGBA4) == 2); - CHECK(PixelFormat::ByteSize(PixelFormat::L8) == 1); - CHECK(PixelFormat::ByteSize(PixelFormat::DEPTH) == 2); - CHECK(PixelFormat::ByteSize(PixelFormat::DEPTHSTENCIL) == 4); - CHECK(PixelFormat::ByteSize(PixelFormat::RGBA32F) == 16); - CHECK(PixelFormat::ByteSize(PixelFormat::RGBA16F) == 8); - CHECK(PixelFormat::ByteSize(PixelFormat::R16F) == 2); - CHECK(PixelFormat::ByteSize(PixelFormat::R32F) == 4); - CHECK(PixelFormat::ByteSize(PixelFormat::R10G10B10A2) == 4); -} - -//------------------------------------------------------------------------------ -TEST(VertexFormatTest) { - CHECK(VertexFormat::NumVertexFormats == 13); - - CHECK(VertexFormat::ByteSize(VertexFormat::Float) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::Float2) == 8); - CHECK(VertexFormat::ByteSize(VertexFormat::Float3) == 12); - CHECK(VertexFormat::ByteSize(VertexFormat::Float4) == 16); - CHECK(VertexFormat::ByteSize(VertexFormat::Byte4) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::Byte4N) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::UByte4) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::UByte4N) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::Short2) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::Short2N) == 4); - CHECK(VertexFormat::ByteSize(VertexFormat::Short4) == 8); - CHECK(VertexFormat::ByteSize(VertexFormat::Short4N) == 8); - CHECK(VertexFormat::ByteSize(VertexFormat::UInt10_2N) == 4); -} - -//------------------------------------------------------------------------------ -TEST(VertexAttrTest) { - CHECK(VertexAttr::NumVertexAttrs == 16); - - CHECK(String(VertexAttr::ToString(VertexAttr::Position)) == "position"); - CHECK(String(VertexAttr::ToString(VertexAttr::Normal)) == "normal"); - CHECK(String(VertexAttr::ToString(VertexAttr::TexCoord0)) == "texcoord0"); - CHECK(String(VertexAttr::ToString(VertexAttr::TexCoord1)) == "texcoord1"); - CHECK(String(VertexAttr::ToString(VertexAttr::TexCoord2)) == "texcoord2"); - CHECK(String(VertexAttr::ToString(VertexAttr::TexCoord3)) == "texcoord3"); - CHECK(String(VertexAttr::ToString(VertexAttr::Tangent)) == "tangent"); - CHECK(String(VertexAttr::ToString(VertexAttr::Binormal)) == "binormal"); - CHECK(String(VertexAttr::ToString(VertexAttr::Weights)) == "weights"); - CHECK(String(VertexAttr::ToString(VertexAttr::Indices)) == "indices"); - CHECK(String(VertexAttr::ToString(VertexAttr::Color0)) == "color0"); - CHECK(String(VertexAttr::ToString(VertexAttr::Color1)) == "color1"); - CHECK(String(VertexAttr::ToString(VertexAttr::Instance0)) == "instance0"); - CHECK(String(VertexAttr::ToString(VertexAttr::Instance1)) == "instance1"); - CHECK(String(VertexAttr::ToString(VertexAttr::Instance2)) == "instance2"); - CHECK(String(VertexAttr::ToString(VertexAttr::Instance3)) == "instance3"); - - CHECK(VertexAttr::FromString("position") == VertexAttr::Position); - CHECK(VertexAttr::FromString("normal") == VertexAttr::Normal); - CHECK(VertexAttr::FromString("texcoord0") == VertexAttr::TexCoord0); - CHECK(VertexAttr::FromString("texcoord1") == VertexAttr::TexCoord1); - CHECK(VertexAttr::FromString("texcoord2") == VertexAttr::TexCoord2); - CHECK(VertexAttr::FromString("texcoord3") == VertexAttr::TexCoord3); - CHECK(VertexAttr::FromString("tangent") == VertexAttr::Tangent); - CHECK(VertexAttr::FromString("binormal") == VertexAttr::Binormal); - CHECK(VertexAttr::FromString("weights") == VertexAttr::Weights); - CHECK(VertexAttr::FromString("indices") == VertexAttr::Indices); - CHECK(VertexAttr::FromString("color0") == VertexAttr::Color0); - CHECK(VertexAttr::FromString("color1") == VertexAttr::Color1); - CHECK(VertexAttr::FromString("instance0") == VertexAttr::Instance0); - CHECK(VertexAttr::FromString("instance1") == VertexAttr::Instance1); - CHECK(VertexAttr::FromString("instance2") == VertexAttr::Instance2); - CHECK(VertexAttr::FromString("instance3") == VertexAttr::Instance3); -} - diff --git a/code/Modules/Gfx/UnitTests/RenderSetupTest.cc b/code/Modules/Gfx/UnitTests/RenderSetupTest.cc deleted file mode 100644 index 0aa80282f..000000000 --- a/code/Modules/Gfx/UnitTests/RenderSetupTest.cc +++ /dev/null @@ -1,41 +0,0 @@ -//------------------------------------------------------------------------------ -// RenderSetupTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Core/Core.h" -#include "Gfx/Gfx.h" - -using namespace Oryol; - -//------------------------------------------------------------------------------ -TEST(RenderSetupTest) { - - #if !ORYOL_UNITTESTS_HEADLESS - Core::Setup(); - Gfx::Setup(GfxSetup::Window(400, 300, "Oryol Test")); - - CHECK(Gfx::GfxSetup().Width == 400); - CHECK(Gfx::GfxSetup().Height == 300); - CHECK(Gfx::GfxSetup().ColorFormat == PixelFormat::RGBA8); - CHECK(Gfx::GfxSetup().DepthFormat == PixelFormat::DEPTHSTENCIL); - CHECK(Gfx::GfxSetup().Title == "Oryol Test"); - CHECK(Gfx::GfxSetup().Windowed == true); - - CHECK(Gfx::DisplayAttrs().WindowWidth >= 400); - CHECK(Gfx::DisplayAttrs().WindowHeight >= 300); - CHECK(Gfx::DisplayAttrs().WindowPosX >= 0); - CHECK(Gfx::DisplayAttrs().WindowPosY >= 0); - CHECK(Gfx::DisplayAttrs().FramebufferWidth >= 400); - CHECK(Gfx::DisplayAttrs().FramebufferHeight >= 300); - CHECK(Gfx::DisplayAttrs().ColorPixelFormat == PixelFormat::RGBA8); - CHECK(Gfx::DisplayAttrs().DepthPixelFormat == PixelFormat::DEPTHSTENCIL); - CHECK(Gfx::DisplayAttrs().SwapInterval == 1); - CHECK(Gfx::DisplayAttrs().WindowTitle == "Oryol Test"); - CHECK(Gfx::DisplayAttrs().Windowed == true); - - Gfx::Discard(); - Core::Discard(); - #endif -} - diff --git a/code/Modules/Gfx/UnitTests/ShapeBuilderTest.h b/code/Modules/Gfx/UnitTests/ShapeBuilderTest.h deleted file mode 100644 index 0519ecba6..000000000 --- a/code/Modules/Gfx/UnitTests/ShapeBuilderTest.h +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/code/Modules/Gfx/UnitTests/TestShaderLibrary.glsl b/code/Modules/Gfx/UnitTests/TestShaderLibrary.glsl deleted file mode 100644 index 98fa52fdc..000000000 --- a/code/Modules/Gfx/UnitTests/TestShaderLibrary.glsl +++ /dev/null @@ -1,53 +0,0 @@ -//------------------------------------------------------------------------------ -// test.shd -//------------------------------------------------------------------------------ - -/* -Bla Bla Bla -*/ - -@block TransformUtil -vec4 myTransform(mat4 mvp, vec4 pos) -{ - return mvp * pos; -} -@end - -@block FSUtil -vec4 myColor(vec4 nrm, vec4 texColor) -{ - return ((nrm * 0.5) + 0.5) * 0.75 + texColor * texColor * texColor; -} -@end - -@vs MyVertexShader -@include TransformUtil -uniform vsParams { - mat4 mvp; -}; -in vec4 position; -in vec4 normal; -in vec2 texcoord0; -out vec4 nrm; -out vec2 uv; -void main() { - gl_Position = myTransform(mvp, position); - nrm = normal; - uv = texcoord0; -} -@end - -@fs MyFragmentShader -@include FSUtil -uniform sampler2D tex; -in vec4 nrm; -in vec2 uv; -out vec4 fragColor; -void main() { - vec4 texColor = texture(tex, uv * vec2(5.0, 3.0)); - fragColor = myColor(nrm, texColor); -} -@end - -@program MyShader MyVertexShader MyFragmentShader - diff --git a/code/Modules/Gfx/UnitTests/TextureFactoryTest.cc b/code/Modules/Gfx/UnitTests/TextureFactoryTest.cc deleted file mode 100644 index cab53b80c..000000000 --- a/code/Modules/Gfx/UnitTests/TextureFactoryTest.cc +++ /dev/null @@ -1,95 +0,0 @@ -//------------------------------------------------------------------------------ -// TextureFactoryTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/private/gfxFactory.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/renderer.h" - -#if ORYOL_OPENGL -#include "Gfx/private/gl/gl_impl.h" -#endif - -using namespace Oryol; -using namespace _priv; - -TEST(RenderTargetCreationTest) { - - #if !ORYOL_UNITTESTS_HEADLESS - // setup a GL context - auto gfxSetup = GfxSetup::Window(400, 300, "Oryol Test"); - displayMgr displayManager; - class renderer renderer; - texturePool texPool; - meshPool meshPool; - - gfxPointers ptrs; - ptrs.displayMgr = &displayManager; - ptrs.renderer = &renderer; - ptrs.texturePool = &texPool; - ptrs.meshPool = &meshPool; - - displayManager.SetupDisplay(gfxSetup, ptrs); - - // setup a meshFactory object - renderer.setup(gfxSetup, ptrs); - gfxFactory factory; - factory.setup(ptrs); - - // create a render target (no depth buffer) - auto texSetup = TextureSetup::RenderTarget2D(320, 256); - texSetup.ColorFormat = PixelFormat::RGBA8; - texture tex0; - tex0.Setup = texSetup; - factory.initTexture(tex0, nullptr, 0); - CHECK(tex0.glTextures[0] != 0); - CHECK(tex0.glDepthRenderbuffer == 0); - const TextureAttrs& attrs0 = tex0.textureAttrs; - CHECK(attrs0.Locator == Locator::NonShared()); - CHECK(attrs0.Type == TextureType::Texture2D); - CHECK(attrs0.ColorFormat == PixelFormat::RGBA8); - CHECK(attrs0.DepthFormat == PixelFormat::InvalidPixelFormat); - CHECK(attrs0.TextureUsage == Usage::Immutable); - CHECK(attrs0.Width == 320); - CHECK(attrs0.Height == 256); - CHECK(attrs0.Depth == 1); - CHECK(1 == attrs0.NumMipMaps); - CHECK(attrs0.IsRenderTarget); - CHECK(!attrs0.HasDepthBuffer); - - // create a render target with depth buffer - auto rtSetup = TextureSetup::RenderTarget2D(640, 480); - rtSetup.ColorFormat = PixelFormat::RGBA8; - rtSetup.DepthFormat = PixelFormat::DEPTHSTENCIL; - texture tex1; - tex1.Setup = rtSetup; - factory.initTexture(tex1, nullptr, 0); - CHECK(tex1.glTextures[0] != 0); - CHECK(tex1.glDepthRenderbuffer != 0); - const TextureAttrs& attrs1 = tex1.textureAttrs; - CHECK(attrs1.Locator == Locator::NonShared()); - CHECK(attrs1.Type == TextureType::Texture2D); - CHECK(attrs1.ColorFormat == PixelFormat::RGBA8); - CHECK(attrs1.DepthFormat == PixelFormat::DEPTHSTENCIL); - CHECK(attrs1.TextureUsage == Usage::Immutable); - CHECK(attrs1.Width == 640); - CHECK(attrs1.Height == 480); - CHECK(attrs1.Depth == 1); - CHECK(1 == attrs1.NumMipMaps); - CHECK(attrs1.IsRenderTarget); - CHECK(attrs1.HasDepthBuffer); - - // cleanup - factory.destroyTexture(tex1); - CHECK(tex1.glTextures[0] == 0); - CHECK(tex1.glDepthRenderbuffer == 0); - - factory.destroyTexture(tex0); - factory.discard(); - renderer.discard(); - displayManager.DiscardDisplay(); - #endif -} - diff --git a/code/Modules/Gfx/UnitTests/TextureSetupTest.cc b/code/Modules/Gfx/UnitTests/TextureSetupTest.cc deleted file mode 100644 index 16aff4207..000000000 --- a/code/Modules/Gfx/UnitTests/TextureSetupTest.cc +++ /dev/null @@ -1,48 +0,0 @@ -//------------------------------------------------------------------------------ -// TextureSetupTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/GfxTypes.h" - -using namespace Oryol; - -TEST(TextureSetupTest) { - - // setup as absolute-size render target, no depth buffer - auto rt = TextureSetup::RenderTarget2D(320, 256); - rt.Locator = Locator("absSize"); - rt.ColorFormat = PixelFormat::RGB8; - CHECK(!rt.ShouldSetupFromFile()); - CHECK(!rt.ShouldSetupFromPixelData()); - CHECK(rt.IsRenderTarget); - CHECK(!rt.HasDepth()); - CHECK(rt.Locator.Location() == "absSize"); - CHECK(rt.Width == 320); - CHECK(rt.Height == 256); - CHECK(rt.ColorFormat == PixelFormat::RGB8); - CHECK(rt.DepthFormat == PixelFormat::InvalidPixelFormat); - CHECK(rt.Sampler.WrapU == TextureWrapMode::ClampToEdge); - CHECK(rt.Sampler.WrapV == TextureWrapMode::ClampToEdge); - CHECK(rt.Sampler.MagFilter == TextureFilterMode::Nearest); - CHECK(rt.Sampler.MinFilter == TextureFilterMode::Nearest); - - // setup as absolute-size render target, with depth buffer - auto rt0 = TextureSetup::RenderTarget2D(320, 256); - rt0.ColorFormat = PixelFormat::RGBA8; - rt0.DepthFormat = PixelFormat::DEPTHSTENCIL; - CHECK(!rt0.ShouldSetupFromFile()); - CHECK(!rt0.ShouldSetupFromPixelData()); - CHECK(rt0.IsRenderTarget); - CHECK(rt0.HasDepth()); - CHECK(rt0.Locator == Locator::NonShared()); - CHECK(rt0.Width == 320); - CHECK(rt0.Height == 256); - CHECK(rt0.ColorFormat == PixelFormat::RGBA8); - CHECK(rt0.DepthFormat == PixelFormat::DEPTHSTENCIL); - CHECK(rt0.Sampler.WrapU == TextureWrapMode::ClampToEdge); - CHECK(rt0.Sampler.WrapV == TextureWrapMode::ClampToEdge); - CHECK(rt0.Sampler.MagFilter == TextureFilterMode::Nearest); - CHECK(rt0.Sampler.MinFilter == TextureFilterMode::Nearest); -} - diff --git a/code/Modules/Gfx/UnitTests/VertexLayoutTest.cc b/code/Modules/Gfx/UnitTests/VertexLayoutTest.cc deleted file mode 100644 index 1797df4c2..000000000 --- a/code/Modules/Gfx/UnitTests/VertexLayoutTest.cc +++ /dev/null @@ -1,70 +0,0 @@ -//------------------------------------------------------------------------------ -// VertexLayoutTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/GfxTypes.h" - -using namespace Oryol; - -TEST(VertexLayoutTest) { - VertexLayout layout; - CHECK(layout.Empty()); - CHECK(0 == layout.NumComponents()); - - layout.Add(VertexAttr::Position, VertexFormat::Float3) - .Add(VertexAttr::Normal, VertexFormat::UByte4N) - .Add(VertexLayout::Component(VertexAttr::TexCoord0, VertexFormat::Float2)); - CHECK(!layout.Empty()); - CHECK(3 == layout.NumComponents()); - - const VertexLayout::Component& comp0 = layout.ComponentAt(0); - CHECK(comp0.Attr == VertexAttr::Position); - CHECK(comp0.Format == VertexFormat::Float3); - CHECK(comp0.ByteSize() == 12); - const VertexLayout::Component& comp1 = layout.ComponentAt(1); - CHECK(comp1.Attr == VertexAttr::Normal); - CHECK(comp1.Format == VertexFormat::UByte4N); - CHECK(comp1.ByteSize() == 4); - const VertexLayout::Component& comp2 = layout.ComponentAt(2); - CHECK(comp2.Attr == VertexAttr::TexCoord0); - CHECK(comp2.Format == VertexFormat::Float2); - CHECK(comp2.ByteSize() == 8); - - CHECK(layout.ByteSize() == 24); - CHECK(layout.ComponentByteOffset(0) == 0); - CHECK(layout.ComponentByteOffset(1) == 12); - CHECK(layout.ComponentByteOffset(2) == 16); - - CHECK(layout.ComponentIndexByVertexAttr(VertexAttr::Position) == 0); - CHECK(layout.ComponentIndexByVertexAttr(VertexAttr::Normal) == 1); - CHECK(layout.ComponentIndexByVertexAttr(VertexAttr::TexCoord0) == 2); - CHECK(layout.ComponentIndexByVertexAttr(VertexAttr::TexCoord1) == InvalidIndex); - CHECK(layout.Contains(VertexAttr::Position)); - CHECK(layout.Contains(VertexAttr::Normal)); - CHECK(layout.Contains(VertexAttr::TexCoord0)); - CHECK(!layout.Contains(VertexAttr::TexCoord1)); - - layout.Clear(); - CHECK(layout.Empty()); - CHECK(0 == layout.NumComponents()); - - VertexLayout l0({ - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }); - CHECK(l0.ComponentIndexByVertexAttr(VertexAttr::Position) == 0); - CHECK(l0.ComponentIndexByVertexAttr(VertexAttr::Normal) == 1); - CHECK(l0.ComponentIndexByVertexAttr(VertexAttr::TexCoord0) == 2); - - VertexLayout l1; - l1.Add({ - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Tangent, VertexFormat::Byte4N }, - }).EnableInstancing(); - CHECK(l1.ComponentIndexByVertexAttr(VertexAttr::Position) == 0); - CHECK(l1.ComponentIndexByVertexAttr(VertexAttr::Tangent) == 1); - CHECK(l1.StepFunction == VertexStepFunction::PerInstance); -} - diff --git a/code/Modules/Gfx/UnitTests/glTypesTest.cc b/code/Modules/Gfx/UnitTests/glTypesTest.cc deleted file mode 100644 index cd8eb9508..000000000 --- a/code/Modules/Gfx/UnitTests/glTypesTest.cc +++ /dev/null @@ -1,38 +0,0 @@ -//------------------------------------------------------------------------------ -// glTypesTest.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "UnitTest++/src/UnitTest++.h" -#include "Gfx/private/gl/gl_impl.h" -#include "Gfx/private/gl/glTypes.h" - -using namespace Oryol; -using namespace _priv; - -//------------------------------------------------------------------------------ -TEST(glTypesTest) { - - // glTexImage format - // FIXME: incomplete - CHECK(glTypes::asGLTexImageFormat(PixelFormat::RGBA8) == GL_RGBA); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::RGB8) == GL_RGB); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::R5G6B5) == GL_RGB); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::R5G5B5A1) == GL_RGBA); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::RGBA4) == GL_RGBA); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::RGBA32F) == GL_RGBA); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::RGBA16F) == GL_RGBA); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::DEPTH) == GL_DEPTH_COMPONENT); - CHECK(glTypes::asGLTexImageFormat(PixelFormat::DEPTHSTENCIL) == GL_DEPTH_STENCIL); - - // glTexImage type - // FIXME: incomplete - CHECK(glTypes::asGLTexImageType(PixelFormat::RGBA8) == GL_UNSIGNED_BYTE); - CHECK(glTypes::asGLTexImageType(PixelFormat::RGB8) == GL_UNSIGNED_BYTE); - CHECK(glTypes::asGLTexImageType(PixelFormat::R5G6B5) == GL_UNSIGNED_SHORT_5_6_5); - CHECK(glTypes::asGLTexImageType(PixelFormat::R5G5B5A1) == GL_UNSIGNED_SHORT_5_5_5_1); - CHECK(glTypes::asGLTexImageType(PixelFormat::RGBA4) == GL_UNSIGNED_SHORT_4_4_4_4); - CHECK(glTypes::asGLTexImageType(PixelFormat::DEPTH) == GL_UNSIGNED_SHORT); - CHECK(glTypes::asGLTexImageType(PixelFormat::RGBA32F) == GL_FLOAT); - CHECK(glTypes::asGLTexImageType(PixelFormat::RGBA16F) == GL_HALF_FLOAT); - CHECK(glTypes::asGLTexImageType(PixelFormat::DEPTHSTENCIL) == GL_UNSIGNED_INT_24_8); -} diff --git a/code/Modules/Gfx/doc/RenderLoop.md b/code/Modules/Gfx/doc/RenderLoop.md index 53f56d441..c8dd024d0 100644 --- a/code/Modules/Gfx/doc/RenderLoop.md +++ b/code/Modules/Gfx/doc/RenderLoop.md @@ -38,7 +38,7 @@ public: OryolMain(SimpleApp); AppState::Code SimpleApp::OnInit() { - Gfx::Setup(GfxSetup::Window(640, 480, "Window Title")); + Gfx::Setup(GfxDesc().Width(640).Height(480).Title("Window Title")); return App::OnInit(); } @@ -81,11 +81,11 @@ OryolMain(SimpleApp); In OnInit(), the call to **Gfx::Setup()** initializes the entire Gfx module, it opens a window, and initializes the underlying 3D-API. The method takes a -**GfxSetup** object with setup parameters (more on that later). +**GfxDesc** object with setup parameters (more on that later). ```cpp AppState::Code SimpleApp::OnInit() { - Gfx::Setup(GfxSetup::Window(640, 480, "Window Title")); + Gfx::Setup(GfxDesc().Width(640).Height(480).Title("Window Title")); return App::OnInit(); } ``` @@ -134,118 +134,87 @@ AppState::Code SimpleApp::OnCleanup() { The call to Gfx::Discard() shuts down the Gfx module, in a more complex application this would also clean up all the left-over rendering resources. -### The GfxSetup object +### The GfxDesc object -The GfxSetup object provides setup parameters to the Gfx module, most +The GfxDesc object provides setup parameters to the Gfx module, most importantly the window size, color- and depth-buffer formats, and the sample count for multisample anti-aliasing. -For the most common setup methods, a small set of construction methods -exist: +The GfxDesc class uses method chaining to override the default parameters: ```cpp -class GfxSetup { -public: - /// shortcut for windowed mode (with RGBA8, 24+8 stencil/depth, no MSAA) - static GfxSetup Window(int width, int height, String windowTitle); - /// shortcut for fullscreen mode (with RGBA8, 24+8 stencil/depth, no MSAA) - static GfxSetup Fullscreen(int width, int height, String windowTitle); - /// shortcut for windowed mode with 4xMSAA (with RGBA8, 24+8 stencil/depth) - static GfxSetup WindowMSAA4(int width, int height, String windowTitle); - /// shortcut for fullscreen mode with 4xMSAA (with RGBA8, 24+8 stencil/depth) - static GfxSetup FullscreenMSAA4(int width, int height, String windowTitle); -... -} +auto gfxDesc = GfxDesc() + .Width(1024) + .Height(768) + .ColorFormat(PixelFormat::RGBA8) + .DepthFormat(PixelFormat::None) + .SampleCount(4) + .Windowed(true) + .Title("My Window Title); +Gfx::Setup(gfxDesc); ``` -You can also set or override the most common setup parameters manually: +The method chaining approach also lets you move the entire GfxDesc object +initialization into the Gfx::Setup() method call: ```cpp -GfxSetup setup; -setup.Width = 1024; -setup.Height = 768; -setup.ColorFormat = PixelFormat::RGBA8; -setup.DepthFormat = PixelFormat::None; -setup.SampleCount = 4; -setup.Windowed = true; -setup.Title = "My Window Title" -Gfx::Setup(setup); +Gfx::Setup(GfxDesc() + .Width(1024) + .Height(768) + .ColorFormat(PixelFormat::RGBA8) + .DepthFormat(PixelFormat::None) + .SampleCount(4) + .Windowed(true) + .Title("My Window Title)); ``` On platforms which support HighDPI rendering (e.g. Windows, macOS, iOS), rendering happens by default at half resolution if a HighDPI display is -detected. To render at full resolution set the GfxSetup::HighDPI member to -true: - -```cpp -GfxSetup setup; -... -setup.HighDPI = true; -... -Gfx::Setup(setup); -``` - -You can override the default 'pass action' (what should happen in BeginPass) -via the GfxSetup::DefaultPassAction member. For instance if you want to -clear the default framebuffer with color red instead of black: +detected. To render at full resolution set the GfxDesc::HighDPI attribute +to true: ```cpp -GfxSetup setup; -setup.DefaultPassAction = PassAction::Clear(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); -... -``` -...you can also change the clear values for the depth/stencil buffer from -the default 1.0 and 0: - -```cpp -GfxSetup setup; -setup.DefaultPassAction = PassAction::Clear(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f), 0.5f, 255); -... +Gfx::Setup(GfxDesc() + ... + .HighDPI(true) + ...); ``` -More information on render pass actions [is provided here](RenderPasses.md). - -Finally there's a number of GfxSetup members which tweak various +There's a number of GfxDesc members which tweak various Gfx resource-system upper bounds. When starting with Oryol you don't need to care about those values yet, but tweaking those values will be useful for very big apps (which use a lot of resources), or very small app which only use a handful resources. -Just for completeness, here are the GfxSetup members for resource -system tweaking: +Just for completeness, here are the GfxDesc members for resource +system tweaking and their default values: ```cpp /// resource pool size by resource type -StaticArray ResourcePoolSize; -/// resource creation throttling (max resources created async per frame) -StaticArray ResourceThrottling; +ResourcePoolSize(GfxResourceType::Code type, int size): default=128 /// initial resource label stack capacity -int ResourceLabelStackCapacity = 256; +ResourceLabelStackCapacity(int capacity): default=256 /// initial resource registry capacity -int ResourceRegistryCapacity = 256; +ResourceRegistryCapacity(int capacity): default=256 /// size of the global uniform buffer (only relevant on some platforms) -int GlobalUniformBufferSize = GfxConfig::DefaultGlobalUniformBufferSize; -/// max number of drawcalls per frame (only relevant on some platforms) -int MaxDrawCallsPerFrame = GfxConfig::DefaultMaxDrawCallsPerFrame; -/// max number of ApplyDrawState per frame (only relevant on some platforms) -int MaxApplyDrawStatesPerFrame = GfxConfig::DefaultMaxApplyDrawStatesPerFrame; +GfxDesc& GlobalUniformBufferSize(int sizeInBytes): default=4MB ``` ### The special HTML5 'canvas tracking' mode -There are 2 special GfxSetup members useful for HTML5 apps: +There are 2 special GfxDesc members useful for HTML5 apps: ```cpp /// if true, ignore own size and instead track size of an HTML element (emscripten only) -bool HtmlTrackElementSize = false; -/// name of the HTML element to track (default: #canvas) -String HtmlElement = "#canvas"; +HtmlTrackElementSize(bool track): default = false +/// name of the HTML element to track +HtmlElement(const StringAtom& domName): default = "canvas" ``` -If **GfxSetup::HtmlTrackElementSize** is set to true, the size members -GfxSetup::Width and GfxSetup::Height are ignored, and the Oryol Gfx module +If **GfxDesc::HtmlTrackElementSize** is set to true, the size members +GfxDesc::Width and GfxDesc::Height are ignored, and the Oryol Gfx module will instead track the size of the HTML DOM element identified by -**GfxSetup::HtmlElement** (the default value '#canvas' automatically +**GfxDesc::HtmlElement** (the default value '#canvas' automatically resolves to the WebGL canvas managed by the emscripten GL wrapper) . This is useful for HTML5 apps where the WebGL canvas size is fully controlled by Javascript code. The Oryol Gfx module needs to know when such changes take diff --git a/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.cc b/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.cc index c6c08ed48..3e01bf332 100644 --- a/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.cc +++ b/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.cc @@ -3,35 +3,41 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "d3d11DisplayMgr.h" -#include "d3d11Types.h" -#include "Gfx/private/renderer.h" - #ifndef UNICODE #define UNICODE #endif -#include "d3d11_impl.h" +#include namespace Oryol { namespace _priv { static DXGI_SWAP_CHAIN_DESC dxgiSwapChainDesc; +d3d11DisplayMgr* d3d11DisplayMgr::ptr = nullptr; + +//------------------------------------------------------------------------------ +d3d11DisplayMgr::d3d11DisplayMgr() { + o_assert(!ptr); + ptr = this; +} //------------------------------------------------------------------------------ d3d11DisplayMgr::~d3d11DisplayMgr() { if (this->IsDisplayValid()) { this->DiscardDisplay(); } + o_assert(ptr); + ptr = nullptr; } //------------------------------------------------------------------------------ void -d3d11DisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { +d3d11DisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); Memory::Clear(&dxgiSwapChainDesc, sizeof(dxgiSwapChainDesc)); - winDisplayMgr::SetupDisplay(setup, ptrs, " (D3D11)"); + winDisplayMgr::SetupDisplay(desc, " (D3D11)"); this->createDeviceAndSwapChain(); const DisplayAttrs& attrs = this->displayAttrs; - this->createDefaultRenderTarget(attrs.FramebufferWidth, attrs.FramebufferHeight); + this->createDefaultRenderTarget(attrs.Width, attrs.Height); } //------------------------------------------------------------------------------ @@ -47,7 +53,7 @@ d3d11DisplayMgr::DiscardDisplay() { void d3d11DisplayMgr::Present() { o_assert_dbg(this->dxgiSwapChain); - this->dxgiSwapChain->Present(this->gfxSetup.SwapInterval, 0); + this->dxgiSwapChain->Present(this->gfxDesc.swapInterval, 0); } //------------------------------------------------------------------------------ @@ -64,17 +70,17 @@ d3d11DisplayMgr::createDeviceAndSwapChain() { #endif Memory::Clear(&dxgiSwapChainDesc, sizeof(dxgiSwapChainDesc)); - dxgiSwapChainDesc.BufferDesc.Width = this->displayAttrs.FramebufferWidth; - dxgiSwapChainDesc.BufferDesc.Height = this->displayAttrs.FramebufferHeight; - dxgiSwapChainDesc.BufferDesc.Format = d3d11Types::asSwapChainFormat(this->gfxSetup.ColorFormat); + dxgiSwapChainDesc.BufferDesc.Width = this->displayAttrs.Width; + dxgiSwapChainDesc.BufferDesc.Height = this->displayAttrs.Height; + dxgiSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; dxgiSwapChainDesc.BufferDesc.RefreshRate.Numerator = 60; dxgiSwapChainDesc.BufferDesc.RefreshRate.Denominator = 1; dxgiSwapChainDesc.OutputWindow = this->hwnd; - dxgiSwapChainDesc.Windowed = this->gfxSetup.Windowed; + dxgiSwapChainDesc.Windowed = this->gfxDesc.windowed; dxgiSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; dxgiSwapChainDesc.BufferCount = 1; - dxgiSwapChainDesc.SampleDesc.Count = this->gfxSetup.SampleCount; - dxgiSwapChainDesc.SampleDesc.Quality = this->gfxSetup.SampleCount > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; + dxgiSwapChainDesc.SampleDesc.Count = this->gfxDesc.sampleCount; + dxgiSwapChainDesc.SampleDesc.Quality = this->gfxDesc.sampleCount > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; dxgiSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; dxgiSwapChainDesc.Flags = 0; @@ -135,14 +141,13 @@ d3d11DisplayMgr::createDefaultRenderTarget(int width, int height) { o_assert_dbg(this->d3d11RenderTargetView); // setup depth/stencil buffer (if required) - if (PixelFormat::None != this->gfxSetup.DepthFormat) { - + if (PixelFormat::None != this->gfxDesc.depthFormat) { D3D11_TEXTURE2D_DESC depthStencilDesc = { }; depthStencilDesc.Width = width; depthStencilDesc.Height = height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; - depthStencilDesc.Format = d3d11Types::asRenderTargetFormat(this->gfxSetup.DepthFormat); + depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc = dxgiSwapChainDesc.SampleDesc; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; @@ -154,7 +159,7 @@ d3d11DisplayMgr::createDefaultRenderTarget(int width, int height) { D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = { }; dsvDesc.Format = depthStencilDesc.Format; - if (this->gfxSetup.SampleCount > 1) { + if (this->gfxDesc.sampleCount > 1) { dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; } else { @@ -195,12 +200,11 @@ d3d11DisplayMgr::onWindowDidResize() { // resize the DXGI framebuffer (this requires that all state is unbound) if (this->dxgiSwapChain) { - const int newWidth = this->displayAttrs.FramebufferWidth; - const int newHeight = this->displayAttrs.FramebufferHeight; + const int newWidth = this->displayAttrs.Width; + const int newHeight = this->displayAttrs.Height; - this->pointers.renderer->resetStateCache(); this->destroyDefaultRenderTarget(); - DXGI_FORMAT d3d11Fmt = d3d11Types::asSwapChainFormat(this->gfxSetup.ColorFormat); + DXGI_FORMAT d3d11Fmt = DXGI_FORMAT_R8G8B8A8_UNORM; HRESULT hr = this->dxgiSwapChain->ResizeBuffers(1, newWidth, newHeight, d3d11Fmt, 0); o_assert(SUCCEEDED(hr)); this->createDefaultRenderTarget(newWidth, newHeight); diff --git a/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.h b/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.h index 498f5d504..a75d8720b 100644 --- a/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.h +++ b/code/Modules/Gfx/private/d3d11/d3d11DisplayMgr.h @@ -5,19 +5,30 @@ @ingroup _priv @brief display manager implementation for D3D11 */ -#include "Gfx/private/win/winDisplayMgr.h" -#include "Gfx/private/d3d11/d3d11_decl.h" +#include "Gfx/private/d3d11/winDisplayMgr.h" + +// d3d11 forward declarations +struct IDXGISwapChain; +struct ID3D11Device; +struct ID3D11DeviceContext; +struct ID3D11Texture2D; +struct ID3D11RenderTargetView; +struct ID3D11DepthStencilView; namespace Oryol { namespace _priv { class d3d11DisplayMgr : public winDisplayMgr { public: + /// constructor + d3d11DisplayMgr(); /// destructor ~d3d11DisplayMgr(); + /// static singleton ptr + static d3d11DisplayMgr* ptr; /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// present the current rendered frame @@ -38,6 +49,15 @@ class d3d11DisplayMgr : public winDisplayMgr { /// pointer to default default depth/stencil view ID3D11DepthStencilView* d3d11DepthStencilView = nullptr; + /// static callback function to get current render-target-view as void* + static const void* d3d11GetRenderTargetView() { + return d3d11DisplayMgr::ptr->d3d11RenderTargetView; + } + /// static callback function to get current depth-stencil view as void* + static const void* d3d11GetDepthStencilView() { + return d3d11DisplayMgr::ptr->d3d11DepthStencilView; + } + /// create swap chain and d3d device void createDeviceAndSwapChain(); /// destroy the d3d device and swap chain diff --git a/code/Modules/Gfx/private/d3d11/d3d11Factory.cc b/code/Modules/Gfx/private/d3d11/d3d11Factory.cc deleted file mode 100644 index 42745d8ad..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Factory.cc +++ /dev/null @@ -1,778 +0,0 @@ -//------------------------------------------------------------------------------ -// d3d11Factory.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "d3d11Factory.h" -#include "d3d11Types.h" -#include "d3d11_impl.h" -#include "Gfx/private/resource.h" -#include "Gfx/private/renderer.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -void -d3d11Factory::setup(const gfxPointers& ptrs) { - o_assert_dbg(!this->subResourceData); - gfxFactoryBase::setup(ptrs); - this->subResourceData = (D3D11_SUBRESOURCE_DATA*) Memory::Alloc(sizeof(D3D11_SUBRESOURCE_DATA) * maxNumSubResourceData); -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::discard() { - o_assert_dbg(this->subResourceData); - Memory::Free(this->subResourceData); - this->subResourceData = nullptr; - gfxFactoryBase::discard(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initMesh(mesh& msh, const void* data, int size) { - o_assert_dbg(this->isValid); - if (msh.Setup.ShouldSetupFullScreenQuad()) { - return this->initFullscreenQuad(msh); - } - else { - return this->initStdMesh(msh, data, size); - } -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::destroyMesh(mesh& msh) { - this->pointers.renderer->invalidateMeshState(); - if (msh.d3d11VertexBuffer) { - msh.d3d11VertexBuffer->Release(); - } - if (msh.d3d11IndexBuffer) { - msh.d3d11IndexBuffer->Release(); - } - msh.Clear(); -} - -//------------------------------------------------------------------------------ -ID3D11Buffer* -d3d11Factory::createBuffer(const void* data, uint32_t dataSize, uint32_t d3d11BindFlags, Usage::Code usage) { - o_assert_dbg((D3D11_BIND_VERTEX_BUFFER == d3d11BindFlags) || (D3D11_BIND_INDEX_BUFFER == d3d11BindFlags)); - ID3D11Device* d3d11Device = this->pointers.renderer->d3d11Device; - o_assert_dbg(d3d11Device); - - D3D11_BUFFER_DESC desc = { }; - desc.ByteWidth = dataSize; - desc.Usage = d3d11Types::asResourceUsage(usage); - desc.BindFlags = d3d11BindFlags; - desc.CPUAccessFlags = d3d11Types::asResourceCPUAccessFlag(usage); - - D3D11_SUBRESOURCE_DATA* initDataPtr = nullptr; - D3D11_SUBRESOURCE_DATA initData = { }; - if (data) { - initData.pSysMem = data; - initDataPtr = &initData; - } - ID3D11Buffer* buf = nullptr; - HRESULT hr = d3d11Device->CreateBuffer(&desc, initDataPtr, &buf); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(buf); - - return buf; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initFullscreenQuad(mesh& mesh) { - o_assert_dbg(nullptr == mesh.d3d11VertexBuffer); - o_assert_dbg(nullptr == mesh.d3d11IndexBuffer); - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = 4; - vbAttrs.BufferUsage = Usage::Immutable; - vbAttrs.Layout.Add(VertexAttr::Position, VertexFormat::Float3); - vbAttrs.Layout.Add(VertexAttr::TexCoord0, VertexFormat::Float2); - mesh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = 6; - ibAttrs.Type = IndexType::Index16; - ibAttrs.BufferUsage = Usage::Immutable; - mesh.indexBufferAttrs = ibAttrs; - - mesh.numPrimGroups = 1; - mesh.primGroups[0] = PrimitiveGroup(0, 6); - - // vertices - const float topV = mesh.Setup.FullScreenQuadFlipV ? 0.0f : 1.0f; - const float botV = mesh.Setup.FullScreenQuadFlipV ? 1.0f : 0.0f; - float vertices[] = { - -1.0f, +1.0f, 0.0f, 0.0f, topV, // top-left corner - +1.0f, +1.0f, 0.0f, 1.0f, topV, // top-right corner - +1.0f, -1.0f, 0.0f, 1.0f, botV, // bottom-right corner - -1.0f, -1.0f, 0.0f, 0.0f, botV // bottom-left corner - }; - - // indices - uint16_t indices[] = { - 0, 2, 1, // topleft -> bottomright -> topright - 0, 3, 2, // topleft -> bottomleft -> bottomright - }; - - // create vertex and index buffer - mesh.d3d11VertexBuffer = this->createBuffer(vertices, sizeof(vertices), D3D11_BIND_VERTEX_BUFFER, mesh.vertexBufferAttrs.BufferUsage); - mesh.d3d11IndexBuffer = this->createBuffer(indices, sizeof(indices), D3D11_BIND_INDEX_BUFFER, mesh.indexBufferAttrs.BufferUsage); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initStdMesh(mesh& mesh, const void* data, int size) { - o_assert_dbg(nullptr == mesh.d3d11VertexBuffer); - o_assert_dbg(nullptr == mesh.d3d11IndexBuffer); - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = mesh.Setup.NumVertices; - vbAttrs.Layout = mesh.Setup.Layout; - vbAttrs.BufferUsage = mesh.Setup.VertexUsage; - mesh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = mesh.Setup.NumIndices; - ibAttrs.Type = mesh.Setup.IndicesType; - ibAttrs.BufferUsage = mesh.Setup.IndexUsage; - mesh.indexBufferAttrs = ibAttrs; - - mesh.numPrimGroups = mesh.Setup.NumPrimitiveGroups(); - o_assert_dbg(mesh.numPrimGroups < GfxConfig::MaxNumPrimGroups); - for (int i = 0; i < mesh.numPrimGroups; i++) { - mesh.primGroups[i] = mesh.Setup.PrimitiveGroup(i); - } - - if (mesh.Setup.NumVertices > 0) { - const int vbSize = vbAttrs.NumVertices * vbAttrs.Layout.ByteSize(); - const uint8_t* vertices = nullptr; - if (InvalidIndex != mesh.Setup.VertexDataOffset) { - const uint8_t* ptr = (const uint8_t*)data; - o_assert_dbg(ptr && (size > 0)); - vertices = ptr + mesh.Setup.VertexDataOffset; - o_assert_dbg((ptr + size) >= (vertices + vbSize)); - } - mesh.d3d11VertexBuffer = this->createBuffer(vertices, vbSize, D3D11_BIND_VERTEX_BUFFER, vbAttrs.BufferUsage); - } - if (IndexType::None != ibAttrs.Type) { - const int ibSize = ibAttrs.NumIndices * IndexType::ByteSize(ibAttrs.Type); - const uint8_t* indices = nullptr; - if (InvalidIndex != mesh.Setup.IndexDataOffset) { - const uint8_t* ptr = (const uint8_t*)data; - o_assert_dbg(ptr && (size > 0)); - indices = ((const uint8_t*)ptr) + mesh.Setup.IndexDataOffset; - o_assert_dbg((ptr + size) >= (indices + ibSize)); - } - mesh.d3d11IndexBuffer = this->createBuffer(indices, ibSize, D3D11_BIND_INDEX_BUFFER, ibAttrs.BufferUsage); - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -static ID3D11ShaderResourceView* -createShaderResourceView(ID3D11Device* d3d11Device, const texture& tex, ID3D11Resource* d3d11Tex, DXGI_FORMAT d3d11Format) { - D3D11_SHADER_RESOURCE_VIEW_DESC shdResViewDesc = { }; - shdResViewDesc.Format = d3d11Format; - switch (tex.Setup.Type) { - case TextureType::Texture2D: - shdResViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - shdResViewDesc.Texture2D.MostDetailedMip = 0; - shdResViewDesc.Texture2D.MipLevels = tex.Setup.NumMipMaps; - break; - case TextureType::TextureCube: - shdResViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; - shdResViewDesc.TextureCube.MostDetailedMip = 0; - shdResViewDesc.TextureCube.MipLevels = tex.Setup.NumMipMaps; - break; - case TextureType::Texture3D: - shdResViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; - shdResViewDesc.Texture3D.MostDetailedMip = 0; - shdResViewDesc.Texture3D.MipLevels = tex.Setup.NumMipMaps; - break; - case TextureType::TextureArray: - shdResViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; - shdResViewDesc.Texture2DArray.MostDetailedMip = 0; - shdResViewDesc.Texture2DArray.MipLevels = tex.Setup.NumMipMaps; - shdResViewDesc.Texture2DArray.FirstArraySlice = 0; - shdResViewDesc.Texture2DArray.ArraySize = tex.Setup.Depth; - break; - default: - o_error("d3d11TextureFactory::createShaderResourceView: invalid texture type!\n"); - break; - } - ID3D11ShaderResourceView* d3d11ShaderResourceView = nullptr; - HRESULT hr = d3d11Device->CreateShaderResourceView(d3d11Tex, &shdResViewDesc, &d3d11ShaderResourceView); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != d3d11ShaderResourceView); - return d3d11ShaderResourceView; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initTexture(texture& tex, const void* data, int size) { - o_assert_dbg(this->subResourceData); - o_assert_dbg(!tex.d3d11Texture2D); - o_assert_dbg(!tex.d3d11Texture3D); - o_assert_dbg(!tex.d3d11ShaderResourceView); - o_assert_dbg(!tex.d3d11SamplerState); - o_assert_dbg(!tex.d3d11DepthStencilTexture); - o_assert_dbg(!tex.d3d11MSAATexture2D); - ID3D11Device* d3d11Device = this->pointers.renderer->d3d11Device; - - const TextureSetup& setup = tex.Setup; - o_assert_dbg(!setup.ShouldSetupFromNativeTexture()); - - // subresourcedata array if initial data is provided - D3D11_SUBRESOURCE_DATA* pInitialData = nullptr; - if (data) { - const uint8_t* srcPtr = (const uint8_t*)data; - const int numFaces = setup.Type==TextureType::TextureCube ? 6:1; - const int numSlices = setup.Type==TextureType::TextureArray ? setup.Depth:1; - const int sliceSize = size / numSlices; - o_assert_dbg((numSlices * sliceSize) == size); - const int numMipMaps = setup.ImageData.NumMipMaps; - int subResourceDataIndex = 0; - for (int faceIndex = 0; faceIndex < numFaces; faceIndex++) { - for (int sliceIndex = 0; sliceIndex < numSlices; sliceIndex++) { - int sliceOffset = sliceIndex * sliceSize; - for (int mipIndex = 0; mipIndex < numMipMaps; mipIndex++, subResourceDataIndex++) { - o_assert_dbg(subResourceDataIndex < maxNumSubResourceData); - D3D11_SUBRESOURCE_DATA& subResData = this->subResourceData[subResourceDataIndex]; - subResData.pSysMem = srcPtr + sliceOffset + setup.ImageData.Offsets[faceIndex][mipIndex]; - const int mipWidth = std::max(setup.Width >> mipIndex, 1); - const int mipHeight = std::max(setup.Height >> mipIndex, 1); - subResData.SysMemPitch = PixelFormat::RowPitch(setup.ColorFormat, mipWidth); - if (setup.Type == TextureType::Texture3D) { - const int mipDepth = std::max(setup.Depth >> mipIndex, 1); - subResData.SysMemSlicePitch = PixelFormat::ImagePitch(setup.ColorFormat, mipWidth, mipHeight); - } - else { - subResData.SysMemSlicePitch = 0; - } - } - } - } - pInitialData = this->subResourceData; - } - - // create the color texture - HRESULT hr; - if (setup.Type != TextureType::Texture3D) { - // setup texture desc for 2D texture - D3D11_TEXTURE2D_DESC texDesc = { }; - texDesc.Width = setup.Width; - texDesc.Height = setup.Height; - texDesc.MipLevels = setup.NumMipMaps; - if (TextureType::TextureArray == setup.Type) { - texDesc.ArraySize = setup.Depth; - } - else if (TextureType::TextureCube == setup.Type) { - texDesc.ArraySize = 6; - } - else { - texDesc.ArraySize = 1; - } - if (setup.IsRenderTarget) { - texDesc.Format = tex.d3d11ColorFormat = d3d11Types::asRenderTargetFormat(setup.ColorFormat); - o_assert2_dbg(texDesc.Format != DXGI_FORMAT_UNKNOWN, "Invalid render target pixel format!"); - texDesc.Usage = D3D11_USAGE_DEFAULT; - texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_RENDER_TARGET; - texDesc.CPUAccessFlags = 0; - } - else { - texDesc.Format = tex.d3d11ColorFormat = d3d11Types::asTextureFormat(setup.ColorFormat); - if (DXGI_FORMAT_UNKNOWN == texDesc.Format) { - o_warn("d3d11TextureFactory: texture pixel format not supported in D3D11\n"); - return ResourceState::Failed; - } - texDesc.Usage = d3d11Types::asResourceUsage(setup.TextureUsage); - texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - texDesc.CPUAccessFlags = d3d11Types::asResourceCPUAccessFlag(setup.TextureUsage); - } - texDesc.SampleDesc.Count = 1; - texDesc.SampleDesc.Quality = 0; - texDesc.MiscFlags = setup.Type == TextureType::TextureCube ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; - hr = d3d11Device->CreateTexture2D(&texDesc, pInitialData, &tex.d3d11Texture2D); - o_assert(SUCCEEDED(hr)); - - // also create an MSAA texture? - if (setup.SampleCount > 1) { - texDesc.SampleDesc.Count = setup.SampleCount; - texDesc.SampleDesc.Quality = 0; - hr = d3d11Device->CreateTexture2D(&texDesc, pInitialData, &tex.d3d11MSAATexture2D); - o_assert(SUCCEEDED(hr)); - } - - // shader-resource-view (always on the non-MSAA texture) - tex.d3d11ShaderResourceView = createShaderResourceView(d3d11Device, tex, tex.d3d11Texture2D, texDesc.Format); - o_assert(tex.d3d11ShaderResourceView); - } - else { - // 3D texture - D3D11_TEXTURE3D_DESC texDesc = { }; - texDesc.Width = setup.Width; - texDesc.Height = setup.Height; - texDesc.Depth = setup.Depth; - texDesc.MipLevels = setup.NumMipMaps; - if (setup.IsRenderTarget) { - texDesc.Format = tex.d3d11ColorFormat = d3d11Types::asRenderTargetFormat(setup.ColorFormat); - o_assert2_dbg(texDesc.Format != DXGI_FORMAT_UNKNOWN, "Invalid render target color pixel format!"); - texDesc.Usage = D3D11_USAGE_DEFAULT; - texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - texDesc.CPUAccessFlags = 0; - } - else { - texDesc.Format = tex.d3d11ColorFormat = d3d11Types::asTextureFormat(setup.ColorFormat); - o_assert2_dbg(texDesc.Format != DXGI_FORMAT_UNKNOWN, "Invalid texture pixel format!"); - texDesc.Usage = d3d11Types::asResourceUsage(setup.TextureUsage); - texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - texDesc.CPUAccessFlags = d3d11Types::asResourceCPUAccessFlag(setup.TextureUsage); - } - texDesc.MiscFlags = 0; - hr = d3d11Device->CreateTexture3D(&texDesc, pInitialData, &tex.d3d11Texture3D); - o_assert(SUCCEEDED(hr)); - tex.d3d11ShaderResourceView = createShaderResourceView(d3d11Device, tex, tex.d3d11Texture3D, texDesc.Format); - o_assert(tex.d3d11ShaderResourceView); - } - - // optional depth-stencil-buffer texture - if (setup.IsRenderTarget && (setup.DepthFormat != PixelFormat::InvalidPixelFormat)) { - // create depth-buffer-texture - D3D11_TEXTURE2D_DESC dsDesc = {}; - dsDesc.Width = setup.Width; - dsDesc.Height = setup.Height; - dsDesc.MipLevels = setup.NumMipMaps; - dsDesc.ArraySize = 1; - dsDesc.Format = d3d11Types::asRenderTargetFormat(setup.DepthFormat); - o_assert2_dbg(dsDesc.Format != DXGI_FORMAT_UNKNOWN, "Invalid render target depth pixel format!"); - dsDesc.Usage = D3D11_USAGE_DEFAULT; - dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - dsDesc.CPUAccessFlags = 0; - dsDesc.SampleDesc.Count = setup.SampleCount; - dsDesc.SampleDesc.Quality = 0; - dsDesc.MiscFlags = 0; - hr = d3d11Device->CreateTexture2D(&dsDesc, nullptr, &tex.d3d11DepthStencilTexture); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != tex.d3d11DepthStencilTexture); - } - - // create sampler object - D3D11_SAMPLER_DESC smpDesc = { }; - smpDesc.Filter = d3d11Types::asSamplerFilter(tex.Setup.Sampler.MagFilter, tex.Setup.Sampler.MinFilter); - smpDesc.AddressU = d3d11Types::asTextureAddressMode(tex.Setup.Sampler.WrapU); - smpDesc.AddressV = d3d11Types::asTextureAddressMode(tex.Setup.Sampler.WrapV); - smpDesc.AddressW = d3d11Types::asTextureAddressMode(tex.Setup.Sampler.WrapW); - smpDesc.MipLODBias = 0.0f; - smpDesc.MaxAnisotropy = 1; - smpDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; - smpDesc.BorderColor[0] = 1.0f; - smpDesc.BorderColor[1] = 1.0f; - smpDesc.BorderColor[2] = 1.0f; - smpDesc.BorderColor[3] = 1.0f; - smpDesc.MinLOD = -D3D11_FLOAT32_MAX; - smpDesc.MaxLOD = D3D11_FLOAT32_MAX; - ID3D11SamplerState* d3d11SamplerState = nullptr; - hr = d3d11Device->CreateSamplerState(&smpDesc, &tex.d3d11SamplerState); - o_assert(SUCCEEDED(hr)); - o_assert(tex.d3d11SamplerState); - - // fill texture attributes - o_assert_dbg(DXGI_FORMAT_UNKNOWN != tex.d3d11ColorFormat); - TextureAttrs attrs; - attrs.Locator = tex.Setup.Locator; - attrs.Type = tex.Setup.Type; - attrs.ColorFormat = tex.Setup.ColorFormat; - attrs.DepthFormat = tex.Setup.DepthFormat; - attrs.SampleCount = tex.Setup.SampleCount; - attrs.TextureUsage = tex.Setup.TextureUsage; - attrs.Width = tex.Setup.Width; - attrs.Height = tex.Setup.Height; - attrs.Depth = tex.Setup.Depth; - attrs.NumMipMaps = tex.Setup.NumMipMaps; - attrs.IsRenderTarget = tex.Setup.IsRenderTarget; - attrs.HasDepthBuffer = tex.Setup.HasDepth(); - tex.textureAttrs = attrs; - - // and done - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::destroyTexture(texture& tex) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateTextureState(); - if (tex.d3d11Texture2D) { - tex.d3d11Texture2D->Release(); - } - if (tex.d3d11Texture3D) { - tex.d3d11Texture3D->Release(); - } - if (tex.d3d11ShaderResourceView) { - tex.d3d11ShaderResourceView->Release(); - } - if (tex.d3d11SamplerState) { - tex.d3d11SamplerState->Release(); - } - if (tex.d3d11DepthStencilTexture) { - tex.d3d11DepthStencilTexture->Release(); - } - if (tex.d3d11MSAATexture2D) { - tex.d3d11MSAATexture2D->Release(); - } - tex.Clear(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - ID3D11Device* d3d11Device = this->pointers.renderer->d3d11Device; - - gfxFactoryBase::initRenderPass(rp); - o_assert_dbg(rp.colorTextures[0]); - const bool isMSAA = rp.colorTextures[0]->Setup.SampleCount > 1; - - // create render-target-view objects - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - o_assert_dbg(nullptr == rp.d3d11RenderTargetViews[i]); - texture* colorTex = rp.colorTextures[i]; - if (colorTex) { - D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = { }; - ID3D11Resource* d3d11Res = nullptr; - if (colorTex->d3d11MSAATexture2D) { - d3d11Res = colorTex->d3d11MSAATexture2D; - D3D11_TEXTURE2D_DESC texDesc = { }; - colorTex->d3d11MSAATexture2D->GetDesc(&texDesc); - rtvDesc.Format = texDesc.Format; - } - else if (colorTex->d3d11Texture2D) { - d3d11Res = colorTex->d3d11Texture2D; - D3D11_TEXTURE2D_DESC texDesc = { }; - colorTex->d3d11Texture2D->GetDesc(&texDesc); - rtvDesc.Format = texDesc.Format; - } - else { - d3d11Res = colorTex->d3d11Texture3D; - D3D11_TEXTURE3D_DESC texDesc = { }; - colorTex->d3d11Texture3D->GetDesc(&texDesc); - rtvDesc.Format = texDesc.Format; - } - o_assert_dbg(d3d11Res); - switch (colorTex->Setup.Type) { - case TextureType::Texture2D: - if (isMSAA) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; - } - else { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = rp.Setup.ColorAttachments[i].MipLevel; - } - break; - case TextureType::TextureCube: - if (isMSAA) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; - rtvDesc.Texture2DMSArray.FirstArraySlice = rp.Setup.ColorAttachments[i].Slice; - rtvDesc.Texture2DMSArray.ArraySize = 1; - } - else { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - rtvDesc.Texture2DArray.MipSlice = rp.Setup.ColorAttachments[i].MipLevel; - rtvDesc.Texture2DArray.FirstArraySlice = rp.Setup.ColorAttachments[i].Slice; - rtvDesc.Texture2DArray.ArraySize = 1; - } - break; - case TextureType::Texture3D: - o_assert_dbg(!isMSAA); - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; - rtvDesc.Texture3D.MipSlice = rp.Setup.ColorAttachments[i].MipLevel; - rtvDesc.Texture3D.FirstWSlice = rp.Setup.ColorAttachments[i].Slice; - rtvDesc.Texture3D.WSize = 1; - break; - case TextureType::TextureArray: - if (isMSAA) { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; - rtvDesc.Texture2DMSArray.FirstArraySlice = rp.Setup.ColorAttachments[i].Slice; - rtvDesc.Texture2DMSArray.ArraySize = 1; - } - else { - rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; - rtvDesc.Texture2DArray.MipSlice = rp.Setup.ColorAttachments[i].MipLevel; - rtvDesc.Texture2DArray.FirstArraySlice = rp.Setup.ColorAttachments[i].Slice; - rtvDesc.Texture2DArray.ArraySize = 1; - } - default: - o_error("d3d11RenderPassFactory: invalid texture type\n"); - break; - } - HRESULT hr = d3d11Device->CreateRenderTargetView(d3d11Res, &rtvDesc, &(rp.d3d11RenderTargetViews[i])); - o_assert(SUCCEEDED(hr) && rp.d3d11RenderTargetViews[i]); - } - } - - // create optional depth-stencil-view object - if (rp.depthStencilTexture) { - o_assert_dbg(nullptr == rp.d3d11DepthStencilView); - texture* dsTex = rp.depthStencilTexture; - o_assert_dbg(dsTex->d3d11DepthStencilTexture); - D3D11_DEPTH_STENCIL_VIEW_DESC dsDesc = { }; - D3D11_TEXTURE2D_DESC texDesc = { }; - dsTex->d3d11DepthStencilTexture->GetDesc(&texDesc); - dsDesc.Format = texDesc.Format; - dsDesc.Flags = 0; - if (isMSAA) { - dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; - dsDesc.Texture2D.MipSlice = 0; - } - else { - dsDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; - } - HRESULT hr = d3d11Device->CreateDepthStencilView(dsTex->d3d11DepthStencilTexture, &dsDesc, &rp.d3d11DepthStencilView); - o_assert(SUCCEEDED(hr) && rp.d3d11DepthStencilView); - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::destroyRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (rp.d3d11RenderTargetViews[i]) { - rp.d3d11RenderTargetViews[i]->Release(); - } - } - if (rp.d3d11DepthStencilView) { - rp.d3d11DepthStencilView->Release(); - } - gfxFactoryBase::destroyRenderPass(rp); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initShader(shader& shd) { - o_assert_dbg(this->isValid); - o_assert_dbg(nullptr == shd.d3d11VertexShader); - o_assert_dbg(nullptr == shd.d3d11PixelShader); - ID3D11Device* d3d11Device = this->pointers.renderer->d3d11Device; - - this->pointers.renderer->invalidateShaderState(); - const ShaderLang::Code slang = ShaderLang::HLSL5; - const ShaderSetup& setup = shd.Setup; - - // create vertex shader - const void* vsPtr = nullptr; - uint32_t vsSize = 0; - setup.VertexShaderByteCode(slang, vsPtr, vsSize); - o_assert_dbg(vsPtr); - HRESULT hr = d3d11Device->CreateVertexShader(vsPtr, vsSize, NULL, &shd.d3d11VertexShader); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(shd.d3d11VertexShader); - - // create pixel shader - const void* psPtr = nullptr; - uint32_t psSize = 0; - setup.FragmentShaderByteCode(slang, psPtr, psSize); - o_assert_dbg(psPtr); - hr = d3d11Device->CreatePixelShader(psPtr, psSize, NULL, &shd.d3d11PixelShader); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(shd.d3d11PixelShader); - - // create constant buffers - D3D11_BUFFER_DESC cbDesc = { }; - for (int i = 0; i < setup.NumUniformBlocks(); i++) { - const ShaderStage::Code bindStage = setup.UniformBlockBindStage(i); - const int bindSlot = setup.UniformBlockBindSlot(i); - o_assert_dbg(InvalidIndex != bindSlot); - const uint32 byteSize = setup.UniformBlockByteSize(i); - o_assert_dbg(byteSize > 0); - - // NOTE: constant buffer size must be multiple of 16 bytes - cbDesc.ByteWidth = Memory::RoundUp(byteSize, 16); - cbDesc.Usage = D3D11_USAGE_DEFAULT; - cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - cbDesc.CPUAccessFlags = 0; - - ID3D11Buffer* d3d11ConstantBuffer = nullptr; - hr = d3d11Device->CreateBuffer(&cbDesc, nullptr, &d3d11ConstantBuffer); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(d3d11ConstantBuffer); - - // the d3d11ConstantBuffer ptr can be 0 at this point, if the - // uniform block only contains textures - shd.addUniformBlockEntry(bindStage, bindSlot, d3d11ConstantBuffer); - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::destroyShader(shader& shd) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateShaderState(); - if (shd.d3d11VertexShader) { - shd.d3d11VertexShader->Release(); - } - if (shd.d3d11PixelShader) { - shd.d3d11PixelShader->Release(); - } - for (int bindStage = 0; bindStage < ShaderStage::NumShaderStages; bindStage++) { - for (int bindSlot = 0; bindSlot < GfxConfig::MaxNumUniformBlocksPerStage; bindSlot++) { - ID3D11Buffer* cb = shd.getConstantBuffer((ShaderStage::Code)bindStage, bindSlot); - if (cb) { - cb->Release(); - } - } - } - shd.Clear(); -} - -//------------------------------------------------------------------------------ -static ID3D11InputLayout* -createInputLayout(ID3D11Device* d3d11Device, const pipeline& pip) { - o_assert_dbg(nullptr == pip.d3d11InputLayout); - - // create a new input layout object and add to cache - D3D11_INPUT_ELEMENT_DESC d3d11Comps[VertexAttr::NumVertexAttrs] = { 0 }; - int d3d11CompIndex = 0; - for (int mshSlotIndex = 0; mshSlotIndex < GfxConfig::MaxNumInputMeshes; mshSlotIndex++) { - const VertexLayout& layout = pip.Setup.Layouts[mshSlotIndex]; - for (int compIndex = 0; compIndex < layout.NumComponents(); compIndex++, d3d11CompIndex++) { - const auto& comp = layout.ComponentAt(compIndex); - o_assert_dbg(d3d11CompIndex < VertexAttr::NumVertexAttrs); - D3D11_INPUT_ELEMENT_DESC& d3d11Comp = d3d11Comps[d3d11CompIndex]; - d3d11Comp.SemanticName = d3d11Types::asSemanticName(comp.Attr); - d3d11Comp.SemanticIndex = d3d11Types::asSemanticIndex(comp.Attr); - d3d11Comp.Format = d3d11Types::asInputElementFormat(comp.Format); - d3d11Comp.InputSlot = mshSlotIndex; - d3d11Comp.AlignedByteOffset = layout.ComponentByteOffset(compIndex); - d3d11Comp.InputSlotClass = d3d11Types::asInputClassification(layout.StepFunction); - if (VertexStepFunction::PerVertex == layout.StepFunction) { - d3d11Comp.InstanceDataStepRate = 0; - } - else { - d3d11Comp.InstanceDataStepRate = layout.StepRate; - } - } - } - - // lookup the vertex shader bytecode - const void* vsByteCode = nullptr; - uint32_t vsSize = 0; - pip.shd->Setup.VertexShaderByteCode(ShaderLang::HLSL5, vsByteCode, vsSize); - o_assert_dbg(vsByteCode && (vsSize > 0)); - - // create d3d11 input layout object - ID3D11InputLayout* d3d11InputLayout = nullptr; - HRESULT hr = d3d11Device->CreateInputLayout( - d3d11Comps, // pInputElementDesc - d3d11CompIndex, // NumElements - vsByteCode, // pShaderBytecodeWithInputSignature - vsSize, // BytecodeLength - &d3d11InputLayout); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != d3d11InputLayout); - return d3d11InputLayout; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -d3d11Factory::initPipeline(pipeline& pip) { - o_assert_dbg(nullptr == pip.d3d11InputLayout); - o_assert_dbg(nullptr == pip.d3d11RasterizerState); - o_assert_dbg(nullptr == pip.d3d11BlendState); - o_assert_dbg(nullptr == pip.d3d11DepthStencilState); - ID3D11Device* d3d11Device = this->pointers.renderer->d3d11Device; - HRESULT hr; - - gfxFactoryBase::initPipeline(pip); - o_assert_dbg(pip.shd); - - pip.d3d11InputLayout = createInputLayout(d3d11Device, pip); - pip.d3d11PrimTopology = d3d11Types::asPrimitiveTopology(pip.Setup.PrimType); - o_assert_dbg(pip.d3d11InputLayout); - - // create state objects (NOTE: creating the same state will return - // the same d3d11 state object, so no need to implement our own reuse) - const RasterizerState& rastState = pip.Setup.RasterizerState; - D3D11_RASTERIZER_DESC rastDesc = { }; - rastDesc.FillMode = D3D11_FILL_SOLID; - rastDesc.CullMode = d3d11Types::asCullMode(rastState.CullFaceEnabled, rastState.CullFace); - rastDesc.FrontCounterClockwise = FALSE; // OpenGL convention - rastDesc.DepthBias = 0; - rastDesc.DepthBiasClamp = 0.0f; - rastDesc.DepthClipEnable = TRUE; - rastDesc.ScissorEnable = rastState.ScissorTestEnabled; - rastDesc.MultisampleEnable = rastState.SampleCount > 1; - rastDesc.AntialiasedLineEnable = FALSE; - hr = d3d11Device->CreateRasterizerState(&rastDesc, &pip.d3d11RasterizerState); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != pip.d3d11RasterizerState); - - const DepthStencilState& dsState = pip.Setup.DepthStencilState; - D3D11_DEPTH_STENCIL_DESC dsDesc = { }; - dsDesc.DepthEnable = TRUE; // FIXME: have DepthTestEnable in RasterizerState? - dsDesc.DepthWriteMask = dsState.DepthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - dsDesc.DepthFunc = d3d11Types::asComparisonFunc(dsState.DepthCmpFunc); - dsDesc.StencilEnable = dsState.StencilEnabled; - dsDesc.StencilReadMask = dsState.StencilReadMask; - dsDesc.StencilWriteMask = dsState.StencilWriteMask; - dsDesc.FrontFace.StencilFailOp = d3d11Types::asStencilOp(dsState.StencilFront.FailOp); - dsDesc.FrontFace.StencilDepthFailOp = d3d11Types::asStencilOp(dsState.StencilFront.DepthFailOp); - dsDesc.FrontFace.StencilPassOp = d3d11Types::asStencilOp(dsState.StencilFront.PassOp); - dsDesc.FrontFace.StencilFunc = d3d11Types::asComparisonFunc(dsState.StencilFront.CmpFunc); - dsDesc.BackFace.StencilFailOp = d3d11Types::asStencilOp(dsState.StencilBack.FailOp); - dsDesc.BackFace.StencilDepthFailOp = d3d11Types::asStencilOp(dsState.StencilBack.DepthFailOp); - dsDesc.BackFace.StencilPassOp = d3d11Types::asStencilOp(dsState.StencilBack.PassOp); - dsDesc.BackFace.StencilFunc = d3d11Types::asComparisonFunc(dsState.StencilBack.CmpFunc); - hr = d3d11Device->CreateDepthStencilState(&dsDesc, &pip.d3d11DepthStencilState); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != pip.d3d11DepthStencilState); - - const BlendState& blendState = pip.Setup.BlendState; - o_assert(blendState.MRTCount < GfxConfig::MaxNumColorAttachments); - D3D11_BLEND_DESC blendDesc = { }; - blendDesc.AlphaToCoverageEnable = FALSE; - blendDesc.IndependentBlendEnable = FALSE; - blendDesc.RenderTarget[0].BlendEnable = blendState.BlendEnabled; - blendDesc.RenderTarget[0].SrcBlend = d3d11Types::asBlendFactor(blendState.SrcFactorRGB); - blendDesc.RenderTarget[0].DestBlend = d3d11Types::asBlendFactor(blendState.DstFactorRGB); - blendDesc.RenderTarget[0].BlendOp = d3d11Types::asBlendOp(blendState.OpRGB); - blendDesc.RenderTarget[0].SrcBlendAlpha = d3d11Types::asBlendFactor(blendState.SrcFactorAlpha); - blendDesc.RenderTarget[0].DestBlendAlpha = d3d11Types::asBlendFactor(blendState.DstFactorAlpha); - blendDesc.RenderTarget[0].BlendOpAlpha = d3d11Types::asBlendOp(blendState.OpAlpha); - blendDesc.RenderTarget[0].RenderTargetWriteMask = d3d11Types::asColorWriteMask(blendState.ColorWriteMask); - hr = d3d11Device->CreateBlendState(&blendDesc, &pip.d3d11BlendState); - o_assert(SUCCEEDED(hr)); - o_assert_dbg(nullptr != pip.d3d11BlendState); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -d3d11Factory::destroyPipeline(pipeline& pip) { - this->pointers.renderer->invalidatePipeline(); - if (nullptr != pip.d3d11InputLayout) { - pip.d3d11InputLayout->Release(); - } - if (nullptr != pip.d3d11RasterizerState) { - pip.d3d11RasterizerState->Release(); - } - if (nullptr != pip.d3d11BlendState) { - pip.d3d11BlendState->Release(); - } - if (nullptr != pip.d3d11DepthStencilState) { - pip.d3d11DepthStencilState->Release(); - } - gfxFactoryBase::destroyPipeline(pip); -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Factory.h b/code/Modules/Gfx/private/d3d11/d3d11Factory.h deleted file mode 100644 index 61c3a90e2..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Factory.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Factory - @ingroup _priv - @brief D3D11 implementation of gfxFactory -*/ -#include "Gfx/private/gfxFactoryBase.h" -#include "Gfx/private/d3d11/d3d11_decl.h" - -namespace Oryol { -namespace _priv { - -class mesh; -class texture; -class renderPass; -class shader; -class pipeline; - -class d3d11Factory : public gfxFactoryBase { -public: - /// setup the factory - void setup(const gfxPointers& ptrs); - /// discard the factory - void discard(); - - /// initialize new mesh object - ResourceState::Code initMesh(mesh& msh, const void* data, int size); - /// destroy a mesh object - void destroyMesh(mesh& msh); - /// initialize a new texture object - ResourceState::Code initTexture(texture& tex, const void* data, int size); - /// destroy a texture object - void destroyTexture(texture& tex); - /// initialize a new render pass object - ResourceState::Code initRenderPass(renderPass& rp); - /// destroy a render pass object - void destroyRenderPass(renderPass& rp); - /// initialize a new shader object - ResourceState::Code initShader(shader& shd); - /// destroy a shader object - void destroyShader(shader& shd); - /// initialize a new pipeline object - ResourceState::Code initPipeline(pipeline& pip); - /// destroy a pipeline object - void destroyPipeline(pipeline& pip); - - /// helper method to setup a mesh object as fullscreen quad - ResourceState::Code initFullscreenQuad(mesh& mesh); - /// helper method to create a standard mesh - ResourceState::Code initStdMesh(mesh& mesh, const void* data, int size); - /// helper method to create vertex or index buffer - ID3D11Buffer* createBuffer(const void* vertexData, uint32_t vertexDataSize, uint32_t d3d11BindFlags, Usage::Code usage); - - static const int maxNumSubResourceData = GfxConfig::MaxNumTextureArraySlices * GfxConfig::MaxNumTextureMipMaps; - D3D11_SUBRESOURCE_DATA* subResourceData = nullptr; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Renderer.cc b/code/Modules/Gfx/private/d3d11/d3d11Renderer.cc deleted file mode 100644 index 73ec19666..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Renderer.cc +++ /dev/null @@ -1,745 +0,0 @@ -//------------------------------------------------------------------------------ -// d3d11Renderer.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "d3d11Renderer.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/GfxTypes.h" -#include -#include -#include "d3d11_impl.h" -#include "d3d11Types.h" -#ifdef max -#undef max -#endif -#ifdef min -#undef min -#endif -#include // std::max() - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -d3d11Renderer::d3d11Renderer() : -d3d11Device(nullptr), -d3d11DeviceContext(nullptr), -valid(false), -rpValid(false), -frameIndex(0), -curRenderPass(nullptr), -curPipeline(nullptr), -curPrimaryMesh(nullptr), -numRTVs(0), -d3d11CurDSV(nullptr), -d3d11CurRasterizerState(nullptr), -d3d11CurDepthStencilState(nullptr), -d3d11CurBlendState(nullptr), -d3d11CurIndexBuffer(nullptr), -d3d11CurInputLayout(nullptr), -d3d11CurVertexShader(nullptr), -d3d11CurPixelShader(nullptr), -d3d11CurPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED), -curStencilRef(0xFFFF) { - this->d3d11CurRTVs.Fill(nullptr); - this->d3d11CurVSCBs.Fill(nullptr); - this->d3d11CurPSCBs.Fill(nullptr); - this->d3d11CurVBs.Fill(nullptr); - this->d3d11CurVSSRVs.Fill(nullptr); - this->d3d11CurPSSRVs.Fill(nullptr); - this->d3d11CurVSSamplers.Fill(nullptr); - this->d3d11CurPSSamplers.Fill(nullptr); - this->curVertexStrides.Fill(0); - this->curVertexOffsets.Fill(0); -} - -//------------------------------------------------------------------------------ -d3d11Renderer::~d3d11Renderer() { - o_assert_dbg(!this->valid); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::setup(const GfxSetup& setup, const gfxPointers& ptrs) { - o_assert_dbg(!this->valid); - - this->valid = true; - this->gfxSetup = setup; - this->pointers = ptrs; - this->d3d11Device = this->pointers.displayMgr->d3d11Device; - this->d3d11DeviceContext = this->pointers.displayMgr->d3d11DeviceContext; -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::discard() { - o_assert_dbg(this->valid); - - this->numRTVs = 0; - this->d3d11CurRTVs.Fill(nullptr); - this->d3d11CurDSV = nullptr; - this->d3d11CurRasterizerState = nullptr; - this->d3d11CurDepthStencilState = nullptr; - this->d3d11CurBlendState = nullptr; - this->d3d11CurIndexBuffer = nullptr; - this->d3d11CurInputLayout = nullptr; - this->d3d11CurVertexShader = nullptr; - this->d3d11CurPixelShader = nullptr; - this->d3d11CurPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - this->d3d11CurVSCBs.Fill(nullptr); - this->d3d11CurPSCBs.Fill(nullptr); - this->d3d11CurVBs.Fill(nullptr); - this->d3d11CurVSSRVs.Fill(nullptr); - this->d3d11CurPSSRVs.Fill(nullptr); - this->d3d11CurVSSamplers.Fill(nullptr); - this->d3d11CurPSSamplers.Fill(nullptr); - this->curVertexStrides.Fill(0); - this->curVertexOffsets.Fill(0); - - this->curRenderPass = nullptr; - this->curPipeline = nullptr; - this->curPrimaryMesh = nullptr; - - this->d3d11DeviceContext = nullptr; - this->d3d11Device = nullptr; - - this->pointers = gfxPointers(); - this->valid = false; -} - -//------------------------------------------------------------------------------ -bool -d3d11Renderer::isValid() const { - return this->valid; -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::resetStateCache() { - o_assert_dbg(this->d3d11DeviceContext); - - this->d3d11DeviceContext->ClearState(); - this->curRenderPass = nullptr; - this->curPipeline = nullptr; - this->numRTVs = 0; - this->d3d11CurRTVs.Fill(nullptr); - this->d3d11CurDSV = nullptr; - this->d3d11CurDepthStencilState = nullptr; - this->d3d11CurRasterizerState = nullptr; - this->d3d11CurBlendState = nullptr; - this->d3d11CurIndexBuffer = nullptr; - this->d3d11CurInputLayout = nullptr; - this->d3d11CurVertexShader = nullptr; - this->d3d11CurPixelShader = nullptr; - this->d3d11CurPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - this->d3d11CurVSCBs.Fill(nullptr); - this->d3d11CurPSCBs.Fill(nullptr); - this->d3d11CurVBs.Fill(nullptr); - this->d3d11CurVSSRVs.Fill(nullptr); - this->d3d11CurPSSRVs.Fill(nullptr); - this->d3d11CurVSSamplers.Fill(nullptr); - this->d3d11CurPSSamplers.Fill(nullptr); - this->curVertexStrides.Fill(0); - this->curVertexOffsets.Fill(0); - this->curStencilRef = 0xFFFF; - this->curBlendColor = glm::vec4(0.0f); -} - -//------------------------------------------------------------------------------ -bool -d3d11Renderer::queryFeature(GfxFeature::Code feat) const { - switch (feat) { - case GfxFeature::TextureCompressionDXT: - case GfxFeature::TextureFloat: - case GfxFeature::Instancing: - case GfxFeature::OriginTopLeft: - case GfxFeature::MSAARenderTargets: - case GfxFeature::MultipleRenderTarget: - case GfxFeature::Texture3D: - case GfxFeature::TextureArray: - return true; - default: - return false; - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::commitFrame() { - o_assert_dbg(this->valid); - this->rpValid = false; - this->curRenderPass = nullptr; - this->curPipeline = nullptr; - this->curPrimaryMesh = nullptr; - this->frameIndex++; -} - -//------------------------------------------------------------------------------ -const DisplayAttrs& -d3d11Renderer::renderPassAttrs() const { - return this->rpAttrs; -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::beginPass(renderPass* pass, const PassAction* action) { - o_assert_dbg(this->valid); - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(action); - - // don't keep texture binding across passes, bound texture might be render targets! - this->invalidateTextureState(); - this->d3d11CurRTVs.Fill(nullptr); - if (nullptr == pass) { - this->rpAttrs = this->pointers.displayMgr->GetDisplayAttrs(); - this->numRTVs = 1; - this->d3d11CurRTVs[0] = this->pointers.displayMgr->d3d11RenderTargetView; - this->d3d11CurDSV = this->pointers.displayMgr->d3d11DepthStencilView; - } - else { - o_assert_dbg(pass->colorTextures[0]); - this->rpAttrs = DisplayAttrs::FromTextureAttrs(pass->colorTextures[0]->textureAttrs); - int i; - for (i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (pass->d3d11RenderTargetViews[i]) { - this->d3d11CurRTVs[i] = pass->d3d11RenderTargetViews[i]; - } - else { - break; - } - } - this->numRTVs = i; - this->d3d11CurDSV = pass->d3d11DepthStencilView; - } - o_assert_dbg(nullptr == this->curRenderPass); - this->curRenderPass = pass; - this->rpValid = true; - - // actually set the render targets in the d3d11 device context - this->d3d11DeviceContext->OMSetRenderTargets(this->numRTVs, &(this->d3d11CurRTVs[0]), this->d3d11CurDSV); - - // set viewport to cover whole screen - this->applyViewPort(0, 0, this->rpAttrs.FramebufferWidth, this->rpAttrs.FramebufferHeight, true); - - // perform clear action - if (nullptr == this->curRenderPass) { - if (action->Flags & PassAction::ClearC0) { - if (this->d3d11CurRTVs[0]) { - this->d3d11DeviceContext->ClearRenderTargetView(this->d3d11CurRTVs[0], glm::value_ptr(action->Color[0])); - } - } - if (action->Flags & PassAction::ClearDS) { - if (this->d3d11CurDSV) { - const UINT f = D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL; - this->d3d11DeviceContext->ClearDepthStencilView(this->d3d11CurDSV, f, action->Depth, action->Stencil); - } - } - } - else { - for (int i = 0; i < this->numRTVs; i++) { - if (this->d3d11CurRTVs[i]) { - if (action->Flags & (PassAction::ClearC0<d3d11DeviceContext->ClearRenderTargetView(this->d3d11CurRTVs[i], glm::value_ptr(action->Color[i])); - } - } - else { - break; - } - } - if (this->d3d11CurDSV && (action->Flags & PassAction::ClearDS)) { - const UINT f = D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL; - this->d3d11DeviceContext->ClearDepthStencilView(this->d3d11CurDSV, f, action->Depth, action->Stencil); - } - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::endPass() { - o_assert_dbg(this->valid); - - const renderPass* rp = this->curRenderPass; - if (rp) { - const bool isMSAA = nullptr != rp->colorTextures[0]->d3d11MSAATexture2D; - if (isMSAA) { - // perform MSAA resolve on offscreen render targets - for (int i = 0; i < this->numRTVs; i++) { - texture* colorTex = rp->colorTextures[i]; - const auto& att = rp->Setup.ColorAttachments[i]; - o_assert_dbg(colorTex->d3d11MSAATexture2D && colorTex->d3d11Texture2D); - UINT subres = D3D11CalcSubresource(att.MipLevel, att.Slice, colorTex->textureAttrs.NumMipMaps); - this->d3d11DeviceContext->ResolveSubresource( - colorTex->d3d11Texture2D, // pDstResource - subres, // DstSubresource - colorTex->d3d11MSAATexture2D, // pSrcResource - subres, // SrcSubresource - colorTex->d3d11ColorFormat); - } - } - } - this->curRenderPass = nullptr; - this->rpValid = false; -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::applyViewPort(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->d3d11DeviceContext); - - D3D11_VIEWPORT vp; - vp.TopLeftX = (FLOAT) x; - vp.TopLeftY = (FLOAT) (originTopLeft ? y : (this->rpAttrs.FramebufferHeight - (y + height))); - vp.Width = (FLOAT) width; - vp.Height = (FLOAT) height; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - this->d3d11DeviceContext->RSSetViewports(1, &vp); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::applyScissorRect(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->d3d11DeviceContext); - - D3D11_RECT rect; - rect.left = x; - rect.top = originTopLeft ? y : this->rpAttrs.FramebufferHeight - (y + height); - rect.right = x + width; - rect.bottom = originTopLeft ? (y + height) : (this->rpAttrs.FramebufferHeight - y); - this->d3d11DeviceContext->RSSetScissorRects(1, &rect); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::applyDrawState(pipeline* pip, mesh** meshes, int numMeshes) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(pip); - o_assert_dbg(pip->shd); - o_assert_dbg(meshes && numMeshes > 0); - - // if any of the meshes are still loading, cancel the next draw state - for (int i = 0; i < numMeshes; i++) { - if (nullptr == meshes[i]) { - this->curPipeline = nullptr; - return; - } - } - o_assert_dbg(pip->d3d11DepthStencilState); - o_assert_dbg(pip->d3d11RasterizerState); - o_assert_dbg(pip->d3d11BlendState); - #if ORYOL_DEBUG - o_assert2(pip->Setup.BlendState.ColorFormat == this->rpAttrs.ColorPixelFormat, "ColorFormat in BlendState must match current render target!\n"); - o_assert2(pip->Setup.BlendState.DepthFormat == this->rpAttrs.DepthPixelFormat, "DepthFormat in BlendState must match current render target!\n"); - o_assert2(pip->Setup.RasterizerState.SampleCount == this->rpAttrs.SampleCount, "SampleCount in RasterizerState must match current render target!\n"); - if (this->curRenderPass) { - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - const texture* tex = this->curRenderPass->colorTextures[i]; - if (tex) { - o_assert2(pip->Setup.BlendState.ColorFormat == tex->textureAttrs.ColorFormat, "ColorFormat in BlendState must match MRT color attachments!\n"); - o_assert2(pip->Setup.RasterizerState.SampleCount == tex->textureAttrs.SampleCount, "SampleCount in RasterizerState must match MRT color attachments!\n"); - } - } - const texture* dsTex = this->curRenderPass->depthStencilTexture; - if (dsTex) { - o_assert2(pip->Setup.BlendState.DepthFormat == dsTex->textureAttrs.DepthFormat, "DepthFormat in BlendState must match depth/stencil attachment!\n"); - } - } - #endif - this->curPipeline = pip; - this->curPrimaryMesh = meshes[0]; - o_assert_dbg(this->curPrimaryMesh); - - // apply state objects (if state has changed) - if (pip->d3d11RasterizerState != this->d3d11CurRasterizerState) { - this->d3d11CurRasterizerState = pip->d3d11RasterizerState; - this->d3d11DeviceContext->RSSetState(pip->d3d11RasterizerState); - } - if ((pip->d3d11DepthStencilState != this->d3d11CurDepthStencilState) || - (pip->Setup.DepthStencilState.StencilRef != this->curStencilRef)) { - - this->d3d11CurDepthStencilState = pip->d3d11DepthStencilState; - this->curStencilRef = pip->Setup.DepthStencilState.StencilRef; - this->d3d11DeviceContext->OMSetDepthStencilState(pip->d3d11DepthStencilState, pip->Setup.DepthStencilState.StencilRef); - } - if ((pip->d3d11BlendState != this->d3d11CurBlendState) || - glm::any(glm::notEqual(pip->Setup.BlendColor, this->curBlendColor))) { - - this->d3d11CurBlendState = pip->d3d11BlendState; - this->curBlendColor = pip->Setup.BlendColor; - this->d3d11DeviceContext->OMSetBlendState(pip->d3d11BlendState, glm::value_ptr(pip->Setup.BlendColor), 0xFFFFFFFF); - } - - // apply vertex buffers - bool vbDirty = false; - for (int slotIndex = 0; slotIndex < GfxConfig::MaxNumInputMeshes; slotIndex++) { - const mesh* msh = slotIndex < numMeshes ? meshes[slotIndex] : nullptr; - if (msh) { - if (this->d3d11CurVBs[slotIndex] != msh->d3d11VertexBuffer) { - this->d3d11CurVBs[slotIndex] = msh->d3d11VertexBuffer; - vbDirty = true; - } - if (this->curVertexStrides[slotIndex] != msh->vertexBufferAttrs.Layout.ByteSize()) { - this->curVertexStrides[slotIndex] = msh->vertexBufferAttrs.Layout.ByteSize(); - vbDirty = true; - } - } - else if (this->d3d11CurVBs[slotIndex] != nullptr) { - vbDirty = true; - this->d3d11CurVBs[slotIndex] = nullptr; - this->curVertexStrides[slotIndex] = 0; - } - } - if (vbDirty) { - this->d3d11DeviceContext->IASetVertexBuffers( - 0, // StartSlot - GfxConfig::MaxNumInputMeshes, // NumBuffers - &(this->d3d11CurVBs[0]), // ppVertexBuffers - &(this->curVertexStrides[0]), // pStrides - &(this->curVertexOffsets[0])); // pOffsets - } - if (this->d3d11CurPrimitiveTopology != pip->d3d11PrimTopology) { - this->d3d11CurPrimitiveTopology = pip->d3d11PrimTopology; - this->d3d11DeviceContext->IASetPrimitiveTopology(pip->d3d11PrimTopology); - } - - // apply optional index buffer (can be nullptr!) - if (this->d3d11CurIndexBuffer != this->curPrimaryMesh->d3d11IndexBuffer) { - this->d3d11CurIndexBuffer = this->curPrimaryMesh->d3d11IndexBuffer; - DXGI_FORMAT d3d11IndexFormat = d3d11Types::asIndexType(this->curPrimaryMesh->indexBufferAttrs.Type); - this->d3d11DeviceContext->IASetIndexBuffer(this->curPrimaryMesh->d3d11IndexBuffer, d3d11IndexFormat, 0); - } - - // apply input layout and shaders - if (this->d3d11CurInputLayout != pip->d3d11InputLayout) { - this->d3d11CurInputLayout = pip->d3d11InputLayout; - this->d3d11DeviceContext->IASetInputLayout(pip->d3d11InputLayout); - } - - // apply shaders - if (this->d3d11CurVertexShader != pip->shd->d3d11VertexShader) { - this->d3d11CurVertexShader = pip->shd->d3d11VertexShader; - this->d3d11DeviceContext->VSSetShader(pip->shd->d3d11VertexShader, NULL, 0); - } - if (this->d3d11CurPixelShader != pip->shd->d3d11PixelShader) { - this->d3d11CurPixelShader = pip->shd->d3d11PixelShader; - this->d3d11DeviceContext->PSSetShader(pip->shd->d3d11PixelShader, NULL, 0); - } - - // apply vertex-shader-stage constant buffers - for (int bindSlot = 0; bindSlot < GfxConfig::MaxNumUniformBlocksPerStage; bindSlot++) { - // NOTE: cb can be nullptr! - ID3D11Buffer* cb = pip->shd->getConstantBuffer(ShaderStage::VS, bindSlot); - if (this->d3d11CurVSCBs[bindSlot] != cb) { - this->d3d11CurVSCBs[bindSlot] = cb; - this->d3d11DeviceContext->VSSetConstantBuffers(bindSlot, 1, &cb); - } - } - - // apply fragment-shader-stage constant buffers - for (int bindSlot = 0; bindSlot < GfxConfig::MaxNumUniformBlocksPerStage; bindSlot++) { - // NOTE: cb can be nullptr! - ID3D11Buffer* cb = pip->shd->getConstantBuffer(ShaderStage::FS, bindSlot); - if (this->d3d11CurPSCBs[bindSlot] != cb) { - this->d3d11CurPSCBs[bindSlot] = cb; - this->d3d11DeviceContext->PSSetConstantBuffers(bindSlot, 1, &cb); - } - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t typeHash, const uint8_t* ptr, int byteSize) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(0 != typeHash); - if (nullptr == this->curPipeline) { - // currently no valid draw state set - return; - } - - const shader* shd = this->curPipeline->shd; - o_assert_dbg(shd); - - #if ORYOL_DEBUG - // check whether the provided struct is type-compatible with the - // expected uniform-block-layout, the size-check shouldn't be necessary - // since the hash should already bail out, but it doesn't hurt either - int ubIndex = shd->Setup.UniformBlockIndexByStageAndSlot(bindStage, bindSlot); - o_assert(InvalidIndex != ubIndex); - const uint32_t ubTypeHash = shd->Setup.UniformBlockTypeHash(ubIndex); - const int ubByteSize = shd->Setup.UniformBlockByteSize(ubIndex); - o_assert(ubTypeHash == typeHash); - o_assert(ubByteSize >= byteSize); - #endif - - // NOTE: UpdateSubresource() and map-discard are equivalent (at least on nvidia) - ID3D11Buffer* cb = shd->getConstantBuffer(bindStage, bindSlot); - o_assert_dbg(cb); - this->d3d11DeviceContext->UpdateSubresource(cb, 0, nullptr, ptr, 0, 0); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::applyTextures(ShaderStage::Code bindStage, texture** textures, int numTextures) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(this->valid); - if (nullptr == this->curPipeline) { - return; - } - - // if any of the provided texture pointers are not valid, this means one of the - // textures isn't valid yet, in this case, disable rendering for the next draw call - for (int i = 0; i < numTextures; i++) { - if (nullptr == textures[i]) { - this->curPipeline = nullptr; - return; - } - } - - // apply textures and samplers - if (ShaderStage::VS == bindStage) { - o_assert_dbg(numTextures <= GfxConfig::MaxNumVertexTextures); - for (int i = 0; i < numTextures; i++) { - if (textures[i]->d3d11ShaderResourceView != this->d3d11CurVSSRVs[i]) { - this->d3d11CurVSSRVs[i] = textures[i]->d3d11ShaderResourceView; - this->d3d11DeviceContext->VSSetShaderResources(i, 1, &(textures[i]->d3d11ShaderResourceView)); - } - if (textures[i]->d3d11SamplerState != this->d3d11CurVSSamplers[i]) { - this->d3d11CurVSSamplers[i] = textures[i]->d3d11SamplerState; - this->d3d11DeviceContext->VSSetSamplers(i, 1, &(textures[i]->d3d11SamplerState)); - } - } - } - else { - o_assert_dbg(numTextures <= GfxConfig::MaxNumFragmentTextures); - for (int i = 0; i < numTextures; i++) { - if (textures[i]->d3d11ShaderResourceView != this->d3d11CurPSSRVs[i]) { - this->d3d11CurPSSRVs[i] = textures[i]->d3d11ShaderResourceView; - this->d3d11DeviceContext->PSSetShaderResources(i, 1, &(textures[i]->d3d11ShaderResourceView)); - } - if (textures[i]->d3d11SamplerState != this->d3d11CurPSSamplers[i]) { - this->d3d11CurPSSamplers[i] = textures[i]->d3d11SamplerState; - this->d3d11DeviceContext->PSSetSamplers(i, 1, &(textures[i]->d3d11SamplerState)); - } - } - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::draw(int baseElementIndex, int numElements, int numInstances) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(numInstances >= 1); - o_assert2_dbg(this->rpValid, "No render target set!\n"); - if (nullptr == this->curPipeline) { - return; - } - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - const IndexType::Code indexType = msh->indexBufferAttrs.Type; - if (indexType != IndexType::None) { - if (numInstances == 1) { - this->d3d11DeviceContext->DrawIndexed(numElements, baseElementIndex, 0); - } - else { - this->d3d11DeviceContext->DrawIndexedInstanced(numElements, numInstances, baseElementIndex, 0, 0); - } - } - else { - if (numInstances == 1) { - this->d3d11DeviceContext->Draw(numElements, baseElementIndex); - } - else { - this->d3d11DeviceContext->DrawInstanced(numElements, numInstances, baseElementIndex, 0); - } - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::draw(int primGroupIndex, int numInstances) { - o_assert_dbg(this->valid); - if (nullptr == this->curPipeline) { - return; - } - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - if (primGroupIndex >= msh->numPrimGroups) { - // this may happen if trying to render a placeholder which doesn't - // have as many materials as the original mesh, anyway, this isn't - // a serious error - return; - } - const PrimitiveGroup& primGroup = msh->primGroups[primGroupIndex]; - this->draw(primGroup.BaseElement, primGroup.NumElements, numInstances); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::updateVertices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(nullptr != msh); - o_assert_dbg(msh->d3d11VertexBuffer); - o_assert_dbg(numBytes > 0); - - o_assert_dbg((numBytes > 0) && (numBytes <= msh->vertexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->vertexBufferAttrs.BufferUsage); - - o_assert2(msh->vbUpdateFrameIndex != this->frameIndex, "Only one data update allowed per buffer and frame!\n"); - msh->vbUpdateFrameIndex = this->frameIndex; - - D3D11_MAPPED_SUBRESOURCE mapped; - HRESULT hr = this->d3d11DeviceContext->Map(msh->d3d11VertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); - o_assert_dbg(SUCCEEDED(hr)); - std::memcpy(mapped.pData, data, numBytes); - this->d3d11DeviceContext->Unmap(msh->d3d11VertexBuffer, 0); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::updateIndices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(nullptr != msh); - o_assert_dbg(msh->d3d11IndexBuffer); - o_assert_dbg(numBytes > 0); - - o_assert_dbg((numBytes > 0) && (numBytes <= msh->indexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->indexBufferAttrs.BufferUsage); - - o_assert2(msh->ibUpdateFrameIndex != this->frameIndex, "Only one data update allowed per buffer and frame!\n"); - msh->ibUpdateFrameIndex = this->frameIndex; - - D3D11_MAPPED_SUBRESOURCE mapped; - HRESULT hr = this->d3d11DeviceContext->Map(msh->d3d11IndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); - o_assert_dbg(SUCCEEDED(hr)); - std::memcpy(mapped.pData, data, numBytes); - this->d3d11DeviceContext->Unmap(msh->d3d11IndexBuffer, 0); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes) { - o_assert_dbg(this->d3d11DeviceContext); - o_assert_dbg(tex); - o_assert_dbg(data); - o_assert_dbg(tex->d3d11Texture2D); - - // only accept 2D textures for now - const TextureAttrs& attrs = tex->textureAttrs; - o_assert_dbg(TextureType::Texture2D == attrs.Type); - o_assert_dbg(Usage::Immutable != attrs.TextureUsage); - o_assert_dbg(!PixelFormat::IsCompressedFormat(attrs.ColorFormat)); - o_assert_dbg(offsetsAndSizes.NumMipMaps == attrs.NumMipMaps); - o_assert_dbg(offsetsAndSizes.NumFaces == 1); - - D3D11_MAPPED_SUBRESOURCE mapped; - for (int mipIndex = 0; mipIndex < attrs.NumMipMaps; mipIndex++) { - o_assert_dbg(offsetsAndSizes.Sizes[0][mipIndex] > 0); - const int mipWidth = std::max(attrs.Width >> mipIndex, 1); - const int mipHeight = std::max(attrs.Height >> mipIndex, 1); - const int srcPitch = PixelFormat::RowPitch(attrs.ColorFormat, mipWidth); - HRESULT hr = this->d3d11DeviceContext->Map( - tex->d3d11Texture2D, // pResource - mipIndex, // Subresource - D3D11_MAP_WRITE_DISCARD, // MapType - 0, // MapFlags - &mapped); // pMappedResource - o_assert_dbg(SUCCEEDED(hr)); - o_assert_dbg(srcPitch <= (int)mapped.RowPitch); - uint8_t* dstPtr = (uint8_t*)mapped.pData; - const uint8_t* srcPtr = ((const uint8_t*)data) + offsetsAndSizes.Offsets[0][mipIndex]; - if (srcPitch == mapped.RowPitch) { - const int mipSize = offsetsAndSizes.Sizes[0][mipIndex]; - o_assert_dbg(mipSize <= (srcPitch*mipHeight)); - std::memcpy(dstPtr, srcPtr, mipSize); - } - else { - o_error("d3dRenderer::updateTexture(): srcPitch!=dstPitch, FIXME UNTESTED!\n"); - for (int rowIndex = 0; rowIndex < mipHeight; rowIndex++) { - std::memcpy(dstPtr, srcPtr, srcPitch); - dstPtr += mapped.RowPitch; - srcPtr += srcPitch; - } - } - this->d3d11DeviceContext->Unmap(tex->d3d11Texture2D, mipIndex); - } -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::invalidateMeshState() { - o_assert_dbg(this->d3d11DeviceContext); - - Log::Info("d3d11Renderer::invalidateMeshState()\n"); - - this->d3d11CurIndexBuffer = nullptr; - this->d3d11CurInputLayout = nullptr; - this->d3d11CurVBs.Fill(nullptr); - this->curVertexStrides.Fill(0); - this->curVertexOffsets.Fill(0); - this->d3d11CurPrimitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - this->d3d11DeviceContext->IASetInputLayout(nullptr); - this->d3d11DeviceContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_UNKNOWN, 0); - this->d3d11DeviceContext->IASetVertexBuffers( - 0, - this->d3d11CurVBs.Size(), - &(this->d3d11CurVBs[0]), - &(this->curVertexStrides[0]), - &(this->curVertexOffsets[0])); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::invalidateShaderState() { - o_assert_dbg(this->d3d11DeviceContext); - - Log::Info("d3d11Renderer::invalidateProgramState()\n"); - - this->d3d11CurVertexShader = nullptr; - this->d3d11CurPixelShader = nullptr; - this->d3d11CurVSCBs.Fill(nullptr); - this->d3d11CurPSCBs.Fill(nullptr); - this->d3d11DeviceContext->VSSetShader(nullptr, nullptr, 0); - this->d3d11DeviceContext->PSSetShader(nullptr, nullptr, 0); - this->d3d11DeviceContext->VSSetConstantBuffers(0, GfxConfig::MaxNumUniformBlocksPerStage, &this->d3d11CurVSCBs[0]); - this->d3d11DeviceContext->PSSetConstantBuffers(0, GfxConfig::MaxNumUniformBlocksPerStage, &this->d3d11CurPSCBs[0]); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::invalidatePipeline() { - o_assert_dbg(this->d3d11DeviceContext); - - Log::Info("d3d11Renderer::invalidatePipeline()\n"); - - this->d3d11CurBlendState = nullptr; - this->d3d11CurDepthStencilState = nullptr; - this->d3d11CurRasterizerState = nullptr; - this->d3d11DeviceContext->OMSetBlendState(nullptr, glm::value_ptr(this->curBlendColor), 0xFFFFFFFF); - this->d3d11DeviceContext->OMSetDepthStencilState(nullptr, 0xFF); - this->d3d11DeviceContext->RSSetState(nullptr); -} - -//------------------------------------------------------------------------------ -void -d3d11Renderer::invalidateTextureState() { - o_assert_dbg(this->d3d11DeviceContext); - - ID3D11ShaderResourceView* const nullVSSRVs[GfxConfig::MaxNumVertexTextures] = { 0 }; - ID3D11ShaderResourceView* const nullFSSRVs[GfxConfig::MaxNumFragmentTextures] = { 0 }; - this->d3d11DeviceContext->VSSetShaderResources(0, GfxConfig::MaxNumVertexTextures, nullVSSRVs); - this->d3d11DeviceContext->PSSetShaderResources(0, GfxConfig::MaxNumFragmentTextures, nullFSSRVs); - - ID3D11SamplerState* const nullVSSamplers[GfxConfig::MaxNumVertexTextures] = { 0 }; - ID3D11SamplerState* const nullFSSamplers[GfxConfig::MaxNumFragmentTextures] = { 0 }; - this->d3d11DeviceContext->VSSetSamplers(0, GfxConfig::MaxNumVertexTextures, nullVSSamplers); - this->d3d11DeviceContext->PSSetSamplers(0, GfxConfig::MaxNumFragmentTextures, nullFSSamplers); - - this->d3d11CurVSSRVs.Fill(nullptr); - this->d3d11CurPSSRVs.Fill(nullptr); - this->d3d11CurVSSamplers.Fill(nullptr); - this->d3d11CurPSSamplers.Fill(nullptr); -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Renderer.h b/code/Modules/Gfx/private/d3d11/d3d11Renderer.h deleted file mode 100644 index 3388faf10..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Renderer.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Renderer - @ingroup _priv - @brief D3D11 implementation of renderer -*/ -#include "Core/Types.h" -#include "Core/Containers/StaticArray.h" -#include "Gfx/GfxTypes.h" -#include -#include "Gfx/private/d3d11/d3d11_decl.h" -#include "Gfx/private/gfxPointers.h" - -namespace Oryol { -namespace _priv { - -class texture; -class pipeline; -class mesh; -class textureBlock; -class renderPass; - -class d3d11Renderer { -public: - /// constructor - d3d11Renderer(); - /// destructor - ~d3d11Renderer(); - - /// setup the renderer - void setup(const GfxSetup& setup, const gfxPointers& ptrs); - /// discard the renderer - void discard(); - /// return true if renderer has been setup - bool isValid() const; - - /// reset the internal state cache - void resetStateCache(); - /// test if a feature is supported - bool queryFeature(GfxFeature::Code feat) const; - /// commit current frame - void commitFrame(); - /// get the current render pass attributes - const DisplayAttrs& renderPassAttrs() const; - - /// begin rendering pass (both ptrs can be nullptr) - void beginPass(renderPass* pass, const PassAction* action); - /// end current rendering pass - void endPass(); - /// apply viewport - void applyViewPort(int x, int y, int width, int height, bool originTopLeft); - /// apply scissor rect - void applyScissorRect(int x, int y, int width, int height, bool originTopLeft); - /// apply draw state - void applyDrawState(pipeline* pip, mesh** meshes, int numMeshes); - /// apply a shader uniform block - void applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t layoutHash, const uint8_t* ptr, int byteSize); - /// apply a textures - void applyTextures(ShaderStage::Code bindStage, texture** textures, int numTextures); - /// submit a draw call with primitive group index in current mesh - void draw(int primGroupIndex, int numInstances); - /// submit a draw call with element range - void draw(int baseElementIndex, int numElements, int numInstances); - /// update vertex data - void updateVertices(mesh* msh, const void* data, int numBytes); - /// update index data - void updateIndices(mesh* msh, const void* data, int numBytes); - /// update texture data - void updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes); - - /// invalidate currently bound mesh state - void invalidateMeshState(); - /// invalidate currently bound shader program state - void invalidateShaderState(); - /// invalidate currently bound draw state - void invalidatePipeline(); - /// invalidate currently bound texture state - void invalidateTextureState(); - - /// pointer to d3d11 device - ID3D11Device* d3d11Device; - /// pointer to immediate mode device context - ID3D11DeviceContext* d3d11DeviceContext; - - bool valid; - bool rpValid; - int frameIndex; - GfxSetup gfxSetup; - gfxPointers pointers; - DisplayAttrs rpAttrs; - - renderPass* curRenderPass; - pipeline* curPipeline; - mesh* curPrimaryMesh; - - int numRTVs; - StaticArray d3d11CurRTVs; - ID3D11DepthStencilView* d3d11CurDSV; - ID3D11RasterizerState* d3d11CurRasterizerState; - ID3D11DepthStencilState* d3d11CurDepthStencilState; - ID3D11BlendState* d3d11CurBlendState; - ID3D11Buffer* d3d11CurIndexBuffer; - ID3D11InputLayout* d3d11CurInputLayout; - ID3D11VertexShader* d3d11CurVertexShader; - ID3D11PixelShader* d3d11CurPixelShader; - D3D_PRIMITIVE_TOPOLOGY d3d11CurPrimitiveTopology; - StaticArray d3d11CurVSCBs; - StaticArray d3d11CurPSCBs; - StaticArray d3d11CurVBs; - StaticArray curVertexStrides; - StaticArray curVertexOffsets; - StaticArray d3d11CurVSSRVs; - StaticArray d3d11CurPSSRVs; - StaticArray d3d11CurVSSamplers; - StaticArray d3d11CurPSSamplers; - - uint16_t curStencilRef; - glm::vec4 curBlendColor; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Resource.cc b/code/Modules/Gfx/private/d3d11/d3d11Resource.cc deleted file mode 100644 index 28a4624b8..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Resource.cc +++ /dev/null @@ -1,144 +0,0 @@ -//------------------------------------------------------------------------------ -// d3d11Resource.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "d3d11Resource.h" -#include "d3d11_impl.h" - -namespace Oryol { -namespace _priv { - -//============================================================================== -d3d11Mesh::~d3d11Mesh() { - o_assert_dbg(nullptr == this->d3d11VertexBuffer); - o_assert_dbg(nullptr == this->d3d11IndexBuffer); -} - -//------------------------------------------------------------------------------ -void - d3d11Mesh::Clear() { - this->d3d11VertexBuffer = nullptr; - this->d3d11IndexBuffer = nullptr; - this->vbUpdateFrameIndex = -1; - this->ibUpdateFrameIndex = -1; - meshBase::Clear(); -} - -//============================================================================== -d3d11Pipeline::d3d11Pipeline() : - d3d11InputLayout(nullptr), - d3d11PrimTopology(D3D_PRIMITIVE_TOPOLOGY_UNDEFINED), - d3d11RasterizerState(nullptr), - d3d11DepthStencilState(nullptr), - d3d11BlendState(nullptr) { - // empty -} - -//------------------------------------------------------------------------------ -d3d11Pipeline::~d3d11Pipeline() { -#if ORYOL_DEBUG - o_assert_dbg(nullptr == this->d3d11InputLayout); - o_assert_dbg(nullptr == this->d3d11RasterizerState); - o_assert_dbg(nullptr == this->d3d11DepthStencilState); - o_assert_dbg(nullptr == this->d3d11BlendState); -#endif -} - -//------------------------------------------------------------------------------ -void -d3d11Pipeline::Clear() { - this->d3d11InputLayout = nullptr; - this->d3d11PrimTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; - this->d3d11RasterizerState = nullptr; - this->d3d11DepthStencilState = nullptr; - this->d3d11BlendState = nullptr; - pipelineBase::Clear(); -} - -//============================================================================== -d3d11Shader::d3d11Shader() { - this->Clear(); -} - -//------------------------------------------------------------------------------ -d3d11Shader::~d3d11Shader() { -#if ORYOL_DEBUG - o_assert_dbg(nullptr == this->d3d11VertexShader); - o_assert_dbg(nullptr == this->d3d11PixelShader); - for (auto cb : this->constantBuffers) { - o_assert_dbg(nullptr == cb); - } -#endif -} - -//------------------------------------------------------------------------------ -void -d3d11Shader::Clear() { - this->d3d11VertexShader = nullptr; - this->d3d11PixelShader = nullptr; - this->constantBuffers.Fill(nullptr); - shaderBase::Clear(); -} - -//------------------------------------------------------------------------------ -void -d3d11Shader::addUniformBlockEntry(ShaderStage::Code bindStage, int bindSlot, ID3D11Buffer* cb) { - o_assert_dbg(cb); - const int cbIndex = (GfxConfig::MaxNumUniformBlocksPerStage * bindStage) + bindSlot; - this->constantBuffers[cbIndex] = cb; -} - -//------------------------------------------------------------------------------ -ID3D11Buffer* -d3d11Shader::getConstantBuffer(ShaderStage::Code bindStage, int bindSlot) const { - const int cbIndex = (GfxConfig::MaxNumUniformBlocksPerStage * bindStage) + bindSlot; - return this->constantBuffers[cbIndex]; -} - -//============================================================================== -d3d11Texture::~d3d11Texture() { - o_assert_dbg(nullptr == this->d3d11Texture2D); - o_assert_dbg(nullptr == this->d3d11ShaderResourceView); - o_assert_dbg(nullptr == this->d3d11SamplerState); - o_assert_dbg(nullptr == this->d3d11DepthStencilTexture); - o_assert_dbg(nullptr == this->d3d11MSAATexture2D); - o_assert_dbg(DXGI_FORMAT_UNKNOWN == this->d3d11ColorFormat); -} - -//------------------------------------------------------------------------------ -void -d3d11Texture::Clear() { - textureBase::Clear(); - this->d3d11Texture2D = nullptr; - this->d3d11ShaderResourceView = nullptr; - this->d3d11SamplerState = nullptr; - this->d3d11DepthStencilTexture = nullptr; - this->d3d11MSAATexture2D = nullptr; - this->d3d11ColorFormat = DXGI_FORMAT_UNKNOWN; -} - -//============================================================================== -d3d11RenderPass::d3d11RenderPass() { - this->d3d11RenderTargetViews.Fill(nullptr); -} - -//------------------------------------------------------------------------------ -d3d11RenderPass::~d3d11RenderPass() { - #if ORYOL_DEBUG - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - o_assert(nullptr == this->d3d11RenderTargetViews[i]); - } - o_assert(nullptr == this->d3d11DepthStencilView); - #endif -} - -//------------------------------------------------------------------------------ -void -d3d11RenderPass::Clear() { - renderPassBase::Clear(); - this->d3d11RenderTargetViews.Fill(nullptr); - this->d3d11DepthStencilView = nullptr; -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Resource.h b/code/Modules/Gfx/private/d3d11/d3d11Resource.h deleted file mode 100644 index ccc0b61f2..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Resource.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -#include "Gfx/private/resourceBase.h" -#include "Gfx/GfxConfig.h" -#include "Core/Containers/StaticArray.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/d3d11/d3d11_decl.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Mesh - @ingroup _priv - @brief D3D11 implementation of mesh -*/ -class d3d11Mesh : public meshBase { -public: - /// destructor - ~d3d11Mesh(); - - /// clear the object (called from meshFactory::DestroyResource()) - void Clear(); - - ID3D11Buffer* d3d11VertexBuffer = nullptr; - ID3D11Buffer* d3d11IndexBuffer = nullptr; - int vbUpdateFrameIndex = -1; - int ibUpdateFrameIndex = -1; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Pipeline - @ingroup _priv - @brief D3D11 implementation of pipeline -*/ -class d3d11Pipeline : public pipelineBase { -public: - /// constructor - d3d11Pipeline(); - /// destructor - ~d3d11Pipeline(); - - /// clear the object (called from pipelineFactory::DestroyResource()) - void Clear(); - - /// pointer to input layout object - ID3D11InputLayout* d3d11InputLayout; - /// rasterize state object - ID3D11RasterizerState* d3d11RasterizerState; - /// primitive topology - D3D_PRIMITIVE_TOPOLOGY d3d11PrimTopology; - /// depth-stencil state object - ID3D11DepthStencilState* d3d11DepthStencilState; - /// blend state object - ID3D11BlendState* d3d11BlendState; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Shader - @ingroup _priv - @brief D3D11 implementation of shader -*/ -class d3d11Shader : public shaderBase { -public: - /// constructor - d3d11Shader(); - /// destructor - ~d3d11Shader(); - - /// clear the object - void Clear(); - - /// the D3D11 vertex shader - ID3D11VertexShader* d3d11VertexShader; - /// the D3D1 pixel shader - ID3D11PixelShader* d3d11PixelShader; - /// add a uniform block entry - void addUniformBlockEntry(ShaderStage::Code bindStage, int bindSlot, ID3D11Buffer* cb); - /// get uniform block constant buffer at bind stage and slot (can return nullptr) - ID3D11Buffer* getConstantBuffer(ShaderStage::Code bindStage, int bindSlot) const; - - static const int NumConstantBuffers = ShaderStage::NumShaderStages * GfxConfig::MaxNumUniformBlocksPerStage; - StaticArray constantBuffers; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Texture - @ingroup _priv - @brief D3D11 implementation of texture -*/ -class d3d11Texture : public textureBase { -public: - /// destructor - ~d3d11Texture(); - - /// clear the object - void Clear(); - - /// d3d11 2D texture object - ID3D11Texture2D* d3d11Texture2D = nullptr; - /// d3d11 3D texture object - ID3D11Texture3D* d3d11Texture3D = nullptr; - /// d3d11 shader resource view object - ID3D11ShaderResourceView* d3d11ShaderResourceView = nullptr; - /// d3d11 sampler state object - ID3D11SamplerState* d3d11SamplerState = nullptr; - /// d3d11 depth-stencil texture (if render target with depth buffer) - ID3D11Texture2D* d3d11DepthStencilTexture = nullptr; - /// d3d11 MSAA texture (optional) - ID3D11Texture2D* d3d11MSAATexture2D = nullptr; - /// d3d11 color texture format - DXGI_FORMAT d3d11ColorFormat = (DXGI_FORMAT) 0; // DXGI_FORMAT_UNKNOWN -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11RenderPass - @ingroup _priv - @brief D3D11 implementation of renderPass -*/ -class d3d11RenderPass : public renderPassBase { -public: - /// constructor - d3d11RenderPass(); - /// destructor - ~d3d11RenderPass(); - - /// clear the object - void Clear(); - - /// the d3d11 render-target-view objects - StaticArray d3d11RenderTargetViews; - /// the optional depth-stencil view object - ID3D11DepthStencilView* d3d11DepthStencilView = nullptr; -}; - -} // namespace Oryol -} // namespace _priv diff --git a/code/Modules/Gfx/private/d3d11/d3d11Types.cc b/code/Modules/Gfx/private/d3d11/d3d11Types.cc deleted file mode 100644 index 5f0634722..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Types.cc +++ /dev/null @@ -1,305 +0,0 @@ -//------------------------------------------------------------------------------ -// d3d11Types.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "d3d11Types.h" -#include "d3d11_impl.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -DXGI_FORMAT -d3d11Types::asSwapChainFormat(PixelFormat::Code pf) { - switch (pf) { - case PixelFormat::RGBA8: - return DXGI_FORMAT_R8G8B8A8_UNORM; - default: - o_error("d3d11Types::asSwapChainFormat: not a valid D3D11 display pixel format\n"); - return DXGI_FORMAT_UNKNOWN; - } -} - -//------------------------------------------------------------------------------ -DXGI_FORMAT -d3d11Types::asRenderTargetFormat(PixelFormat::Code pf) { - switch (pf) { - case PixelFormat::RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case PixelFormat::RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case PixelFormat::RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case PixelFormat::R10G10B10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; - case PixelFormat::DEPTH: return DXGI_FORMAT_D16_UNORM; - case PixelFormat::DEPTHSTENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; - default: - o_error("d3d11Types::asRenderTargetFormat(): invalid pixel format!\n"); - return DXGI_FORMAT_UNKNOWN; - } -} - -//------------------------------------------------------------------------------ -DXGI_FORMAT -d3d11Types::asTextureFormat(PixelFormat::Code pf) { - switch(pf) { - case PixelFormat::RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; - case PixelFormat::RGBA4: return DXGI_FORMAT_B4G4R4A4_UNORM; - case PixelFormat::R5G6B5: return DXGI_FORMAT_B5G6R5_UNORM; - case PixelFormat::R5G5B5A1: return DXGI_FORMAT_B5G5R5A1_UNORM; - case PixelFormat::RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case PixelFormat::RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case PixelFormat::R10G10B10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; - case PixelFormat::L8: return DXGI_FORMAT_R8_UNORM; - case PixelFormat::DXT1: return DXGI_FORMAT_BC1_UNORM; - case PixelFormat::DXT3: return DXGI_FORMAT_BC2_UNORM; - case PixelFormat::DXT5: return DXGI_FORMAT_BC3_UNORM; - case PixelFormat::DEPTH: return DXGI_FORMAT_D16_UNORM; - case PixelFormat::DEPTHSTENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; - default: - return DXGI_FORMAT_UNKNOWN; - } -} - -//------------------------------------------------------------------------------ -D3D11_USAGE -d3d11Types::asResourceUsage(Usage::Code usage) { - switch (usage) { - case Usage::Immutable: return D3D11_USAGE_IMMUTABLE; - case Usage::Dynamic: return D3D11_USAGE_DYNAMIC; - case Usage::Stream: return D3D11_USAGE_DYNAMIC; - default: - o_error("invalid usage\n"); - return D3D11_USAGE_IMMUTABLE; - } -} - -//------------------------------------------------------------------------------ -uint32_t -d3d11Types::asResourceCPUAccessFlag(Usage::Code usage) { - switch (usage) { - case Usage::Immutable: return 0; - case Usage::Dynamic: return D3D11_CPU_ACCESS_WRITE; - case Usage::Stream: return D3D11_CPU_ACCESS_WRITE; - default: - o_error("invalid usage\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -const char* -d3d11Types::asSemanticName(VertexAttr::Code attr) { - // not a bug, SPIRV-Cross uses TEXCOORD as universal semantic name - return "TEXCOORD"; -} - -//------------------------------------------------------------------------------ -uint32_t -d3d11Types::asSemanticIndex(VertexAttr::Code attr) { - // not a bug, see oryol-shdc tool - return (uint32_t) attr; -} - -//------------------------------------------------------------------------------ -DXGI_FORMAT -d3d11Types::asInputElementFormat(VertexFormat::Code fmt) { - switch (fmt) { - case VertexFormat::Short2: return DXGI_FORMAT_R16G16_SINT; - case VertexFormat::Float: return DXGI_FORMAT_R32_FLOAT; - case VertexFormat::Float2: return DXGI_FORMAT_R32G32_FLOAT; - case VertexFormat::Float3: return DXGI_FORMAT_R32G32B32_FLOAT; - case VertexFormat::Float4: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case VertexFormat::Byte4: return DXGI_FORMAT_R8G8B8A8_SINT; - case VertexFormat::Byte4N: return DXGI_FORMAT_R8G8B8A8_SNORM; - case VertexFormat::UByte4: return DXGI_FORMAT_R8G8B8A8_UINT; - case VertexFormat::UByte4N: return DXGI_FORMAT_R8G8B8A8_UNORM; - case VertexFormat::Short2N: return DXGI_FORMAT_R16G16_SNORM; - case VertexFormat::Short4: return DXGI_FORMAT_R16G16B16A16_SINT; - case VertexFormat::Short4N: return DXGI_FORMAT_R16G16B16A16_SNORM; - default: - o_error("d3d11Types::asInputElementFormat: invalid vertex format!\n"); - return DXGI_FORMAT_UNKNOWN; - } -} - -//------------------------------------------------------------------------------ -D3D11_CULL_MODE -d3d11Types::asCullMode(bool cullEnabled, Face::Code face) { - if (cullEnabled) { - if (Face::Front == face) { - return D3D11_CULL_FRONT; - } - else { - return D3D11_CULL_BACK; - } - } - else { - return D3D11_CULL_NONE; - } -} - -//------------------------------------------------------------------------------ -D3D11_COMPARISON_FUNC -d3d11Types::asComparisonFunc(CompareFunc::Code func) { - switch (func) { - case CompareFunc::Never: return D3D11_COMPARISON_NEVER; - case CompareFunc::Less: return D3D11_COMPARISON_LESS; - case CompareFunc::Equal: return D3D11_COMPARISON_EQUAL; - case CompareFunc::LessEqual: return D3D11_COMPARISON_LESS_EQUAL; - case CompareFunc::Greater: return D3D11_COMPARISON_GREATER; - case CompareFunc::NotEqual: return D3D11_COMPARISON_NOT_EQUAL; - case CompareFunc::GreaterEqual: return D3D11_COMPARISON_GREATER_EQUAL; - default: return D3D11_COMPARISON_ALWAYS; - } -} - -//------------------------------------------------------------------------------ -D3D11_STENCIL_OP -d3d11Types::asStencilOp(StencilOp::Code op) { - switch (op) { - case StencilOp::Keep: return D3D11_STENCIL_OP_KEEP; - case StencilOp::Zero: return D3D11_STENCIL_OP_ZERO; - case StencilOp::Replace: return D3D11_STENCIL_OP_REPLACE; - case StencilOp::IncrClamp: return D3D11_STENCIL_OP_INCR_SAT; - case StencilOp::DecrClamp: return D3D11_STENCIL_OP_DECR_SAT; - case StencilOp::Invert: return D3D11_STENCIL_OP_INVERT; - case StencilOp::IncrWrap: return D3D11_STENCIL_OP_INCR; - default: return D3D11_STENCIL_OP_DECR; - } -} - -//------------------------------------------------------------------------------ -D3D11_BLEND -d3d11Types::asBlendFactor(BlendFactor::Code b) { - switch (b) { - case BlendFactor::Zero: return D3D11_BLEND_ZERO; - case BlendFactor::One: return D3D11_BLEND_ONE; - case BlendFactor::SrcColor: return D3D11_BLEND_SRC_COLOR; - case BlendFactor::OneMinusSrcColor: return D3D11_BLEND_INV_SRC_COLOR; - case BlendFactor::SrcAlpha: return D3D11_BLEND_SRC_ALPHA; - case BlendFactor::OneMinusSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA; - case BlendFactor::DstColor: return D3D11_BLEND_DEST_COLOR; - case BlendFactor::OneMinusDstColor: return D3D11_BLEND_INV_DEST_COLOR; - case BlendFactor::DstAlpha: return D3D11_BLEND_DEST_ALPHA; - case BlendFactor::OneMinusDstAlpha: return D3D11_BLEND_INV_DEST_ALPHA; - case BlendFactor::SrcAlphaSaturated: return D3D11_BLEND_SRC_ALPHA_SAT; - case BlendFactor::BlendColor: return D3D11_BLEND_BLEND_FACTOR; - case BlendFactor::OneMinusBlendColor: return D3D11_BLEND_INV_BLEND_FACTOR; - case BlendFactor::BlendAlpha: return D3D11_BLEND_BLEND_FACTOR; - case BlendFactor::OneMinusBlendAlpha: return D3D11_BLEND_INV_BLEND_FACTOR; - default: return D3D11_BLEND_ONE; - } -} - -//------------------------------------------------------------------------------ -D3D11_BLEND_OP -d3d11Types::asBlendOp(BlendOperation::Code op) { - // FIXME: D3D11 also has MIN and MAX - switch (op) { - case BlendOperation::Add: return D3D11_BLEND_OP_ADD; - case BlendOperation::Subtract: return D3D11_BLEND_OP_SUBTRACT; - case BlendOperation::ReverseSubtract: return D3D11_BLEND_OP_REV_SUBTRACT; - default: return D3D11_BLEND_OP_ADD; - } -} - -//------------------------------------------------------------------------------ -uint8_t -d3d11Types::asColorWriteMask(PixelChannel::Mask mask) { - uint8_t res = 0; - if (mask & PixelChannel::Red) res |= D3D11_COLOR_WRITE_ENABLE_RED; - if (mask & PixelChannel::Green) res |= D3D11_COLOR_WRITE_ENABLE_GREEN; - if (mask & PixelChannel::Blue) res |= D3D11_COLOR_WRITE_ENABLE_BLUE; - if (mask & PixelChannel::Alpha) res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; - return res; -} - -//------------------------------------------------------------------------------ -D3D11_INPUT_CLASSIFICATION -d3d11Types::asInputClassification(VertexStepFunction::Code func) { - return func == VertexStepFunction::PerVertex ? D3D11_INPUT_PER_VERTEX_DATA : D3D11_INPUT_PER_INSTANCE_DATA; -} - -//------------------------------------------------------------------------------ -D3D11_FILTER -d3d11Types::asSamplerFilter(TextureFilterMode::Code magFilter, TextureFilterMode::Code minFilter) { - if (TextureFilterMode::Nearest == magFilter) { - switch (minFilter) { - case TextureFilterMode::Nearest: - case TextureFilterMode::NearestMipmapNearest: - return D3D11_FILTER_MIN_MAG_MIP_POINT; - case TextureFilterMode::Linear: - case TextureFilterMode::LinearMipmapNearest: - return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - case TextureFilterMode::NearestMipmapLinear: - return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - case TextureFilterMode::LinearMipmapLinear: - return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - default: - break; - } - } - else if (TextureFilterMode::Linear == magFilter) { - switch (magFilter) { - case TextureFilterMode::Nearest: - case TextureFilterMode::NearestMipmapNearest: - return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - case TextureFilterMode::Linear: - case TextureFilterMode::LinearMipmapNearest: - return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - case TextureFilterMode::NearestMipmapLinear: - return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - case TextureFilterMode::LinearMipmapLinear: - return D3D11_FILTER_MIN_MAG_MIP_LINEAR; - default: - break; - } - } - // fallthrough: invalid filter combination - o_error("d3d11Types::asFilter(): invalid filter combination!\n"); - return D3D11_FILTER_MIN_MAG_MIP_POINT; -} - -//------------------------------------------------------------------------------ -D3D11_TEXTURE_ADDRESS_MODE -d3d11Types::asTextureAddressMode(TextureWrapMode::Code mode) { - switch (mode) { - case TextureWrapMode::ClampToEdge: - return D3D11_TEXTURE_ADDRESS_CLAMP; - case TextureWrapMode::Repeat: - return D3D11_TEXTURE_ADDRESS_WRAP; - case TextureWrapMode::MirroredRepeat: - return D3D11_TEXTURE_ADDRESS_MIRROR; - default: - return D3D11_TEXTURE_ADDRESS_CLAMP; - } -} - -//------------------------------------------------------------------------------ -DXGI_FORMAT -d3d11Types::asIndexType(IndexType::Code c) { - switch (c) { - case IndexType::None: return DXGI_FORMAT_UNKNOWN; // this is a valid return type! - case IndexType::Index16: return DXGI_FORMAT_R16_UINT; - case IndexType::Index32: return DXGI_FORMAT_R32_UINT; - default: - o_error("d3d11Types::asIndexType(): invalid value!\n"); - return DXGI_FORMAT_UNKNOWN; - } -} - -//------------------------------------------------------------------------------ -D3D11_PRIMITIVE_TOPOLOGY -d3d11Types::asPrimitiveTopology(PrimitiveType::Code c) { - switch (c) { - case PrimitiveType::Points: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; - case PrimitiveType::Lines: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; - case PrimitiveType::LineStrip: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; - case PrimitiveType::Triangles: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - case PrimitiveType::TriangleStrip: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; - default: - o_error("d3d11Types::asPrimitiveTopology(): invalid value!\n"); - return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; - } -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/d3d11/d3d11Types.h b/code/Modules/Gfx/private/d3d11/d3d11Types.h deleted file mode 100644 index 00433e3c3..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11Types.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::d3d11Types - @ingroup _priv - @brief D3D11 enum conversion helpers -*/ -#include "Core/Types.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/d3d11/d3d11_decl.h" - -namespace Oryol { -namespace _priv { - -class d3d11Types { -public: - /// convert PixelFormat to a display DXGI format - static DXGI_FORMAT asSwapChainFormat(PixelFormat::Code pixelFormat); - /// convert PixelFormat to a rendertarget DXGI format - static DXGI_FORMAT asRenderTargetFormat(PixelFormat::Code pixelFormat); - /// convert PixelFormat to a D3D11 texture format - static DXGI_FORMAT asTextureFormat(PixelFormat::Code pixelFormat); - /// convert Usage to a d3d11 usage for buffers and textures - static D3D11_USAGE asResourceUsage(Usage::Code usage); - /// convert Usage to a d3d11 CPU access flags mask - static uint32_t asResourceCPUAccessFlag(Usage::Code usage); - /// convert vertex attribute to d3d11 semantic name - static const char* asSemanticName(VertexAttr::Code attr); - /// convert vertex attribute to d3d11 semantic index - static uint32_t asSemanticIndex(VertexAttr::Code attr); - /// convert vertex component format to d3d11 input element DXGI_FORMAT - static DXGI_FORMAT asInputElementFormat(VertexFormat::Code fmt); - /// convert polygon face to d3d11 cull mode - static D3D11_CULL_MODE asCullMode(bool cullEnabled, Face::Code face); - /// convert CompareFunc to d3d11 comparison func - static D3D11_COMPARISON_FUNC asComparisonFunc(CompareFunc::Code func); - /// convert stencil-op to d3d11 stencil-op - static D3D11_STENCIL_OP asStencilOp(StencilOp::Code op); - /// convert blend factor to d3d11 blend factor - static D3D11_BLEND asBlendFactor(BlendFactor::Code b); - /// convert blend operation to d3d11 blend op - static D3D11_BLEND_OP asBlendOp(BlendOperation::Code op); - /// convert PixelChannel::Mask to d3d11 color write mask - static uint8_t asColorWriteMask(PixelChannel::Mask mask); - /// convert VertexStepFunction to d3d11 input classification - static D3D11_INPUT_CLASSIFICATION asInputClassification(VertexStepFunction::Code func); - /// convert min/mag filters to d3d11 filter - static D3D11_FILTER asSamplerFilter(TextureFilterMode::Code magFilter, TextureFilterMode::Code minFilter); - /// convert texture wrap mode to d3d11 texture address mode - static D3D11_TEXTURE_ADDRESS_MODE asTextureAddressMode(TextureWrapMode::Code mode); - /// convert vertex index type to dxgi format - static DXGI_FORMAT asIndexType(IndexType::Code c); - /// convert primitive type - static D3D11_PRIMITIVE_TOPOLOGY asPrimitiveTopology(PrimitiveType::Code c); -}; - -} // namespace _priv -} // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Gfx/private/d3d11/d3d11_decl.h b/code/Modules/Gfx/private/d3d11/d3d11_decl.h deleted file mode 100644 index e30cd73ca..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11_decl.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @file d3d11_decl.h - @brief D3D11 forward declarations - - Try to use this as much as possible in headers instead of including - the full d3d11 headers. -*/ -#include "Gfx/private/win/win_decl.h" - -struct IDXGISwapChain; -struct IDXGISwapChain1; -struct DXGI_SWAP_CHAIN_DESC; -struct DXGI_SWAP_CHAIN_DESC1; - -struct ID3D11Device; -struct ID3D11DeviceContext; -struct ID3D11Resource; -struct ID3D11Texture2D; -struct ID3D11Texture3D; -struct ID3D11ShaderResourceView; -struct ID3D11RenderTargetView; -struct ID3D11DepthStencilView; -struct ID3D11Buffer; -struct ID3D11VertexShader; -struct ID3D11PixelShader; -struct ID3D11InputLayout; -struct ID3D11RasterizerState; -struct ID3D11DepthStencilState; -struct ID3D11BlendState; -struct ID3D11SamplerState; -typedef struct D3D11_TEXTURE2D_DESC D3D11_TEXTURE2D_DESC; - -enum D3D11_USAGE; -typedef enum DXGI_FORMAT DXGI_FORMAT; -typedef enum D3D11_CULL_MODE D3D11_CULL_MODE; -typedef enum D3D11_COMPARISON_FUNC D3D11_COMPARISON_FUNC; -typedef enum D3D11_STENCIL_OP D3D11_STENCIL_OP; -typedef enum D3D11_BLEND D3D11_BLEND; -typedef enum D3D11_BLEND_OP D3D11_BLEND_OP; -typedef enum D3D11_INPUT_CLASSIFICATION D3D11_INPUT_CLASSIFICATION; -typedef enum D3D11_FILTER D3D11_FILTER; -typedef enum D3D11_TEXTURE_ADDRESS_MODE D3D11_TEXTURE_ADDRESS_MODE; -typedef enum D3D_PRIMITIVE_TOPOLOGY D3D_PRIMITIVE_TOPOLOGY; -typedef D3D_PRIMITIVE_TOPOLOGY D3D11_PRIMITIVE_TOPOLOGY; -typedef struct D3D11_SUBRESOURCE_DATA D3D11_SUBRESOURCE_DATA; diff --git a/code/Modules/Gfx/private/d3d11/d3d11_impl.h b/code/Modules/Gfx/private/d3d11/d3d11_impl.h deleted file mode 100644 index e4f75abe5..000000000 --- a/code/Modules/Gfx/private/d3d11/d3d11_impl.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @file d3d11_impl.h - @brief includes the D3D11 headers - - Include this file in sources, and d3d11_decl.h in headers. -*/ -#define NOMINMAX -#include \ No newline at end of file diff --git a/code/Modules/Gfx/private/win/winDisplayMgr.cc b/code/Modules/Gfx/private/d3d11/winDisplayMgr.cc similarity index 96% rename from code/Modules/Gfx/private/win/winDisplayMgr.cc rename to code/Modules/Gfx/private/d3d11/winDisplayMgr.cc index bd40ba67a..ee37ceb22 100644 --- a/code/Modules/Gfx/private/win/winDisplayMgr.cc +++ b/code/Modules/Gfx/private/d3d11/winDisplayMgr.cc @@ -68,24 +68,22 @@ winDisplayMgr::~winDisplayMgr() { //------------------------------------------------------------------------------ void -winDisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs, const char* windowTitlePostfix) { +winDisplayMgr::SetupDisplay(const GfxDesc& desc, const char* windowTitlePostfix) { o_assert(!this->IsDisplayValid()); - displayMgrBase::SetupDisplay(setup, ptrs); + displayMgrBase::SetupDisplay(desc); - this->initDPI(setup.HighDPI); + this->initDPI(desc.highDPI); this->registerWindowClass(); this->createWindow(windowTitlePostfix); // get actual size and update display attrs int actWidth, actHeight; this->winGetWindowSize(&actWidth, &actHeight); - this->displayAttrs.WindowWidth = int(actWidth / this->windowScale); - this->displayAttrs.WindowHeight = int(actHeight / this->windowScale); - this->displayAttrs.FramebufferWidth = int(this->displayAttrs.WindowWidth * this->contentScale); - this->displayAttrs.FramebufferHeight = int(this->displayAttrs.WindowHeight * this->contentScale); - this->curFramebufferWidth = this->displayAttrs.FramebufferWidth; - this->curFramebufferHeight = this->displayAttrs.FramebufferHeight; + this->displayAttrs.Width = int(this->displayAttrs.Width * this->contentScale); + this->displayAttrs.Height = int(this->displayAttrs.Height * this->contentScale); + this->curFramebufferWidth = this->displayAttrs.Width; + this->curFramebufferHeight = this->displayAttrs.Height; } //------------------------------------------------------------------------------ @@ -185,7 +183,7 @@ winDisplayMgr::createWindow(const char* titlePostFix) { // setup window style flags this->dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; this->dwExStyle = WS_EX_APPWINDOW; - if (this->gfxSetup.Windowed) { + if (this->gfxDesc.windowed) { this->dwStyle |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; this->dwExStyle |= WS_EX_WINDOWEDGE; } @@ -194,9 +192,9 @@ winDisplayMgr::createWindow(const char* titlePostFix) { } int width, height; - this->computeWindowSize(this->gfxSetup.Width, this->gfxSetup.Height, width, height); + this->computeWindowSize(this->gfxDesc.width, this->gfxDesc.height, width, height); - StringBuilder strBuilder(this->gfxSetup.Title); + StringBuilder strBuilder(this->gfxDesc.title); strBuilder.Append(titlePostFix); WideString title = StringConverter::UTF8ToWide(strBuilder.AsCStr()); @@ -314,15 +312,21 @@ winDisplayMgr::checkWindowResize() { const int crHeight = (curClientRect.bottom - curClientRect.top); const int windowWidth = crWidth / this->windowScale; const int windowHeight = crHeight / this->windowScale; + int fbWidth = (crWidth * this->contentScale) / this->windowScale; + int fbHeight = (crHeight * this->contentScale) / this->windowScale; + // width and height will be 0 if the windows is minimized, set the size to one in this case + if (fbWidth == 0) { + fbWidth = 1; + } + if (fbHeight == 0) { + fbHeight = 1; + } // NOTE: this method is not called when minimized, or restored from minimized - if ((windowWidth != this->displayAttrs.WindowWidth) || (windowHeight != this->displayAttrs.WindowHeight)) { - - this->displayAttrs.WindowWidth = windowWidth; - this->displayAttrs.WindowHeight = windowHeight; - this->displayAttrs.FramebufferWidth = int(windowWidth * this->contentScale); - this->displayAttrs.FramebufferHeight = int(windowHeight * this->contentScale); + if ((fbWidth != this->displayAttrs.Width) || (fbHeight != this->displayAttrs.Height)) { + this->displayAttrs.Width = fbWidth; + this->displayAttrs.Height = fbHeight; this->inputFramebufferSize(crWidth, crHeight); this->inputWindowSize(crWidth, crHeight); this->onWindowDidResize(); diff --git a/code/Modules/Gfx/private/win/winInputDefs.h b/code/Modules/Gfx/private/d3d11/winDisplayMgr.h similarity index 53% rename from code/Modules/Gfx/private/win/winInputDefs.h rename to code/Modules/Gfx/private/d3d11/winDisplayMgr.h index abf9cc95d..1bdaffd1c 100644 --- a/code/Modules/Gfx/private/win/winInputDefs.h +++ b/code/Modules/Gfx/private/d3d11/winDisplayMgr.h @@ -1,15 +1,42 @@ #pragma once //------------------------------------------------------------------------------ /** - @brief input-related defines on Windows (taken from and compatible with GLFW) + @class Oryol::_priv::winDisplayMgr + @ingroup _priv + @brief common display manager functionality for Windows D3D11 renders + + NOTE: most of the window and messaging code is taken from GLFW3! */ -#define ORYOL_WIN_WHEEL_DELTA 120 +#include "Gfx/private/displayMgrBase.h" + +// Windows.h forward declarations +#if defined(_WIN64) +typedef __int64 INT_PTR, *PINT_PTR; +typedef unsigned __int64 UINT_PTR, *PUINT_PTR; +typedef __int64 LONG_PTR, *PLONG_PTR; +typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; +#define __int3264 __int64 +#else +typedef int INT_PTR, *PINT_PTR; +typedef unsigned int UINT_PTR, *PUINT_PTR; +typedef long LONG_PTR, *PLONG_PTR; +typedef unsigned long ULONG_PTR, *PULONG_PTR; +#define __int3264 __int32 +#endif +typedef void* HANDLE; +typedef unsigned long DWORD; +struct HWND__; +typedef HWND__* HWND; +typedef UINT_PTR WPARAM; +typedef LONG_PTR LPARAM; +typedef LONG_PTR LRESULT; +typedef ULONG_PTR SIZE_T, *PSIZE_T; +#define ORYOL_WIN_WHEEL_DELTA 120 #define ORYOL_WIN_MOD_SHIFT 0x0001 #define ORYOL_WIN_MOD_CONTROL 0x0002 #define ORYOL_WIN_MOD_ALT 0x0004 #define ORYOL_WIN_MOD_SUPER 0x0008 - #define ORYOL_WIN_MOUSE_BUTTON_1 0 #define ORYOL_WIN_MOUSE_BUTTON_2 1 #define ORYOL_WIN_MOUSE_BUTTON_3 2 @@ -22,19 +49,15 @@ #define ORYOL_WIN_MOUSE_BUTTON_LEFT ORYOL_WIN_MOUSE_BUTTON_1 #define ORYOL_WIN_MOUSE_BUTTON_RIGHT ORYOL_WIN_MOUSE_BUTTON_2 #define ORYOL_WIN_MOUSE_BUTTON_MIDDLE ORYOL_WIN_MOUSE_BUTTON_3 - #define ORYOL_WIN_RELEASE 0 #define ORYOL_WIN_PRESS 1 #define ORYOL_WIN_REPEAT 2 - #define ORYOL_WIN_CURSOR 0x00033001 #define ORYOL_WIN_STICKY_KEYS 0x00033002 #define ORYOL_WIN_STICKY_MOUSE_BUTTONS 0x00033003 - #define ORYOL_WIN_CURSOR_NORMAL 0x00034001 #define ORYOL_WIN_CURSOR_HIDDEN 0x00034002 #define ORYOL_WIN_CURSOR_DISABLED 0x00034003 - #define ORYOL_WIN_KEY_INVALID -2 #define ORYOL_WIN_KEY_UNKNOWN -1 #define ORYOL_WIN_KEY_SPACE 32 @@ -159,4 +182,146 @@ #define ORYOL_WIN_KEY_MENU 348 #define ORYOL_WIN_KEY_LAST ORYOL_WIN_KEY_MENU +namespace Oryol { +namespace _priv { + +class winDisplayMgr : public displayMgrBase { +public: + /// constructor + winDisplayMgr(); + /// destructor + ~winDisplayMgr(); + + /// setup the display system, must happen before rendering + void SetupDisplay(const GfxDesc& desc, const char* windowTitlePostfix); + /// discard the display, rendering cannot happen after + void DiscardDisplay(); + /// process window system events (call near start of frame) + void ProcessSystemEvents(); + /// check whether the window system requests to quit the application + bool QuitRequested() const; + + /// register the window class + void registerWindowClass(); + /// unregister the window class + void unregisterWindowClass(); + /// create the application window + void createWindow(const char* titlePostFix); + /// destroy the application window + void destroyWindow(); + /// initialize DPI-related stuff + void initDPI(bool highDpiRequested); + + /// compute actual window size from client rect size plus window chrome + void computeWindowSize(int clientWidth, int clientHeight, int& outWidth, int& outHeight); + /// setup the key translation table + void setupKeyTranslationTable(); + + /// set input mode (called from d3d11InputMgr) + void setInputMode(int mode, int value); + /// set cursor mode (called from setInputMode) + void setCursorMode(int newMode); + /// lowlevel set cursor mode + void winSetCursorMode(int mode); + /// lowlevel set cursor pos + void winSetCursorPos(double xpos, double ypos); + /// lowlevel get cursor pos + void winGetCursorPos(double* xpos, double* ypos); + /// lowlevel get window size + void winGetWindowSize(int* width, int* height); + /// update cursor clip rect + void updateClipCursor(); + + /// get modifier keys + int inputGetKeyMods(); + /// translate key code from WM wParam, lParam + int inputTranslateKey(WPARAM wParam, LPARAM lParam); + /// called from WinProc when key has been pressed + void inputKey(int keyCode, int scanCode, int action, int mods); + /// called from WinProc on WM_CHAR + void inputChar(unsigned int codePoint, int mods, int plain); + // called from WinProc on WM mouse button messages + void inputMouseClick(int button, int action, int mods); + /// called from WinProc window focus messages + void inputWindowFocus(bool focused); + /// called on mouse moved + void inputCursorMotion(double x, double y); + /// cursor enters/leaves windows, called from winproc + void inputCursorEnter(bool entered); + /// called from winproc on mouse wheel + void inputScroll(double x, double y); + /// called on WM_SIZE + void inputFramebufferSize(int width, int height); + /// called on WM_SIZE + void inputWindowSize(int width, int height); + /// called on WM_MOVE + void inputWindowPos(int xpos, int ypos); + /// window has been iconified/restored + void inputWindowIconify(bool iconified); + + /// check if window size was changed, and track those changes + void checkWindowResize(); + /// called from checkWindowResize when window did actually resize + virtual void onWindowDidResize(); + + static winDisplayMgr* self; + + bool quitRequested; + HWND hwnd; + DWORD dwStyle; + DWORD dwExStyle; + bool inCreateWindow; + bool dpiAware; + int windowScale; + int contentScale; + + int cursorMode; + double cursorPosX; + double cursorPosY; + double windowCursorPosX; + double windowCursorPosY; + int win32CursorPosX; + int win32CursorPosY; + bool cursorTracked; + bool iconified; + char mouseButtons[ORYOL_WIN_MOUSE_BUTTON_LAST + 1]; + char keys[ORYOL_WIN_KEY_LAST + 1]; + short int publicKeys[512]; // key-code translation table + + // callback signatures (see glfw3.h) + typedef void(*windowposfun)(int, int); + typedef void(*windowsizefun)(int, int); + typedef void(*windowclosefun)(); + typedef void(*windowrefreshfun)(); + typedef void(*windowfocusfun)(int); + typedef void(*windowiconifyfun)(int); + typedef void(*framebuffersizefun)(int, int); + typedef void(*mousebuttonfun)(int, int, int); + typedef void(*cursorposfun)(double, double); + typedef void(*cursorenterfun)(int); + typedef void(*scrollfun)(double, double); + typedef void(*keyfun)(int, int, int, int); + typedef void(*charfun)(unsigned int); + typedef void(*charmodsfun)(unsigned int, int); + + // callback pointers, these are usually populated by the Oryol Input module + struct callbackTable { + windowposfun pos; + windowsizefun size; + windowclosefun close; + windowrefreshfun refresh; + windowfocusfun focus; + windowiconifyfun iconify; + framebuffersizefun fbsize; + mousebuttonfun mouseButton; + cursorposfun cursorPos; + cursorenterfun cursorEnter; + scrollfun scroll; + keyfun key; + charfun character; + charmodsfun charmods; + } callbacks; +}; +} // namespace _priv +} // namespace Oryol diff --git a/code/Modules/Gfx/private/displayMgr.h b/code/Modules/Gfx/private/displayMgr.h index c062f5120..6e6f0cb55 100644 --- a/code/Modules/Gfx/private/displayMgr.h +++ b/code/Modules/Gfx/private/displayMgr.h @@ -16,31 +16,31 @@ namespace _priv { class displayMgr : public d3d11DisplayMgr { }; } } #elif ORYOL_METAL -#include "Gfx/private/mtl/mtlDisplayMgr.h" +#include "Gfx/private/metal/mtlDisplayMgr.h" namespace Oryol { namespace _priv { class displayMgr : public mtlDisplayMgr { }; } } #elif (ORYOL_ANDROID || ORYOL_RASPBERRYPI) -#include "Gfx/private/egl/eglDisplayMgr.h" +#include "Gfx/private/gl/eglDisplayMgr.h" namespace Oryol { namespace _priv { class displayMgr : public eglDisplayMgr { }; } } #elif (ORYOL_WINDOWS || ORYOL_MACOS || ORYOL_LINUX) -#include "Gfx/private/glfw/glfwDisplayMgr.h" +#include "Gfx/private/gl/glfwDisplayMgr.h" namespace Oryol { namespace _priv { class displayMgr : public glfwDisplayMgr { }; } } #elif ORYOL_EMSCRIPTEN -#include "Gfx/private/emsc/emscDisplayMgr.h" +#include "Gfx/private/gl/emscDisplayMgr.h" namespace Oryol { namespace _priv { class displayMgr : public emscDisplayMgr { }; } } #elif ORYOL_IOS -#include "Gfx/private/ios/iosDisplayMgr.h" +#include "Gfx/private/gl/iosDisplayMgr.h" namespace Oryol { namespace _priv { class displayMgr : public iosDisplayMgr { }; diff --git a/code/Modules/Gfx/private/displayMgrBase.cc b/code/Modules/Gfx/private/displayMgrBase.cc index 8a8607c00..6b7d0adb4 100644 --- a/code/Modules/Gfx/private/displayMgrBase.cc +++ b/code/Modules/Gfx/private/displayMgrBase.cc @@ -21,15 +21,19 @@ displayMgrBase::~displayMgrBase() { This method must be overwritten in a platform-specific subclass. */ void -displayMgrBase::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { +displayMgrBase::SetupDisplay(const GfxDesc& desc) { o_assert(!this->displayValid); - this->displayValid = true; - this->gfxSetup = setup; - this->displayAttrs = setup.GetDisplayAttrs(); - this->pointers = ptrs; - this->curFramebufferWidth = this->displayAttrs.FramebufferWidth; - this->curFramebufferHeight = this->displayAttrs.FramebufferHeight; + this->gfxDesc = desc; + this->displayAttrs.Width = desc.Width; + this->displayAttrs.Height = desc.Height; + this->displayAttrs.ColorFormat = desc.ColorFormat; + this->displayAttrs.DepthFormat = desc.DepthFormat; + this->displayAttrs.SampleCount = desc.SampleCount; + this->displayAttrs.Windowed = desc.Windowed; + this->displayAttrs.SwapInterval = desc.SwapInterval; + this->curFramebufferWidth = this->displayAttrs.Width; + this->curFramebufferHeight = this->displayAttrs.Height; } //------------------------------------------------------------------------------ @@ -37,7 +41,6 @@ void displayMgrBase::DiscardDisplay() { o_assert(this->displayValid); this->displayValid = false; - this->pointers = gfxPointers(); } //------------------------------------------------------------------------------ @@ -46,14 +49,6 @@ displayMgrBase::IsDisplayValid() const { return this->displayValid; } -//------------------------------------------------------------------------------ -void -displayMgrBase::ModifyDisplay(const GfxSetup& setup) { - o_assert(this->displayValid); - this->displayAttrs = setup.GetDisplayAttrs(); - this->notifyEventHandlers(GfxEvent(GfxEvent::DisplayModified, this->displayAttrs)); -} - //------------------------------------------------------------------------------ /** This method is expected to process the platform specific window system @@ -64,11 +59,11 @@ displayMgrBase::ModifyDisplay(const GfxSetup& setup) { */ void displayMgrBase::ProcessSystemEvents() { - if ((this->curFramebufferWidth != this->displayAttrs.FramebufferWidth) || - (this->curFramebufferHeight != this->displayAttrs.FramebufferHeight)) { + if ((this->curFramebufferWidth != this->displayAttrs.Width) || + (this->curFramebufferHeight != this->displayAttrs.Height)) { - this->curFramebufferWidth = this->displayAttrs.FramebufferWidth; - this->curFramebufferHeight = this->displayAttrs.FramebufferHeight; + this->curFramebufferWidth = this->displayAttrs.Width; + this->curFramebufferHeight = this->displayAttrs.Height; this->notifyEventHandlers(GfxEvent(GfxEvent::DisplayModified, this->displayAttrs)); } } diff --git a/code/Modules/Gfx/private/displayMgrBase.h b/code/Modules/Gfx/private/displayMgrBase.h index 1c952c6c4..65df201ea 100644 --- a/code/Modules/Gfx/private/displayMgrBase.h +++ b/code/Modules/Gfx/private/displayMgrBase.h @@ -9,7 +9,6 @@ */ #include "Gfx/GfxTypes.h" #include "Core/Containers/Map.h" -#include "Gfx/private/gfxPointers.h" #include namespace Oryol { @@ -26,13 +25,11 @@ class displayMgrBase { ~displayMgrBase(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// return true if display is currently setup bool IsDisplayValid() const; - /// modify the display settings, may not be supported on all platforms - void ModifyDisplay(const GfxSetup& setup); /// process window system events (call near start of frame) void ProcessSystemEvents(); /// present the current rendered frame @@ -52,11 +49,10 @@ class displayMgrBase { /// notify event handlers, all handlers get the same message object void notifyEventHandlers(const GfxEvent& gfxEvent); - GfxSetup gfxSetup; + GfxDesc gfxDesc; DisplayAttrs displayAttrs; eventHandlerId uniqueIdCounter = 0; Map handlers; - gfxPointers pointers; bool displayValid = false; int curFramebufferWidth = 0; // used to detect display size changes int curFramebufferHeight = 0; // used to detect display size changes diff --git a/code/Modules/Gfx/private/flextgl/flextgl.sh b/code/Modules/Gfx/private/flextgl/flextgl.sh deleted file mode 100755 index 5db687ee4..000000000 --- a/code/Modules/Gfx/private/flextgl/flextgl.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# helper bash script to generate gl files -# -python3 ../../../../../flextgl/flextGLgen.py -T glfw3 -D . flextgl_profile.txt diff --git a/code/Modules/Gfx/private/gfxBackend.h b/code/Modules/Gfx/private/gfxBackend.h new file mode 100644 index 000000000..c71ac3410 --- /dev/null +++ b/code/Modules/Gfx/private/gfxBackend.h @@ -0,0 +1,14 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + @class Oryol::_priv::gfxBackend + @ingroup _priv + @brief wrapper class for gfx backend implementations +*/ +#include "Gfx/private/sokol/sokolGfxBackend.h" + +namespace Oryol { +namespace _priv { +class gfxBackend : public sokolGfxBackend { }; +} // namespace _priv +} // namespace Oryol diff --git a/code/Modules/Gfx/private/gfxFactory.h b/code/Modules/Gfx/private/gfxFactory.h deleted file mode 100644 index f71dd983e..000000000 --- a/code/Modules/Gfx/private/gfxFactory.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -#if ORYOL_OPENGL -#include "Gfx/private/gl/glFactory.h" -#elif ORYOL_D3D11 -#include "Gfx/private/d3d11/d3d11Factory.h" -#elif ORYOL_METAL -#include "Gfx/private/mtl/mtlFactory.h" -#else -#error "Platform not yet supported!" -#endif - -namespace Oryol { -namespace _priv { -#if ORYOL_OPENGL -class gfxFactory : public glFactory { }; -#elif ORYOL_D3D11 -class gfxFactory : public d3d11Factory { }; -#elif ORYOL_METAL -class gfxFactory : public mtlFactory { }; -#endif - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gfxFactoryBase.cc b/code/Modules/Gfx/private/gfxFactoryBase.cc deleted file mode 100644 index cc50e9283..000000000 --- a/code/Modules/Gfx/private/gfxFactoryBase.cc +++ /dev/null @@ -1,86 +0,0 @@ -//------------------------------------------------------------------------------ -// gfxFactoryBase.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "gfxFactoryBase.h" -#include "Gfx/private/resource.h" -#include "Gfx/private/resourcePools.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -gfxFactoryBase::~gfxFactoryBase() { - o_assert_dbg(!this->isValid); -} - -//------------------------------------------------------------------------------ -void -gfxFactoryBase::setup(const gfxPointers& ptrs) { - o_assert_dbg(!this->isValid); - this->pointers = ptrs; - this->isValid = true; -} - -//------------------------------------------------------------------------------ -void -gfxFactoryBase::discard() { - o_assert_dbg(this->isValid); - this->pointers = gfxPointers(); - this->isValid = false; -} - -//------------------------------------------------------------------------------ -void -gfxFactoryBase::garbageCollect() { - // this is only implemented in some subclasses, depending on - // rendering backend -} - -//------------------------------------------------------------------------------ -ResourceState::Code -gfxFactoryBase::initPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - pip.shd = this->pointers.shaderPool->Lookup(pip.Setup.Shader); - o_assert_dbg(pip.shd && (ResourceState::Valid == pip.shd->State)); - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -gfxFactoryBase::destroyPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - pip.Clear(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -gfxFactoryBase::initRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - o_assert_dbg(nullptr == rp.colorTextures[i]); - Id id = rp.Setup.ColorAttachments[i].Texture; - if (id.IsValid()) { - rp.colorTextures[i] = this->pointers.texturePool->Lookup(id); - o_assert_dbg(rp.colorTextures[i] && (ResourceState::Valid == rp.colorTextures[i]->State)); - } - } - o_assert_dbg(nullptr == rp.depthStencilTexture); - Id id = rp.Setup.DepthStencilTexture; - if (id.IsValid()) { - rp.depthStencilTexture = this->pointers.texturePool->Lookup(id); - o_assert_dbg(rp.depthStencilTexture && (ResourceState::Valid == rp.depthStencilTexture->State)); - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -gfxFactoryBase::destroyRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - rp.Clear(); -} - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/gfxFactoryBase.h b/code/Modules/Gfx/private/gfxFactoryBase.h deleted file mode 100644 index b93cebcb4..000000000 --- a/code/Modules/Gfx/private/gfxFactoryBase.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::gfxFactoryBase - @ingroup _priv - @brief base class for Gfx resource factories -*/ -#include "Resource/ResourceState.h" -#include "Gfx/private/gfxPointers.h" -#include "Gfx/GfxTypes.h" - -namespace Oryol { -namespace _priv { - -class pipeline; -class renderPass; - -class gfxFactoryBase { -public: - /// destructor - ~gfxFactoryBase(); - - /// setup the factory - void setup(const gfxPointers& ptrs); - /// discard the factory - void discard(); - /// optional garbage-collect released resources (not implemented on all platforms) - void garbageCollect(); - /// initialize pipeline object - ResourceState::Code initPipeline(pipeline& pip); - /// destroy pipeline object - void destroyPipeline(pipeline& pip); - /// initialize renderPass object - ResourceState::Code initRenderPass(renderPass& rp); - /// destroy renderPass object - void destroyRenderPass(renderPass& rp); - - gfxPointers pointers; - bool isValid = false; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gfxPointers.h b/code/Modules/Gfx/private/gfxPointers.h deleted file mode 100644 index a3827e22a..000000000 --- a/code/Modules/Gfx/private/gfxPointers.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::gfxPointers - @ingroup _privv - @brief pointers to the internal Gfx module objects - - This is used to internally communicate pointers to Gfx 'singletons', instead - of the typical singleton macros (which would expose the pointers to the outside world). -*/ -#include "Core/Types.h" - -namespace Oryol { - -namespace _priv { - -class renderer; -class displayMgr; -class gfxResourceContainer; -class meshPool; -class shaderPool; -class texturePool; -class pipelinePool; -class renderPassPool; - -struct gfxPointers { - class renderer* renderer = nullptr; - class displayMgr* displayMgr = nullptr; - class gfxResourceContainer* resContainer = nullptr; - class meshPool* meshPool = nullptr; - class shaderPool* shaderPool = nullptr; - class texturePool* texturePool = nullptr; - class pipelinePool* pipelinePool = nullptr; - class renderPassPool* renderPassPool = nullptr; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gfxResourceContainer.cc b/code/Modules/Gfx/private/gfxResourceContainer.cc deleted file mode 100644 index f10112d46..000000000 --- a/code/Modules/Gfx/private/gfxResourceContainer.cc +++ /dev/null @@ -1,469 +0,0 @@ -//------------------------------------------------------------------------------ -// gfxResourceContainer.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "Core/Core.h" -#include "gfxResourceContainer.h" -#include "displayMgr.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::setup(const GfxSetup& setup, const gfxPointers& ptrs) { - o_assert(!this->IsValid()); - - this->pointers = ptrs; - this->pendingLoaders.Reserve(128); - this->destroyQueue.Reserve(128); - - this->meshPool.Setup(GfxResourceType::Mesh, setup.ResourcePoolSize[GfxResourceType::Mesh]); - this->shaderPool.Setup(GfxResourceType::Shader, setup.ResourcePoolSize[GfxResourceType::Shader]); - this->texturePool.Setup(GfxResourceType::Texture, setup.ResourcePoolSize[GfxResourceType::Texture]); - this->pipelinePool.Setup(GfxResourceType::Pipeline, setup.ResourcePoolSize[GfxResourceType::Pipeline]); - this->renderPassPool.Setup(GfxResourceType::RenderPass, setup.ResourcePoolSize[GfxResourceType::RenderPass]); - this->factory.setup(this->pointers); - this->runLoopId = Core::PostRunLoop()->Add([this]() { - this->update(); - }); - - ResourceContainerBase::Setup(setup.ResourceLabelStackCapacity, setup.ResourceRegistryCapacity); -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::discard() { - o_assert_dbg(this->IsValid()); - - Core::PostRunLoop()->Remove(this->runLoopId); - for (const auto& loader : this->pendingLoaders) { - loader->Cancel(); - } - this->pendingLoaders.Clear(); - - ResourceContainerBase::Discard(); - - this->renderPassPool.Discard(); - this->pipelinePool.Discard(); - this->texturePool.Discard(); - this->shaderPool.Discard(); - this->meshPool.Discard(); - this->factory.discard(); - this->pointers = gfxPointers(); -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::Create(const MeshSetup& setup, const void* data, int size) { - o_assert_dbg(this->IsValid()); - o_assert_dbg(!setup.ShouldSetupFromFile()); - - Id resId = this->registry.Lookup(setup.Locator); - if (resId.IsValid()) { - return resId; - } - else { - resId = this->meshPool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - mesh& res = this->meshPool.Assign(resId, ResourceState::Setup); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initMesh(res, data, size); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->meshPool.UpdateState(resId, newState); - } - return resId; -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::Create(const TextureSetup& setup, const void* data, int size) { - o_assert_dbg(this->IsValid()); - o_assert_dbg(!setup.ShouldSetupFromFile()); - - Id resId = this->registry.Lookup(setup.Locator); - if (resId.IsValid()) { - return resId; - } - else { - resId = this->texturePool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - texture& res = this->texturePool.Assign(resId, ResourceState::Setup); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initTexture(res, data, size); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->texturePool.UpdateState(resId, newState); - } - return resId; -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::prepareAsync(const MeshSetup& setup) { - o_assert_dbg(this->IsValid()); - - Id resId = this->meshPool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - mesh& res = this->meshPool.Assign(resId, ResourceState::Pending); - res.Setup = setup; - return resId; -} - -//------------------------------------------------------------------------------ -template<> ResourceState::Code -gfxResourceContainer::initAsync(const Id& resId, const MeshSetup& setup, const void* data, int size) { - o_assert_dbg(this->IsValid()); - - // the prepared resource may have been destroyed while it was loading - if (this->meshPool.Contains(resId)) { - mesh& res = this->meshPool.Assign(resId, ResourceState::Pending); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initMesh(res, data, size); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->meshPool.UpdateState(resId, newState); - return newState; - } - else { - // the prepared mesh object was destroyed before it was loaded - o_warn("gfxResourceContainer::initAsync(): resource destroyed before initAsync (type: %d, slot: %d!)\n", - resId.Type, resId.SlotIndex); - return ResourceState::InvalidState; - } -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::prepareAsync(const TextureSetup& setup) { - o_assert_dbg(this->IsValid()); - - Id resId = this->texturePool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - texture& res = this->texturePool.Assign(resId, ResourceState::Pending); - res.Setup = setup; - return resId; -} - -//------------------------------------------------------------------------------ -template<> ResourceState::Code -gfxResourceContainer::initAsync(const Id& resId, const TextureSetup& setup, const void* data, int size) { - o_assert_dbg(this->IsValid()); - - // the prepared resource may have been destroyed while it was loading - if (this->texturePool.Contains(resId)) { - texture& res = this->texturePool.Assign(resId, ResourceState::Pending); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initTexture(res, data, size); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->texturePool.UpdateState(resId, newState); - return newState; - } - else { - // the prepared texture object was destroyed before it was loaded - o_warn("gfxResourceContainer::initAsync(): resource destroyed before initAsync (type: %d, slot: %d!)\n", - resId.Type, resId.SlotIndex); - return ResourceState::InvalidState; - } -} - -//------------------------------------------------------------------------------ -ResourceState::Code -gfxResourceContainer::failedAsync(const Id& resId) { - o_assert_dbg(this->IsValid()); - - switch (resId.Type) { - case GfxResourceType::Mesh: - // the prepared resource may have been destroyed while it was loading - if (this->meshPool.Contains(resId)) { - this->meshPool.UpdateState(resId, ResourceState::Failed); - return ResourceState::Failed; - } - break; - - case GfxResourceType::Texture: - // the prepared resource may have been destroyed while it was loading - if (this->texturePool.Contains(resId)) { - this->texturePool.UpdateState(resId, ResourceState::Failed); - return ResourceState::Failed; - } - break; - - default: - o_error("Invalid resource type for async creation!"); - break; - } - // fallthrough: resource was already destroyed while still loading - return ResourceState::InvalidState; -} - - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::Create(const ShaderSetup& setup, const void* /*data*/, int /*size*/) { - o_assert_dbg(this->IsValid()); - - Id resId = this->registry.Lookup(setup.Locator); - if (resId.IsValid()) { - return resId; - } - else { - resId = this->shaderPool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - shader& res = this->shaderPool.Assign(resId, ResourceState::Setup); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initShader(res); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->shaderPool.UpdateState(resId, newState); - } - return resId; -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::Create(const PipelineSetup& setup, const void* /*data*/, int /*size*/) { - o_assert_dbg(this->IsValid()); - - Id resId = this->registry.Lookup(setup.Locator); - if (resId.IsValid()) { - return resId; - } - else { - resId = this->pipelinePool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - pipeline& res = this->pipelinePool.Assign(resId, ResourceState::Setup); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initPipeline(res); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->pipelinePool.UpdateState(resId, newState); - } - return resId; -} - -//------------------------------------------------------------------------------ -template<> Id -gfxResourceContainer::Create(const PassSetup& setup, const void* /*data*/, int /*size*/) { - o_assert_dbg(this->IsValid()); - - Id resId = this->registry.Lookup(setup.Locator); - if (resId.IsValid()) { - return resId; - } - else { - resId = this->renderPassPool.AllocId(); - this->registry.Add(setup.Locator, resId, this->PeekLabel()); - renderPass& res = this->renderPassPool.Assign(resId, ResourceState::Setup); - res.Setup = setup; - const ResourceState::Code newState = this->factory.initRenderPass(res); - o_assert((newState == ResourceState::Valid) || (newState == ResourceState::Failed)); - this->renderPassPool.UpdateState(resId, newState); - } - return resId; -} - -//------------------------------------------------------------------------------ -Id -gfxResourceContainer::Load(const Ptr& loader) { - o_assert_dbg(this->IsValid()); - - Id resId = this->registry.Lookup(loader->Locator()); - if (resId.IsValid()) { - return resId; - } - else { - this->pendingLoaders.Add(loader); - resId = loader->Start(); - return resId; - } -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::DestroyDeferred(const ResourceLabel& label) { - o_assert_dbg(this->IsValid()); - Array ids = this->registry.Remove(label); - if (ids.Size() > 0) { - this->destroyQueue.Reserve(ids.Size()); - for (const Id& id : ids) { - this->destroyQueue.Add(id); - } - } -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::GarbageCollect() { - for (const Id& id : this->destroyQueue) { - this->destroyResource(id); - } - this->destroyQueue.Clear(); - this->factory.garbageCollect(); -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::destroyResource(const Id& id) { - switch (id.Type) { - case GfxResourceType::Texture: - { - if (ResourceState::Valid == this->texturePool.QueryState(id)) { - texture* tex = this->texturePool.Lookup(id); - if (tex) { - this->factory.destroyTexture(*tex); - } - } - this->texturePool.Unassign(id); - } - break; - - case GfxResourceType::Mesh: - { - if (ResourceState::Valid == this->meshPool.QueryState(id)) { - mesh* msh = this->meshPool.Lookup(id); - if (msh) { - this->factory.destroyMesh(*msh); - } - } - this->meshPool.Unassign(id); - } - break; - - case GfxResourceType::Shader: - { - if (ResourceState::Valid == this->shaderPool.QueryState(id)) { - shader* shd = this->shaderPool.Lookup(id); - if (shd) { - this->factory.destroyShader(*shd); - } - } - this->shaderPool.Unassign(id); - } - break; - - case GfxResourceType::Pipeline: - { - if (ResourceState::Valid == this->pipelinePool.QueryState(id)) { - pipeline* pip = this->pipelinePool.Lookup(id); - if (pip) { - this->factory.destroyPipeline(*pip); - } - } - this->pipelinePool.Unassign(id); - } - break; - - case GfxResourceType::RenderPass: - { - if (ResourceState::Valid == this->renderPassPool.QueryState(id)) { - renderPass* rp = this->renderPassPool.Lookup(id); - if (rp) { - this->factory.destroyRenderPass(*rp); - } - } - this->renderPassPool.Unassign(id); - } - break; - - default: - o_assert(false); - break; - } -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::Destroy(const ResourceLabel& label) { - o_assert_dbg(this->IsValid()); - Array ids = this->registry.Remove(label); - for (const Id& id : ids) { - this->destroyResource(id); - } -} - -//------------------------------------------------------------------------------ -void -gfxResourceContainer::update() { - o_assert_dbg(this->IsValid()); - - /// call update method on resource pools (this is cheap) - this->meshPool.Update(); - this->shaderPool.Update(); - this->texturePool.Update(); - this->pipelinePool.Update(); - - // trigger loaders, and remove from pending array if finished - for (int i = this->pendingLoaders.Size() - 1; i >= 0; i--) { - const auto& loader = this->pendingLoaders[i]; - ResourceState::Code state = loader->Continue(); - if (ResourceState::Pending != state) { - this->pendingLoaders.Erase(i); - } - } -} - -//------------------------------------------------------------------------------ -ResourceInfo -gfxResourceContainer::QueryResourceInfo(const Id& resId) const { - o_assert_dbg(this->IsValid()); - - switch (resId.Type) { - case GfxResourceType::Texture: - return this->texturePool.QueryResourceInfo(resId); - case GfxResourceType::Mesh: - return this->meshPool.QueryResourceInfo(resId); - case GfxResourceType::Shader: - return this->shaderPool.QueryResourceInfo(resId); - case GfxResourceType::Pipeline: - return this->pipelinePool.QueryResourceInfo(resId); - case GfxResourceType::RenderPass: - return this->renderPassPool.QueryResourceInfo(resId); - default: - o_assert(false); - return ResourceInfo(); - } -} - -//------------------------------------------------------------------------------ -ResourcePoolInfo -gfxResourceContainer::QueryPoolInfo(GfxResourceType::Code resType) const { - o_assert_dbg(this->IsValid()); - - switch (resType) { - case GfxResourceType::Texture: - return this->texturePool.QueryPoolInfo(); - case GfxResourceType::Mesh: - return this->meshPool.QueryPoolInfo(); - case GfxResourceType::Shader: - return this->shaderPool.QueryPoolInfo(); - case GfxResourceType::Pipeline: - return this->pipelinePool.QueryPoolInfo(); - case GfxResourceType::RenderPass: - return this->renderPassPool.QueryPoolInfo(); - default: - o_assert(false); - return ResourcePoolInfo(); - } -} - -//------------------------------------------------------------------------------ -int -gfxResourceContainer::QueryFreeSlots(GfxResourceType::Code resourceType) const { - o_assert_dbg(this->IsValid()); - - switch (resourceType) { - case GfxResourceType::Texture: - return this->texturePool.GetNumFreeSlots(); - case GfxResourceType::Mesh: - return this->meshPool.GetNumFreeSlots(); - case GfxResourceType::Shader: - return this->shaderPool.GetNumFreeSlots(); - case GfxResourceType::Pipeline: - return this->pipelinePool.GetNumFreeSlots(); - case GfxResourceType::RenderPass: - return this->renderPassPool.GetNumFreeSlots(); - default: - o_assert(false); - return 0; - } -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gfxResourceContainer.h b/code/Modules/Gfx/private/gfxResourceContainer.h deleted file mode 100644 index 3f80d9a12..000000000 --- a/code/Modules/Gfx/private/gfxResourceContainer.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::gfxResourceContainer - @ingroup _priv - @brief resource container base implementation of the Gfx module -*/ -#include "Core/RunLoop.h" -#include "Core/Containers/Array.h" -#include "Resource/ResourceLoader.h" -#include "Resource/ResourceContainerBase.h" -#include "Resource/ResourceInfo.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/private/gfxFactory.h" -#include "Gfx/private/gfxPointers.h" - -namespace Oryol { -namespace _priv { - -class renderer; -class displayMgr; - -class gfxResourceContainer : public ResourceContainerBase { -public: - /// setup the resource container - void setup(const GfxSetup& setup, const gfxPointers& ptrs); - /// discard the resource manager - void discard(); - - /// create a resource object with data - template Id Create(const SETUP& setup, const void* data=nullptr, int size=0); - /// asynchronously load resource object - Id Load(const Ptr& loader); - /// query number of free slots for resource type - int QueryFreeSlots(GfxResourceType::Code resourceType) const; - /// query resource info (fast) - ResourceInfo QueryResourceInfo(const Id& id) const; - /// query resource pool info (slow) - ResourcePoolInfo QueryPoolInfo(GfxResourceType::Code resType) const; - /// immediately destroy resources by label - void Destroy(const ResourceLabel& label); - /// queue resources for destruction in GarbageCollect - void DestroyDeferred(const ResourceLabel& label); - /// destroy deferred resources (called from Gfx::CommitFrame) - void GarbageCollect(); - - /// prepare async creation (usually called at start of async Load) - template Id prepareAsync(const SETUP& setup); - /// setup async resource (usually called during async Load) - template ResourceState::Code initAsync(const Id& resId, const SETUP& setup, const void* data, int size); - /// notify resource container that async creation had failed - ResourceState::Code failedAsync(const Id& resId); - - /// lookup mesh object - mesh* lookupMesh(const Id& resId); - /// lookup shader object - shader* lookupShader(const Id& resId); - /// lookup texture object - texture* lookupTexture(const Id& resId); - /// lookup pipeline object - pipeline* lookupPipeline(const Id& resId); - /// lookup render-pass object - renderPass* lookupRenderPass(const Id& resId); - - /// per-frame update (update resource pools and pending loaders) - void update(); - /// destroy a single resource - void destroyResource(const Id& id); - - gfxPointers pointers; - gfxFactory factory; - class meshPool meshPool; - class shaderPool shaderPool; - class texturePool texturePool; - class pipelinePool pipelinePool; - class renderPassPool renderPassPool; - RunLoop::Id runLoopId = RunLoop::InvalidId; - Array> pendingLoaders; - Array destroyQueue; -}; - -//------------------------------------------------------------------------------ -inline mesh* -gfxResourceContainer::lookupMesh(const Id& resId) { - o_assert_dbg(this->valid); - return this->meshPool.Lookup(resId); -} - -//------------------------------------------------------------------------------ -inline shader* -gfxResourceContainer::lookupShader(const Id& resId) { - o_assert_dbg(this->valid); - return this->shaderPool.Lookup(resId); -} - -//------------------------------------------------------------------------------ -inline texture* -gfxResourceContainer::lookupTexture(const Id& resId) { - o_assert_dbg(this->valid); - return this->texturePool.Lookup(resId); -} - -//------------------------------------------------------------------------------ -inline pipeline* -gfxResourceContainer::lookupPipeline(const Id& resId) { - o_assert_dbg(this->valid); - return this->pipelinePool.Lookup(resId); -} - -//------------------------------------------------------------------------------ -inline renderPass* -gfxResourceContainer::lookupRenderPass(const Id& resId) { - o_assert_dbg(this->valid); - return this->renderPassPool.Lookup(resId); -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/egl/eglDisplayMgr.cc b/code/Modules/Gfx/private/gl/eglDisplayMgr.cc similarity index 80% rename from code/Modules/Gfx/private/egl/eglDisplayMgr.cc rename to code/Modules/Gfx/private/gl/eglDisplayMgr.cc index 832ea93d9..a64adebbd 100644 --- a/code/Modules/Gfx/private/egl/eglDisplayMgr.cc +++ b/code/Modules/Gfx/private/gl/eglDisplayMgr.cc @@ -10,8 +10,6 @@ #if ORYOL_RASPBERRYPI #include "bcm_host.h" #endif -#include "Gfx/private/gl/gl_impl.h" -#include "Gfx/private/gl/glCaps.h" #if ORYOL_ANDROID // this is in the app's main file (see App.h -> OryolApp) @@ -21,15 +19,6 @@ extern android_app* OryolAndroidAppState; namespace Oryol { namespace _priv { -//------------------------------------------------------------------------------ -eglDisplayMgr::eglDisplayMgr() : -eglDisplay(nullptr), -eglConfig(nullptr), -eglSurface(nullptr), -eglContext(nullptr) { - // empty -} - //------------------------------------------------------------------------------ eglDisplayMgr::~eglDisplayMgr() { if (this->IsDisplayValid()) { @@ -39,13 +28,13 @@ eglDisplayMgr::~eglDisplayMgr() { //------------------------------------------------------------------------------ void -eglDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { +eglDisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); o_assert(nullptr == this->eglDisplay); Log::Info("eglDisplayMgr::SetupDisplay() called!\n"); - displayMgrBase::SetupDisplay(gfxSetup, ptrs); + displayMgrBase::SetupDisplay(desc); #if ORYOL_RASPBERRYPI bcm_host_init(); @@ -61,17 +50,17 @@ eglDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { // make sure we have a valid rendering RGBA format, e.g. RGB is // not a valid framebuffer format - int colorBits = PixelFormat::NumBits(gfxSetup.ColorFormat, PixelChannel::Red); + int colorBits = PixelFormat::NumBits(desc.ColorFormat(), PixelChannel::Red); o_assert((colorBits == 4) || (colorBits == 8)); EGLint eglConfigAttrs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_SAMPLES, gfxSetup.SampleCount, + EGL_SAMPLES, desc.SampleCount(), EGL_RED_SIZE, colorBits, EGL_GREEN_SIZE, colorBits, EGL_BLUE_SIZE, colorBits, EGL_ALPHA_SIZE, colorBits, - EGL_DEPTH_SIZE, PixelFormat::NumBits(gfxSetup.DepthFormat, PixelChannel::Depth), - EGL_STENCIL_SIZE, PixelFormat::NumBits(gfxSetup.DepthFormat, PixelChannel::Stencil), + EGL_DEPTH_SIZE, PixelFormat::NumBits(desc.DepthFormat(), PixelChannel::Depth), + EGL_STENCIL_SIZE, PixelFormat::NumBits(desc.DepthFormat(), PixelChannel::Stencil), EGL_NONE }; EGLint numConfigs = 0; @@ -97,6 +86,11 @@ eglDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { this->eglContext = eglCreateContext(this->eglDisplay, this->eglConfig, EGL_NO_CONTEXT, contextAttrs); o_assert(eglGetError() == EGL_SUCCESS); o_assert(nullptr != this->eglContext); + #if ORYOL_OPENGLES3 + this->useGLES2 = false; + #else + this->useGLES2 = true; + #endif #if ORYOL_ANDROID o_assert(OryolAndroidAppState); @@ -110,7 +104,7 @@ eglDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { eglGetConfigAttrib(this->eglDisplay, this->eglConfig, EGL_NATIVE_VISUAL_ID, &format); int32_t w = ANativeWindow_getWidth(window); int32_t h = ANativeWindow_getHeight(window); - if (!gfxSetup.HighDPI) { + if (!gfxDesc.HighDPI()) { w/=2; h/=2; } ANativeWindow_setBuffersGeometry(window, w, h, format); @@ -160,21 +154,14 @@ eglDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { o_error("eglDisplayMgr: eglMakeCurrent failed!\n"); return; } - #if ORYOL_OPENGLES3 - glCaps::Setup(glCaps::GLES3); - #else - glCaps::Setup(glCaps::GLES2); - #endif // query actual display size and set in DisplayAttrs EGLint actualWidth = 0, actualHeight = 0; eglQuerySurface(this->eglDisplay, this->eglSurface, EGL_WIDTH, &actualWidth); eglQuerySurface(this->eglDisplay, this->eglSurface, EGL_HEIGHT, &actualHeight); Log::Info("eglDisplayMgr: actual framebuffer size w=%d, h=%d\n", actualWidth, actualHeight); - this->displayAttrs.FramebufferWidth = actualWidth; - this->displayAttrs.FramebufferHeight = actualHeight; - this->displayAttrs.WindowWidth = actualWidth; - this->displayAttrs.WindowHeight = actualHeight; + this->displayAttrs.Width = actualWidth; + this->displayAttrs.Height = actualHeight; } //------------------------------------------------------------------------------ @@ -189,7 +176,6 @@ eglDisplayMgr::DiscardDisplay() { eglTerminate(this->eglDisplay); this->eglConfig = nullptr; this->eglDisplay = nullptr; - glCaps::Discard(); displayMgrBase::DiscardDisplay(); } @@ -202,13 +188,6 @@ eglDisplayMgr::Present() { displayMgrBase::Present(); } -//------------------------------------------------------------------------------ -void -eglDisplayMgr::glBindDefaultFramebuffer() { - ::glBindFramebuffer(GL_FRAMEBUFFER, 0); - ORYOL_GL_CHECK_ERROR(); -} - } // namespace _priv } // namespace Oryol diff --git a/code/Modules/Gfx/private/egl/eglDisplayMgr.h b/code/Modules/Gfx/private/gl/eglDisplayMgr.h similarity index 63% rename from code/Modules/Gfx/private/egl/eglDisplayMgr.h rename to code/Modules/Gfx/private/gl/eglDisplayMgr.h index 54c3be85c..9d9710271 100644 --- a/code/Modules/Gfx/private/egl/eglDisplayMgr.h +++ b/code/Modules/Gfx/private/gl/eglDisplayMgr.h @@ -6,7 +6,7 @@ @brief display manager class for EGL platforms */ #include "Gfx/private/displayMgrBase.h" -#include "Gfx/private/gl/gl_decl.h" +#include "Gfx/private/gl/gl.h" #include namespace Oryol { @@ -14,25 +14,21 @@ namespace _priv { class eglDisplayMgr : public displayMgrBase { public: - /// constructor - eglDisplayMgr(); /// destructor ~eglDisplayMgr(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& GfxSetup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// present the current rendered frame void Present(); - /// bind the default frame buffer - void glBindDefaultFramebuffer(); - - EGLDisplay eglDisplay; - EGLConfig eglConfig; - EGLSurface eglSurface; - EGLContext eglContext; + bool useGLES2 = false; + EGLDisplay eglDisplay = nullptr; + EGLConfig eglConfig = nullptr; + EGLSurface eglSurface = nullptr; + EGLContext eglContext = nullptr; }; } // namespace _priv diff --git a/code/Modules/Gfx/private/emsc/emscDisplayMgr.cc b/code/Modules/Gfx/private/gl/emscDisplayMgr.cc similarity index 71% rename from code/Modules/Gfx/private/emsc/emscDisplayMgr.cc rename to code/Modules/Gfx/private/gl/emscDisplayMgr.cc index 753135496..cd76575c3 100644 --- a/code/Modules/Gfx/private/emsc/emscDisplayMgr.cc +++ b/code/Modules/Gfx/private/gl/emscDisplayMgr.cc @@ -3,8 +3,6 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "emscDisplayMgr.h" -#include "Gfx/private/gl/gl_impl.h" -#include "Gfx/private/gl/glCaps.h" #include namespace Oryol { @@ -57,10 +55,7 @@ EMSCRIPTEN_KEEPALIVE bool is_soft_fullscreen_active() { } // extern "C" //------------------------------------------------------------------------------ -emscDisplayMgr::emscDisplayMgr() : -storedCanvasWidth(0), -storedCanvasHeight(0), -ctx(0) { +emscDisplayMgr::emscDisplayMgr() { self = this; } @@ -74,25 +69,26 @@ emscDisplayMgr::~emscDisplayMgr() { //------------------------------------------------------------------------------ void -emscDisplayMgr::SetupDisplay(const GfxSetup& renderSetup, const gfxPointers& ptrs) { +emscDisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); - displayMgrBase::SetupDisplay(renderSetup, ptrs); + displayMgrBase::SetupDisplay(desc); - if (renderSetup.HtmlTrackElementSize) { + if (desc.HtmlTrackElementSize) { // register notification callback when canvas size changes double width, height; - if (EMSCRIPTEN_RESULT_SUCCESS == emscripten_get_element_css_size(renderSetup.HtmlElement.AsCStr(), &width, &height)) { - this->displayAttrs.FramebufferWidth = (int) width; - this->displayAttrs.FramebufferHeight = (int) height; + if (EMSCRIPTEN_RESULT_SUCCESS == emscripten_get_element_css_size(desc.HtmlElement.AsCStr(), &width, &height)) { + this->displayAttrs.Width = (int) width; + this->displayAttrs.Height = (int) height; Log::Info("Tracked HTML element size '%s': %dx%d\n", - renderSetup.HtmlElement.AsCStr(), - this->displayAttrs.FramebufferWidth, - this->displayAttrs.FramebufferHeight); + desc.HtmlElement.AsCStr(), + this->displayAttrs.Width, + this->displayAttrs.Height); } - emscripten_set_canvas_element_size(renderSetup.HtmlElement.AsCStr(), this->displayAttrs.FramebufferWidth, this->displayAttrs.FramebufferHeight); + emscripten_set_canvas_element_size(desc.HtmlElement.AsCStr(), this->displayAttrs.Width, this->displayAttrs.Height); emscripten_set_resize_callback(nullptr, nullptr, false, emscWindowSizeChanged); - } else if (renderSetup.Windowed) { - emscripten_set_canvas_element_size("#canvas", renderSetup.Width, renderSetup.Height); + } + else if (desc.Windowed) { + emscripten_set_canvas_element_size(desc.HtmlElement.AsCStr(), desc.Width, desc.Height); } else { enter_soft_fullscreen(); @@ -100,19 +96,19 @@ emscDisplayMgr::SetupDisplay(const GfxSetup& renderSetup, const gfxPointers& ptr EmscriptenWebGLContextAttributes ctxAttrs; emscripten_webgl_init_context_attributes(&ctxAttrs); - ctxAttrs.alpha = 0 < PixelFormat::NumBits(renderSetup.ColorFormat, PixelChannel::Alpha); - ctxAttrs.depth = 0 < PixelFormat::NumBits(renderSetup.DepthFormat, PixelChannel::Depth); - ctxAttrs.stencil = 0 < PixelFormat::NumBits(renderSetup.DepthFormat, PixelChannel::Stencil); - ctxAttrs.antialias = renderSetup.SampleCount > 1; + ctxAttrs.alpha = 0 < PixelFormat::NumBits(desc.ColorFormat, PixelChannel::Alpha); + ctxAttrs.depth = 0 < PixelFormat::NumBits(desc.DepthFormat, PixelChannel::Depth); + ctxAttrs.stencil = 0 < PixelFormat::NumBits(desc.DepthFormat, PixelChannel::Stencil); + ctxAttrs.antialias = desc.SampleCount > 1; ctxAttrs.premultipliedAlpha = false; ctxAttrs.preserveDrawingBuffer = false; Log::Info("emscDisplayMgr: alpha=%d, depth=%d, stencil=%d, antialias=%d\n", ctxAttrs.alpha, ctxAttrs.depth, ctxAttrs.stencil, ctxAttrs.antialias); // first try to get an WebGL2 context - glCaps::Flavour glFlavour = glCaps::GLES3; ctxAttrs.minorVersion = 0; #if ORYOL_OPENGLES3 + this->useGLES2 = false; Log::Info("emscDisplayMgr: trying to create WebGL2 context...\n"); ctxAttrs.majorVersion = 2; this->ctx = emscripten_webgl_create_context(nullptr, &ctxAttrs); @@ -126,14 +122,14 @@ emscDisplayMgr::SetupDisplay(const GfxSetup& renderSetup, const gfxPointers& ptr Log::Info("emscDisplayMgr: using WebGL1 context...\n"); ctxAttrs.majorVersion = 1; this->ctx = emscripten_webgl_create_context(nullptr, &ctxAttrs); - glFlavour = glCaps::GLES2; + this->useGLES2 = true; } o_assert2(this->ctx > 0, "Failed to create WebGL context"); // in case we run on an iOS device, need to enabled the // WEBKIT_WEBGL_compressed_texture_pvrtc, this is not done automatically // by emscripten (only for WEBGL_compressed_texture_pvrtc) - if (glFlavour == glCaps::GLES2) { + if (this->useGLES2) { Log::Info("emscDisplayMgr: try enabling WEBKIT_WEBGL_compressed_texture_pvrtc\n"); if (emscripten_webgl_enable_extension(this->ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc")) { Log::Info("emscDisplayMgr: WEBKIT_WEBGL_compressed_texture_pvrtc enabled\n"); @@ -143,8 +139,6 @@ emscDisplayMgr::SetupDisplay(const GfxSetup& renderSetup, const gfxPointers& ptr } } emscripten_webgl_make_context_current(this->ctx); - - glCaps::Setup(glFlavour); } //------------------------------------------------------------------------------ @@ -152,28 +146,19 @@ void emscDisplayMgr::DiscardDisplay() { o_assert(this->IsDisplayValid()); o_assert(0 != this->ctx); - - glCaps::Discard(); emscripten_webgl_destroy_context(this->ctx); this->ctx = 0; displayMgrBase::DiscardDisplay(); } -//------------------------------------------------------------------------------ -void -emscDisplayMgr::glBindDefaultFramebuffer() { - ::glBindFramebuffer(GL_FRAMEBUFFER, 0); - ORYOL_GL_CHECK_ERROR(); -} - //------------------------------------------------------------------------------ EM_BOOL emscDisplayMgr::emscCanvasSizeChanged(int eventType, const void* reserved, void* userData) { int newWidth, newHeight; - emscripten_get_canvas_element_size("#canvas", &newWidth, &newHeight); - self->displayAttrs.FramebufferWidth = newWidth; - self->displayAttrs.FramebufferHeight = newHeight; + emscripten_get_canvas_element_size(self->gfxDesc.HtmlElement.AsCStr(), &newWidth, &newHeight); + self->displayAttrs.Width = newWidth; + self->displayAttrs.Height = newHeight; return true; } @@ -181,10 +166,10 @@ emscDisplayMgr::emscCanvasSizeChanged(int eventType, const void* reserved, void* EM_BOOL emscDisplayMgr::emscWindowSizeChanged(int eventType, const EmscriptenUiEvent* uiEvent, void* userData) { double width, height; - if (EMSCRIPTEN_RESULT_SUCCESS == emscripten_get_element_css_size(self->gfxSetup.HtmlElement.AsCStr(), &width, &height)) { - self->displayAttrs.FramebufferWidth = (int) width; - self->displayAttrs.FramebufferHeight = (int) height; - emscripten_set_canvas_element_size(self->gfxSetup.HtmlElement.AsCStr(), self->displayAttrs.FramebufferWidth, self->displayAttrs.FramebufferHeight); + if (EMSCRIPTEN_RESULT_SUCCESS == emscripten_get_element_css_size(self->gfxDesc.HtmlElement.AsCStr(), &width, &height)) { + self->displayAttrs.Width = (int) width; + self->displayAttrs.Height = (int) height; + emscripten_set_canvas_element_size(self->gfxDesc.HtmlElement.AsCStr(), self->displayAttrs.Width, self->displayAttrs.Height); } return true; } diff --git a/code/Modules/Gfx/private/emsc/emscDisplayMgr.h b/code/Modules/Gfx/private/gl/emscDisplayMgr.h similarity index 83% rename from code/Modules/Gfx/private/emsc/emscDisplayMgr.h rename to code/Modules/Gfx/private/gl/emscDisplayMgr.h index b0d1de714..b88af3e6c 100644 --- a/code/Modules/Gfx/private/emsc/emscDisplayMgr.h +++ b/code/Modules/Gfx/private/gl/emscDisplayMgr.h @@ -27,21 +27,18 @@ class emscDisplayMgr : public displayMgrBase { ~emscDisplayMgr(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); - /// bind the default frame buffer - void glBindDefaultFramebuffer(); /// emscripten callback when canvas size has changed (for soft-fullscreen) static EM_BOOL emscCanvasSizeChanged(int eventType, const void* reserved, void* userData); /// emscripten callback when window size has changed (for HTMLUseCanvasSize) static EM_BOOL emscWindowSizeChanged(int eventType, const EmscriptenUiEvent* uiEvent, void* userData); static emscDisplayMgr* self; - int storedCanvasWidth; - int storedCanvasHeight; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; + bool useGLES2 = false; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = 0; }; } // namespace _priv diff --git a/code/Modules/Gfx/private/flextgl/flextGL.c b/code/Modules/Gfx/private/gl/flextGL.c similarity index 100% rename from code/Modules/Gfx/private/flextgl/flextGL.c rename to code/Modules/Gfx/private/gl/flextGL.c diff --git a/code/Modules/Gfx/private/flextgl/flextGL.h b/code/Modules/Gfx/private/gl/flextGL.h similarity index 100% rename from code/Modules/Gfx/private/flextgl/flextGL.h rename to code/Modules/Gfx/private/gl/flextGL.h diff --git a/code/Modules/Gfx/private/gl/flextgl.sh b/code/Modules/Gfx/private/gl/flextgl.sh new file mode 100755 index 000000000..ee1bca08a --- /dev/null +++ b/code/Modules/Gfx/private/gl/flextgl.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# helper bash script to generate gl files +# +python3 ../../../../flextgl/flextGLgen.py -T glfw3 -D . flextgl_profile.txt diff --git a/code/Modules/Gfx/private/flextgl/flextgl_profile.txt b/code/Modules/Gfx/private/gl/flextgl_profile.txt similarity index 100% rename from code/Modules/Gfx/private/flextgl/flextgl_profile.txt rename to code/Modules/Gfx/private/gl/flextgl_profile.txt diff --git a/code/Modules/Gfx/private/gl/gl.h b/code/Modules/Gfx/private/gl/gl.h new file mode 100644 index 000000000..c810bcd7d --- /dev/null +++ b/code/Modules/Gfx/private/gl/gl.h @@ -0,0 +1,33 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + @file Gfx/private/gl.h + @brief include the right GL headers for the target platform +*/ +#if !ORYOL_OPENGL +#error "attempting to include GL headers on non-GL platform" +#endif +#if ORYOL_RASPBERRYPI +#define GL_GLEXT_PROTOTYPES +#include "GLES2/gl2.h" +#include "GLES2/gl2ext.h" +#elif ORYOL_WINDOWS || ORYOL_LINUX || ORYOL_MACOS +#include "Gfx/private/gl/flextGL.h" +#elif ORYOL_IOS +#include +#include +#elif ORYOL_EMSCRIPTEN + #if ORYOL_OPENGLES2 + #define GL_GLEXT_PROTOTYPES + #include + #include + #else + #include + #endif +#elif ORYOL_ANDROID +#define GL_GLEXT_PROTOTYPES +#include +#include +#else +#error "Missing platform for GL header include!" +#endif diff --git a/code/Modules/Gfx/private/gl/glCaps.cc b/code/Modules/Gfx/private/gl/glCaps.cc deleted file mode 100644 index 8c4a82370..000000000 --- a/code/Modules/Gfx/private/gl/glCaps.cc +++ /dev/null @@ -1,349 +0,0 @@ -//------------------------------------------------------------------------------ -// glCaps.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "gl_impl.h" -#include "Core/Assertion.h" -#include "Core/String/StringBuilder.h" -#include "glCaps.h" - -namespace Oryol { -namespace _priv { - -glCaps::state_t glCaps::state; - -//------------------------------------------------------------------------------ -void -glCaps::Setup(Flavour flav) { - o_assert_dbg(!state.isValid); - state = state_t(); - state.isValid = true; - state.flavour = flav; - - setupFeatures(flav); - setupLimits(flav); - printInfo(flav); -} - -//------------------------------------------------------------------------------ -void -glCaps::Discard() { - o_assert_dbg(state.isValid); - state.isValid = false; -} - -//------------------------------------------------------------------------------ -bool -glCaps::IsValid() { - return state.isValid; -} - -//------------------------------------------------------------------------------ -void -glCaps::setupLimits(Flavour flav) { - ORYOL_GL_CHECK_ERROR(); - ::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &state.intLimits[MaxTextureSize]); - ::glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &state.intLimits[MaxCubeMapTextureSize]); - ::glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &state.intLimits[MaxViewPortWidth]); - ::glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &state.intLimits[MaxVertexAttribs]); - ::glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &state.intLimits[MaxCombinedTextureImageUnits]); - ::glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &state.intLimits[MaxVertexTextureImageUnits]); - ORYOL_GL_CHECK_ERROR(); - #if ORYOL_OPENGLES2 - if (flav == GLES2) { - ::glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &state.intLimits[MaxVertexUniformComponents]); - ::glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &state.intLimits[MaxFragmentUniformComponents]); - } - #endif - #if !ORYOL_OPENGLES2 - if (flav != GLES2) { - ::glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &state.intLimits[MaxVertexUniformComponents]); - ::glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &state.intLimits[MaxFragmentUniformComponents]); - } - #endif - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glCaps::setupFeatures(Flavour flav) { - ORYOL_GL_CHECK_ERROR(); - if (flav != GL_3_3_CORE) { - // non-core GL, check extensions - StringBuilder strBuilder((const char*)::glGetString(GL_EXTENSIONS)); - ORYOL_GL_CHECK_ERROR(); - state.features[TextureCompressionDXT] = strBuilder.Contains("_texture_compression_s3tc") || - strBuilder.Contains("_compressed_texture_s3tc") || - strBuilder.Contains("_texture_compression_dxt1"); - state.features[TextureCompressionPVRTC] = strBuilder.Contains("_texture_compression_pvrtc") || - strBuilder.Contains("_compressed_texture_pvrtc"); - state.features[TextureCompressionATC] = strBuilder.Contains("_compressed_ATC_texture") || - strBuilder.Contains("_compressed_texture_atc"); - state.features[TextureFloat] = strBuilder.Contains("_texture_float"); - state.features[InstancedArrays] = strBuilder.Contains("_instanced_arrays"); - state.features[DebugOutput] = strBuilder.Contains("_debug_output"); - if (flav == GLES2) { - state.features[TextureHalfFloat] = strBuilder.Contains("_texture_half_float"); - } - else { - state.features[TextureHalfFloat] = state.features[TextureFloat]; - } - // FIXME: check for EXT_draw_buffers - } - if (flav == GL_3_3_CORE) { - // GL 3.3 Core - state.features[InstancedArrays] = true; - state.features[TextureFloat] = true; - state.features[MultipleRenderTarget] = true; - state.features[MSAARenderTargets] = true; - state.features[PackedVertexFormat_10_2] = true; - state.features[MapBuffer] = true; - state.features[TextureCompressionDXT] = true; - state.features[Texture3D] = true; - state.features[TextureArray] = true; - } - else if (flav == GLES3) { - state.features[InstancedArrays] = true; - state.features[TextureFloat] = true; - state.features[MultipleRenderTarget] = true; - state.features[MSAARenderTargets] = true; - state.features[PackedVertexFormat_10_2] = true; - #if !ORYOL_EMSCRIPTEN - state.features[MapBuffer] = true; - #endif - state.features[TextureCompressionETC2] = true; - state.features[Texture3D] = true; - state.features[TextureArray] = true; - } - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -bool -glCaps::HasTextureFormat(PixelFormat::Code fmt) { - if (PixelFormat::IsCompressedFormat(fmt)) { - switch (fmt) { - case PixelFormat::DXT1: - case PixelFormat::DXT3: - case PixelFormat::DXT5: - return HasFeature(TextureCompressionDXT); - case PixelFormat::PVRTC2_RGB: - case PixelFormat::PVRTC4_RGB: - case PixelFormat::PVRTC2_RGBA: - case PixelFormat::PVRTC4_RGBA: - return HasFeature(TextureCompressionPVRTC); - case PixelFormat::ETC2_RGB8: - case PixelFormat::ETC2_SRGB8: - return HasFeature(TextureCompressionETC2); - default: - return false; - } - } - else { - // non compressed format, always supported - return true; - } -} - -//------------------------------------------------------------------------------ -void -glCaps::VertexAttribDivisor(GLuint index, GLuint divisor) { - if (state.features[InstancedArrays]) { - #if ORYOL_IOS && ORYOL_OPENGLES2 - ::glVertexAttribDivisorEXT(index, divisor); - #elif ORYOL_RASPBERRYPI - o_error("glCaps::VertexAttribDivisor() called!\n"); - #elif ORYOL_OPENGLES2 && !ORYOL_EMSCRIPTEN - ::glVertexAttribDivisorANGLE(index, divisor); - #else - ::glVertexAttribDivisor(index, divisor); - #endif - } -} - -//------------------------------------------------------------------------------ -void -glCaps::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount) { - if (state.features[InstancedArrays]) { - #if ORYOL_IOS && ORYOL_OPENGLES2 - ::glDrawArraysInstancedEXT(mode, first, count, primcount); - #elif ORYOL_RASPBERRYPI - o_error("glCaps::DrawArraysInstanced() called!\n"); - #elif ORYOL_OPENGLES2 && !ORYOL_EMSCRIPTEN - ::glDrawArraysInstancedANGLE(mode, first, count, primcount); - #else - ::glDrawArraysInstanced(mode, first, count, primcount); - #endif - } -} - -//------------------------------------------------------------------------------ -void -glCaps::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount) { - if (state.features[InstancedArrays]) { - #if ORYOL_IOS && ORYOL_OPENGLES2 - ::glDrawElementsInstancedEXT(mode, count, type, indices, primcount); - #elif ORYOL_RASPBERRYPI - o_error("glCaps::DrawElementsInstanced() called!\n"); - #elif ORYOL_OPENGLES2 && !ORYOL_EMSCRIPTEN - ::glDrawElementsInstancedANGLE(mode, count, type, indices, primcount); - #else - ::glDrawElementsInstanced(mode, count, type, indices, primcount); - #endif - } -} - -//------------------------------------------------------------------------------ -void -glCaps::printInfo(Flavour flav) { - ORYOL_GL_CHECK_ERROR(); - printString(GL_VERSION, "GL_VERSION", false); - printString(GL_VENDOR, "GL_VENDOR", false); - printString(GL_RENDERER, "GL_RENDERER", false); - printString(GL_SHADING_LANGUAGE_VERSION, "GL_SHADING_LANGUAGE_VERSION", false); - printInt(GL_MAX_TEXTURE_SIZE, "GL_MAX_TEXTURE_SIZE", 1); - printInt(GL_MAX_CUBE_MAP_TEXTURE_SIZE, "GL_MAX_CUBE_MAP_TEXTURE_SIZE", 1); - printInt(GL_MAX_VIEWPORT_DIMS, "GL_MAX_VIEWPORT_DIMS", 2); - printInt(GL_MAX_VERTEX_ATTRIBS, "GL_MAX_VERTEX_ATTRIBS", 1); - printInt(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS", 1); - printInt(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS", 1); - #if ORYOL_OPENGLES2 - if (flav == GLES2) { - printInt(GL_MAX_VERTEX_UNIFORM_VECTORS, "GL_MAX_VERTEX_UNIFORM_VECTORS", 1); - printInt(GL_MAX_FRAGMENT_UNIFORM_VECTORS, "GL_MAX_FRAGMENT_UNIFORM_VECTORS", 1); - } - #endif - #if !ORYOL_OPENGLES2 - if (flav != GLES2) { - printInt(GL_MAX_VERTEX_UNIFORM_COMPONENTS, "GL_MAX_VERTEX_UNIFORM_COMPONENTS", 1); - printInt(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS", 1); - } - if (HasFeature(MultipleRenderTarget)) { - printInt(GL_MAX_COLOR_ATTACHMENTS, "GL_MAX_COLOR_ATTACHMENTS", 1); - } - if (HasFeature(Texture3D)) { - printInt(GL_MAX_3D_TEXTURE_SIZE, "GL_MAX_3D_TEXTURE_SIZE", 1); - } - #endif - if (flav != GL_3_3_CORE) { - printString(GL_EXTENSIONS, "GL_EXTENSIONS", true); - } - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glCaps::printString(GLenum glEnum, const char* name, bool replaceSpaceWithNewLine) { - o_assert(name); - const char* rawStr = (const char*) ::glGetString(glEnum); - if (rawStr) { - String str(rawStr); - if (replaceSpaceWithNewLine) { - StringBuilder strBuilder; - strBuilder.Append(" "); - strBuilder.Append(str); - strBuilder.SubstituteAll(" ", "\n"); - str = strBuilder.GetString(); - } - Log::Info("%s: %s\n", name, str.AsCStr()); - } - else { - o_warn("::glGetString() returned nullptr!\n"); - } -} - -//------------------------------------------------------------------------------ -void -glCaps::printInt(GLenum glEnum, const char* name, int dim) { - o_assert(name); - o_assert_range(dim, 4); - GLint value[4]; - ::glGetIntegerv(glEnum, &(value[0])); - if (1 == dim) Log::Info("%s: %d\n", name, value[0]); - else if (2 == dim) Log::Info("%s: %d %d\n", name, value[0], value[1]); - else if (3 == dim) Log::Info("%s: %d %d %d\n", name, value[0], value[1], value[2]); - else if (4 == dim) Log::Info("%s: %d %d %d %d\n", name, value[0], value[1], value[2], value[3]); -} - -//------------------------------------------------------------------------------ -#if !ORYOL_EMSCRIPTEN -#if defined(GL_ARB_debug_output) -#if ORYOL_WINDOWS -void __stdcall -#else -void -#endif -debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam) -{ - const char* typeStr; - switch (type) - { - case GL_DEBUG_TYPE_ERROR_ARB: typeStr = "ERROR"; break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: typeStr = "DEPRECATED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: typeStr = "UNDEFINED_BEHAVIOR"; break; - case GL_DEBUG_TYPE_PORTABILITY_ARB: typeStr = "PORTABILITY"; break; - case GL_DEBUG_TYPE_PERFORMANCE_ARB: typeStr = "PERFORMANCE"; break; - case GL_DEBUG_TYPE_OTHER_ARB: typeStr = "OTHER"; break; - default: typeStr = ""; break; - } - const char* sevStr; - switch (severity) - { - case GL_DEBUG_SEVERITY_LOW_ARB: sevStr = "LOW"; break; - case GL_DEBUG_SEVERITY_MEDIUM_ARB: sevStr = "MEDIUM"; break; - case GL_DEBUG_SEVERITY_HIGH_ARB: sevStr = "HIGH"; break; - default: sevStr = ""; break; - } - o_warn("##### OpenGL Debug Message:\n" - "type: %s, id: %d, severity: %s\nmsg: %s\n", - typeStr, id, sevStr, message); -} -#endif -#endif - -//------------------------------------------------------------------------------ -void -glCaps::EnableDebugOutput(Severity s) { - #if defined(GL_ARB_debug_output) && !ORYOL_EMSCRIPTEN - if (HasFeature(DebugOutput)) { - GLenum glSeverity = GL_DONT_CARE; - switch (s) { - case SeverityHigh: glSeverity = GL_DEBUG_SEVERITY_HIGH_ARB; break; - case SeverityMedium: glSeverity = GL_DEBUG_SEVERITY_MEDIUM_ARB; break; - case SeverityLow: glSeverity = GL_DEBUG_SEVERITY_LOW_ARB; break; - } - ::glDebugMessageControlARB(GL_DONT_CARE, // source - GL_DONT_CARE, // type - glSeverity, // severity - 0, // count - nullptr, // ids - GL_TRUE); // enabled - if (!state.isDebugOutputEnabled) { - ::glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - ::glDebugMessageCallbackARB(debugOutputCallback, nullptr); - state.isDebugOutputEnabled = true; - } - } - #endif -} - -//------------------------------------------------------------------------------ -void -glCaps::DisableDebugOutput() { - #if defined(GL_ARB_debug_output) && !ORYOL_EMSCRIPTEN - if (glCaps::HasFeature(glCaps::DebugOutput)) { - ::glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - state.isDebugOutputEnabled = false; - } - #endif -} - -//------------------------------------------------------------------------------ -bool -glCaps::IsDebugOutputEnabled() { - return state.isDebugOutputEnabled; -} - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/gl/glCaps.h b/code/Modules/Gfx/private/gl/glCaps.h deleted file mode 100644 index 0d6474d23..000000000 --- a/code/Modules/Gfx/private/gl/glCaps.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glCaps - @ingroup _priv - @brief GL capabilities and extension wrapper -*/ -#include "Core/Types.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/gl/gl_decl.h" - -namespace Oryol { -namespace _priv { - -class glCaps { -public: - enum Limit { - MaxTextureSize = 0, - MaxCubeMapTextureSize, - MaxViewPortWidth, - MaxViewPortHeight, - MaxVertexAttribs, - MaxVertexUniformComponents, - MaxVaryingComponents, - MaxCombinedTextureImageUnits, - MaxVertexTextureImageUnits, - MaxFragmentUniformComponents, - MaxUniformBufferBindings, - UniformBufferOffsetAlignment, - - NumLimits, - }; - - enum Feature { - TextureCompressionDXT, - TextureCompressionPVRTC, - TextureCompressionETC2, - TextureCompressionATC, - TextureFloat, - TextureHalfFloat, - InstancedArrays, - DebugOutput, - MSAARenderTargets, - MapBuffer, - PackedVertexFormat_10_2, - MultipleRenderTarget, - Texture3D, - TextureArray, - - NumFeatures, - }; - - enum Severity { - SeverityHigh, - SeverityMedium, - SeverityLow, - }; - - enum Flavour { - GL_3_3_CORE, - GLES2, - GLES3, - - InvalidFlavour, - }; - - /// setup the info values - static void Setup(Flavour flav); - /// discard the info values - static void Discard(); - /// check if object has been setup - static bool IsValid(); - - /// get an integer limit value - static int IntLimit(Limit c); - /// test if the GL implementation has a feature - static bool HasFeature(Feature f); - /// test if a texture format is supported - static bool HasTextureFormat(PixelFormat::Code fmt); - /// return true if GL version matches 'flavour' - static bool IsFlavour(Flavour flav); - - /// enable debug output (GL_ARB_debug_output) - static void EnableDebugOutput(Severity s); - /// disable debug output - static void DisableDebugOutput(); - /// return true if debug output is enabled - static bool IsDebugOutputEnabled(); - - /// wrapper function for glVertexAttribDivisor - static void VertexAttribDivisor(GLuint index, GLuint divisor); - /// wrapper function for glDrawArraysInstanced - static void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount); - /// wrapper function for glDrawElementsInstanced - static void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei primcount); - - /// setup the limit values - static void setupLimits(Flavour flav); - /// setup the feature flags - static void setupFeatures(Flavour flav); - /// dump general GL info (extensions, constants, ...) - static void printInfo(Flavour flav); - /// print a GL string, optionally replace spaces with newlines - static void printString(GLenum glEnum, const char* name, bool replaceSpaceWithNewLine); - /// print a 1..4 dimensional integer value - static void printInt(GLenum glEnum, const char* name, int dim); - - static struct state_t { - bool isValid = false; - bool isDebugOutputEnabled = false; - Flavour flavour = InvalidFlavour; - int intLimits[NumLimits] = { }; - bool features[NumFeatures] = { }; - } state; -}; - -//------------------------------------------------------------------------------ -inline int -glCaps::IntLimit(Limit l) { - o_assert_dbg(state.isValid); - o_assert_range_dbg(l, NumLimits); - return state.intLimits[l]; -} - -//------------------------------------------------------------------------------ -inline bool -glCaps::HasFeature(Feature f) { - o_assert_dbg(state.isValid); - o_assert_dbg(f < NumFeatures); - return state.features[f]; -} - -//------------------------------------------------------------------------------ -inline bool -glCaps::IsFlavour(Flavour flav) { - o_assert_dbg(state.isValid); - return flav == state.flavour; -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glFactory.cc b/code/Modules/Gfx/private/gl/glFactory.cc deleted file mode 100644 index 2221c9b00..000000000 --- a/code/Modules/Gfx/private/gl/glFactory.cc +++ /dev/null @@ -1,887 +0,0 @@ -//------------------------------------------------------------------------------ -// glFactory.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "glFactory.h" -#include "gl_impl.h" -#include "glTypes.h" -#include "glCaps.h" -#include "Gfx/private/renderer.h" -#include "Gfx/private/resource.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initMesh(mesh& msh, const void* data, int size) { - o_assert_dbg(this->isValid); - if (msh.Setup.ShouldSetupFullScreenQuad()) { - return this->initFullscreenQuad(msh); - } - else { - return this->initStdMesh(msh, data, size); - } -} - -//------------------------------------------------------------------------------ -void -glFactory::destroyMesh(mesh& mesh) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateMeshState(); - for (auto& buf : mesh.buffers) { - for (int i = 0; i < buf.numSlots; i++) { - GLuint glBuf = buf.glBuffers[i]; - if (0 != glBuf) { - ::glDeleteBuffers(1, &glBuf); - } - } - } - mesh.Clear(); -} - -//------------------------------------------------------------------------------ -GLuint -glFactory::createBuffer(GLenum type, const void* data, uint32_t bufSize, Usage::Code usage) { - o_assert_dbg((type == GL_ARRAY_BUFFER) || (type == GL_ELEMENT_ARRAY_BUFFER)); - o_assert_dbg(bufSize > 0); - this->pointers.renderer->invalidateMeshState(); - GLuint buf = 0; - ::glGenBuffers(1, &buf); - ORYOL_GL_CHECK_ERROR(); - o_assert_dbg(0 != buf); - ::glBindBuffer(type, buf); - ::glBufferData(type, bufSize, data, glTypes::asGLBufferUsage(usage)); - ORYOL_GL_CHECK_ERROR(); - this->pointers.renderer->invalidateMeshState(); - return buf; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initStdMesh(mesh& msh, const void* data, int size) { - o_assert_dbg(0 == msh.buffers[mesh::vb].glBuffers[0]); - o_assert_dbg(0 == msh.buffers[mesh::ib].glBuffers[0]); - o_assert_dbg(1 == msh.buffers[mesh::vb].numSlots); - o_assert_dbg(1 == msh.buffers[mesh::ib].numSlots); - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = msh.Setup.NumVertices; - vbAttrs.Layout = msh.Setup.Layout; - vbAttrs.BufferUsage = msh.Setup.VertexUsage; - msh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = msh.Setup.NumIndices; - ibAttrs.Type = msh.Setup.IndicesType; - ibAttrs.BufferUsage = msh.Setup.IndexUsage; - msh.indexBufferAttrs = ibAttrs; - - msh.numPrimGroups = msh.Setup.NumPrimitiveGroups(); - o_assert_dbg(msh.numPrimGroups < GfxConfig::MaxNumPrimGroups); - for (int i = 0; i < msh.numPrimGroups; i++) { - msh.primGroups[i] = msh.Setup.PrimitiveGroup(i); - } - - const uint8_t* ptr = (const uint8_t*)data; - - // create vertex buffer(s) - if (msh.Setup.NumVertices > 0) { - const int vbSize = vbAttrs.NumVertices * vbAttrs.Layout.ByteSize(); - msh.buffers[mesh::vb].numSlots = Usage::Stream == vbAttrs.BufferUsage ? 2 : 1; - const uint8_t* vertices = nullptr; - if (ptr) { - o_assert_dbg(msh.Setup.VertexDataOffset >= 0); - vertices = ptr + msh.Setup.VertexDataOffset; - o_assert_dbg((ptr + size) >= (vertices + vbSize)); - } - for (uint8_t slotIndex = 0; slotIndex < msh.buffers[mesh::vb].numSlots; slotIndex++) { - msh.buffers[mesh::vb].glBuffers[slotIndex] = this->createBuffer(GL_ARRAY_BUFFER, vertices, vbSize, vbAttrs.BufferUsage); - o_assert_dbg(0 != msh.buffers[mesh::vb].glBuffers[slotIndex]); - } - } - - // create optional index buffer(s) - if (ibAttrs.Type != IndexType::None) { - const int ibSize = ibAttrs.NumIndices * IndexType::ByteSize(ibAttrs.Type); - msh.buffers[mesh::ib].numSlots = Usage::Stream == ibAttrs.BufferUsage ? 2 : 1; - const uint8_t* indices = nullptr; - if (ptr) { - o_assert_dbg(msh.Setup.IndexDataOffset >= 0); - indices = ptr + msh.Setup.IndexDataOffset; - o_assert_dbg((ptr + size) >= (indices + ibSize)); - } - for (uint8_t slotIndex = 0; slotIndex < msh.buffers[mesh::ib].numSlots; slotIndex++) { - msh.buffers[mesh::ib].glBuffers[slotIndex] = this->createBuffer(GL_ELEMENT_ARRAY_BUFFER, indices, ibSize, ibAttrs.BufferUsage); - o_assert_dbg(0 != msh.buffers[mesh::ib].glBuffers[slotIndex]); - } - } - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initFullscreenQuad(mesh& mesh) { - o_assert_dbg(mesh.Setup.Layout.NumComponents() == 2); - o_assert_dbg(mesh.Setup.Layout.ComponentAt(0).Attr == VertexAttr::Position); - o_assert_dbg(mesh.Setup.Layout.ComponentAt(1).Attr == VertexAttr::TexCoord0); - o_assert_dbg(mesh.Setup.Layout.ComponentAt(0).Format == VertexFormat::Float3); - o_assert_dbg(mesh.Setup.Layout.ComponentAt(1).Format == VertexFormat::Float2); - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = 4; - vbAttrs.BufferUsage = Usage::Immutable; - vbAttrs.Layout = mesh.Setup.Layout; - mesh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = 6; - ibAttrs.Type = IndexType::Index16; - ibAttrs.BufferUsage = Usage::Immutable; - mesh.indexBufferAttrs = ibAttrs; - - mesh.numPrimGroups = 1; - mesh.primGroups[0] = PrimitiveGroup(0, 6); - - // vertices - const float topV = mesh.Setup.FullScreenQuadFlipV ? 0.0f : 1.0f; - const float botV = mesh.Setup.FullScreenQuadFlipV ? 1.0f : 0.0f; - float vertices[] = { - -1.0f, +1.0f, 0.0f, 0.0f, topV, // top-left corner - +1.0f, +1.0f, 0.0f, 1.0f, topV, // top-right corner - +1.0f, -1.0f, 0.0f, 1.0f, botV, // bottom-right corner - -1.0f, -1.0f, 0.0f, 0.0f, botV // bottom-left corner - }; - - // indices - uint16_t indices[] = { - 0, 2, 1, // topleft -> bottomright -> topright - 0, 3, 2, // topleft -> bottomleft -> bottomright - }; - - // create vertex and index buffer - o_assert_dbg(1 == mesh.buffers[mesh::vb].numSlots); - o_assert_dbg(1 == mesh.buffers[mesh::ib].numSlots); - mesh.buffers[mesh::vb].glBuffers[0] = this->createBuffer(GL_ARRAY_BUFFER, vertices, sizeof(vertices), mesh.vertexBufferAttrs.BufferUsage); - mesh.buffers[mesh::ib].glBuffers[0] = this->createBuffer(GL_ELEMENT_ARRAY_BUFFER, indices, sizeof(indices), mesh.indexBufferAttrs.BufferUsage); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initTexture(texture& tex, const void* data, int size) { - o_assert_dbg(this->isValid); - o_assert_dbg(0 == tex.glTextures[0]); - o_assert_dbg(0 == tex.glTextures[1]); - ORYOL_GL_CHECK_ERROR(); - - const TextureSetup& setup = tex.Setup; - - // test if the texture format is actually supported - if (!glCaps::HasTextureFormat(setup.ColorFormat)) { - o_warn("glTextureFactory: unsupported texture format for resource '%s'\n", setup.Locator.Location().AsCStr()); - return ResourceState::Failed; - } - - // check required features - if ((setup.Type == TextureType::Texture3D) && (!glCaps::HasFeature(glCaps::Texture3D))) { - o_warn("glTextureFactory: 3D textures not supported\n"); - return ResourceState::Failed; - } - if ((setup.Type == TextureType::TextureArray) && (!glCaps::HasFeature(glCaps::TextureArray))) { - o_warn("glTextureFactory: array textures not supported\n"); - return ResourceState::Failed; - } - - // create one or two texture object - tex.numSlots = (Usage::Stream == setup.TextureUsage) ? 2 : 1; - #if ORYOL_DEBUG - // initialize with data is only allowed for immutable texture - if (data) { - o_assert_dbg(setup.TextureUsage == Usage::Immutable); - o_assert_dbg(setup.ImageData.NumMipMaps > 0); - o_assert_dbg(setup.ImageData.NumFaces > 0); - } - #endif - const GLenum glTexImageInternalFormat = glTypes::asGLTexImageInternalFormat(setup.ColorFormat); - const GLenum glTexTarget = glTypes::asGLTextureTarget(setup.Type); - if (tex.Setup.ShouldSetupFromNativeTexture()) { - // existing native GL texture object provided - tex.nativeHandles = true; - tex.glTextures[0] = (GLuint) tex.Setup.NativeHandle[0]; - tex.glTextures[1] = (GLuint) tex.Setup.NativeHandle[1]; - } - else { - // create GL texture objects - const GLenum glTexImageFormat = glTypes::asGLTexImageFormat(setup.ColorFormat); - const bool isCompressed = PixelFormat::IsCompressedFormat(setup.ColorFormat); - const uint8_t* srcPtr = (const uint8_t*) data; - for (int slotIndex = 0; slotIndex < tex.numSlots; slotIndex++) { - - this->pointers.renderer->invalidateTextureState(); - ::glGenTextures(1, &tex.glTextures[slotIndex]); - ::glActiveTexture(GL_TEXTURE0); - ::glBindTexture(glTexTarget, tex.glTextures[slotIndex]); - ORYOL_GL_CHECK_ERROR(); - - GLenum glMinFilter = glTypes::asGLTexFilterMode(setup.Sampler.MinFilter); - GLenum glMagFilter = glTypes::asGLTexFilterMode(setup.Sampler.MagFilter); - if (1 == setup.NumMipMaps) { - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - ::glTexParameteri(glTexTarget, GL_TEXTURE_MAX_LEVEL, 0); // see: http://www.opengl.org/wiki/Hardware_specifics:_NVidia - } - #endif - if ((glMinFilter == GL_NEAREST_MIPMAP_NEAREST) || (glMinFilter == GL_NEAREST_MIPMAP_LINEAR)) { - glMinFilter = GL_NEAREST; - } - else if ((glMinFilter == GL_LINEAR_MIPMAP_NEAREST) || (glMinFilter == GL_LINEAR_MIPMAP_LINEAR)) { - glMinFilter = GL_LINEAR; - } - } - ::glTexParameteri(glTexTarget, GL_TEXTURE_MIN_FILTER, glMinFilter); - ::glTexParameteri(glTexTarget, GL_TEXTURE_MAG_FILTER, glMagFilter); - if (setup.Type == TextureType::TextureCube) { - ::glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - ::glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - else { - ::glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_S, glTypes::asGLTexWrapMode(setup.Sampler.WrapU)); - ::glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_T, glTypes::asGLTexWrapMode(setup.Sampler.WrapV)); - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - if (setup.Type == TextureType::Texture3D) { - ::glTexParameteri(glTexTarget, GL_TEXTURE_WRAP_R, glTypes::asGLTexWrapMode(setup.Sampler.WrapW)); - } - } - #endif - } - ORYOL_GL_CHECK_ERROR(); - - const int numFaces = setup.Type == TextureType::TextureCube ? 6 : 1; - for (int faceIndex = 0; faceIndex < numFaces; faceIndex++) { - GLenum glImgTarget; - if (TextureType::TextureCube == setup.Type) { - glImgTarget = glTypes::asGLCubeFaceTarget(faceIndex); - } - else { - glImgTarget = glTexTarget; - } - for (int mipIndex = 0; mipIndex < setup.NumMipMaps; mipIndex++) { - GLvoid* mipDataPtr = nullptr; - int mipDataSize = 0; - if (srcPtr) { - if (mipIndex < setup.ImageData.NumMipMaps) { - mipDataPtr = (GLvoid*)(srcPtr + setup.ImageData.Offsets[faceIndex][mipIndex]); - mipDataSize = setup.ImageData.Sizes[faceIndex][mipIndex]; - o_assert_dbg(mipDataSize > 0); - } - } - int mipWidth = setup.Width >> mipIndex; - if (mipWidth == 0) { - mipWidth = 1; - } - int mipHeight = setup.Height >> mipIndex; - if (mipHeight == 0) { - mipHeight = 1; - } - if ((TextureType::Texture2D == setup.Type) || (TextureType::TextureCube == setup.Type)) { - if (isCompressed) { - ::glCompressedTexImage2D(glImgTarget, - mipIndex, - glTexImageInternalFormat, - mipWidth, mipHeight, - 0, - mipDataSize, mipDataPtr); - ORYOL_GL_CHECK_ERROR(); - } - else { - const GLenum glTexImageType = glTypes::asGLTexImageType(setup.ColorFormat); - ::glTexImage2D(glImgTarget, - mipIndex, - glTexImageInternalFormat, - mipWidth, mipHeight, - 0, - glTexImageFormat, - glTexImageType, - mipDataPtr); - ORYOL_GL_CHECK_ERROR(); - } - } - #if !ORYOL_OPENGLES2 - else if ((TextureType::Texture3D == setup.Type) || (TextureType::TextureArray == setup.Type)) { - int mipDepth = setup.Depth >> mipIndex; - if (mipDepth == 0) { - mipDepth = 1; - } - if (isCompressed) { - ::glCompressedTexImage3D(glImgTarget, - mipIndex, - glTexImageInternalFormat, - mipWidth, mipHeight, mipDepth, - 0, - mipDataSize, mipDataPtr); - ORYOL_GL_CHECK_ERROR(); - } - else { - const GLenum glTexImageType = glTypes::asGLTexImageType(setup.ColorFormat); - ::glTexImage3D(glImgTarget, - mipIndex, - glTexImageInternalFormat, - mipWidth, mipHeight, mipDepth, - 0, - glTexImageFormat, - glTexImageType, - mipDataPtr); - ORYOL_GL_CHECK_ERROR(); - } - } - #endif - } - } - } - } - - // additional render target stuff - if (setup.IsRenderTarget) { - // create MSAA renderbuffer - #if !ORYOL_OPENGLES2 - const bool msaa = (setup.SampleCount > 1) && glCaps::HasFeature(glCaps::MSAARenderTargets); - if (msaa) { - ::glGenRenderbuffers(1, &tex.glMSAARenderbuffer); - o_assert_dbg(0 != tex.glMSAARenderbuffer); - ::glBindRenderbuffer(GL_RENDERBUFFER, tex.glMSAARenderbuffer); - ::glRenderbufferStorageMultisample(GL_RENDERBUFFER, setup.SampleCount, glTexImageInternalFormat, setup.Width, setup.Height); - } - #endif - ORYOL_GL_CHECK_ERROR(); - - // create depth buffer - if (setup.HasDepth()) { - o_assert_dbg(PixelFormat::IsValidTextureDepthFormat(setup.DepthFormat)); - ::glGenRenderbuffers(1, &tex.glDepthRenderbuffer); - o_assert_dbg(0 != tex.glDepthRenderbuffer); - ::glBindRenderbuffer(GL_RENDERBUFFER, tex.glDepthRenderbuffer); - GLenum glDepthFormat = glTypes::asGLDepthAttachmentFormat(setup.DepthFormat); - #if !ORYOL_OPENGLES2 - if (msaa) { - ::glRenderbufferStorageMultisample(GL_RENDERBUFFER, setup.SampleCount, glDepthFormat, setup.Width, setup.Height); - } - else - #endif - { - ::glRenderbufferStorage(GL_RENDERBUFFER, glDepthFormat, setup.Width, setup.Height); - } - ORYOL_GL_CHECK_ERROR(); - } - } - - // setup texture object - TextureAttrs attrs; - attrs.Locator = tex.Setup.Locator; - attrs.Type = tex.Setup.Type; - attrs.ColorFormat = tex.Setup.ColorFormat; - attrs.DepthFormat = tex.Setup.DepthFormat; - attrs.SampleCount = tex.Setup.SampleCount; - attrs.TextureUsage = tex.Setup.TextureUsage; - attrs.Width = tex.Setup.Width; - attrs.Height = tex.Setup.Height; - attrs.Depth = tex.Setup.Depth; - attrs.NumMipMaps = tex.Setup.NumMipMaps; - attrs.IsRenderTarget = tex.Setup.IsRenderTarget; - attrs.HasDepthBuffer = tex.Setup.HasDepth(); - tex.textureAttrs = attrs; - tex.glTarget = glTexTarget; - - this->pointers.renderer->invalidateTextureState(); - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -glFactory::destroyTexture(texture& tex) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateTextureState(); - if (!tex.nativeHandles) { - for (auto& glTex : tex.glTextures) { - if (0 != glTex) { - ::glDeleteTextures(1, &glTex); - ORYOL_GL_CHECK_ERROR(); - } - } - } - if (0 != tex.glMSAARenderbuffer) { - ::glDeleteRenderbuffers(1, &tex.glMSAARenderbuffer); - ORYOL_GL_CHECK_ERROR(); - } - if (0 != tex.glDepthRenderbuffer) { - ::glDeleteRenderbuffers(1, &tex.glDepthRenderbuffer); - ORYOL_GL_CHECK_ERROR(); - } - tex.Clear(); -} - -//------------------------------------------------------------------------------ -static void -attachFramebufferTextures(const renderPass& rp) { - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - texture* colorTex = rp.colorTextures[i]; - if (colorTex) { - const GLuint glTex = colorTex->glTextures[0]; - o_assert_dbg(glTex); - const auto& att = rp.Setup.ColorAttachments[i]; - switch (colorTex->textureAttrs.Type) { - case TextureType::Texture2D: - ::glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0+i, - GL_TEXTURE_2D, - glTex, - att.MipLevel); - break; - case TextureType::TextureCube: - ::glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0+i, - glTypes::asGLCubeFaceTarget(att.Slice), - glTex, - att.MipLevel); - break; - default: - #if !ORYOL_OPENGLES2 - // 3D- and 2D-array-texture - ::glFramebufferTextureLayer(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0+i, - glTex, - att.MipLevel, - att.Slice); - #endif - break; - } - } - } - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -static void -attachFramebufferRenderbuffers(const renderPass& rp) { - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - texture* colorTex = rp.colorTextures[i]; - if (colorTex) { - const GLuint glRenderBuf = colorTex->glMSAARenderbuffer; - o_assert_dbg(glRenderBuf); - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, glRenderBuf); - } - } - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -static void -attachFramebufferDepthStencilBuffer(const renderPass& rp) { - texture* dsTex = rp.depthStencilTexture; - if (dsTex) { - const GLuint glDepthRenderBuffer = dsTex->glDepthRenderbuffer; - o_assert_dbg(glDepthRenderBuffer); - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, glDepthRenderBuffer); - if (PixelFormat::IsDepthStencilFormat(dsTex->Setup.DepthFormat)) { - ::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, glDepthRenderBuffer); - } - } -} - -//------------------------------------------------------------------------------ -static void -checkFramebufferCompleteness() { - const GLenum status = ::glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - const char* reason; - switch (status) { - #if !ORYOL_OPENGLES2 - case GL_FRAMEBUFFER_UNDEFINED: reason = "GL_FRAMEBUFFER_UNDEFINED"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: reason = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; break; - #endif - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: reason = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: reason = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; break; - case GL_FRAMEBUFFER_UNSUPPORTED: reason = "GL_FRAMEBUFFER_UNSUPPORTED"; break; - #if ORYOL_OPENGLES2 && !ORYOL_EMSCRIPTEN - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: reason = "GL_FRAMEBUFFER_IMCOMPLETE_DIMENSIONS"; break; - #endif - default: reason = "unknown"; - } - o_warn("glRenderPassFactory: framebuffer completeness check failed with '%s'\n", reason); - } -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - o_assert_dbg(0 == rp.glFramebuffer); - - gfxFactoryBase::initRenderPass(rp); - o_assert_dbg(rp.colorTextures[0]); - const bool isMSAA = 0 != rp.colorTextures[0]->glMSAARenderbuffer; - - GLint glOrigFramebuffer = 0; - ::glGetIntegerv(GL_FRAMEBUFFER_BINDING, &glOrigFramebuffer); - ORYOL_GL_CHECK_ERROR(); - - // create new framebuffer object - ::glGenFramebuffers(1, &rp.glFramebuffer); - o_assert_dbg(0 != rp.glFramebuffer); - ::glBindFramebuffer(GL_FRAMEBUFFER, rp.glFramebuffer); - ORYOL_GL_CHECK_ERROR(); - - // attach color textures or MSAA render buffers, and depth-stencil buffer - if (isMSAA) { - attachFramebufferRenderbuffers(rp); - } - else { - attachFramebufferTextures(rp); - } - attachFramebufferDepthStencilBuffer(rp); - - // check framebuffer completeness - checkFramebufferCompleteness(); - - // for MSAA, need to create resolve-framebuffers... - if (isMSAA) { - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - texture* colorTex = rp.colorTextures[i]; - if (colorTex) { - ::glGenFramebuffers(1, &rp.glMSAAResolveFramebuffers[i]); - ::glBindFramebuffer(GL_FRAMEBUFFER, rp.glMSAAResolveFramebuffers[i]); - const GLuint glTex = colorTex->glTextures[0]; - o_assert_dbg(glTex); - const auto& att = rp.Setup.ColorAttachments[i]; - switch (colorTex->textureAttrs.Type) { - case TextureType::Texture2D: - ::glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, - glTex, - att.MipLevel); - break; - case TextureType::TextureCube: - ::glFramebufferTexture2D(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - glTypes::asGLCubeFaceTarget(att.Slice), - glTex, - att.MipLevel); - break; - default: - #if !ORYOL_OPENGLES2 - // 2D-array and 3D texture - ::glFramebufferTextureLayer(GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - glTex, - att.MipLevel, - att.Slice); - #endif - break; - } - checkFramebufferCompleteness(); - } - } - } - - // restore default framebuffer binding - ::glBindFramebuffer(GL_FRAMEBUFFER, glOrigFramebuffer); - ORYOL_GL_CHECK_ERROR(); - this->pointers.renderer->invalidateTextureState(); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -glFactory::destroyRenderPass(renderPass& rp) { - o_assert_dbg(this->isValid); - if (0 != rp.glFramebuffer) { - ::glDeleteFramebuffers(1, &rp.glFramebuffer); - } - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (rp.glMSAAResolveFramebuffers[i]) { - ::glDeleteFramebuffers(1, &(rp.glMSAAResolveFramebuffers[i])); - } - } - ORYOL_GL_CHECK_ERROR(); - gfxFactoryBase::destroyRenderPass(rp); -} - -//------------------------------------------------------------------------------ -static GLuint -compileShader(ShaderStage::Code stage, const char* sourceString, int sourceLen) { - o_assert_dbg(sourceString && (sourceLen > 0)); - - GLuint glShader = ::glCreateShader(glTypes::asGLShaderStage(stage)); - o_assert_dbg(0 != glShader); - ORYOL_GL_CHECK_ERROR(); - - // attach source to shader object - ::glShaderSource(glShader, 1, &sourceString, &sourceLen); - ORYOL_GL_CHECK_ERROR(); - - // compile the shader - ::glCompileShader(glShader); - ORYOL_GL_CHECK_ERROR(); - - // compilation failed? - GLint compileStatus = 0; - ::glGetShaderiv(glShader, GL_COMPILE_STATUS, &compileStatus); - ORYOL_GL_CHECK_ERROR(); - - #if ORYOL_DEBUG - GLint logLength = 0; - ::glGetShaderiv(glShader, GL_INFO_LOG_LENGTH, &logLength); - ORYOL_GL_CHECK_ERROR(); - if (logLength > 0) { - - // first print the shader source - Log::Info("SHADER SOURCE:\n%s\n\n", sourceString); - - // now print the info log - GLchar* shdLogBuf = (GLchar*) Memory::Alloc(logLength); - ::glGetShaderInfoLog(glShader, logLength, &logLength, shdLogBuf); - ORYOL_GL_CHECK_ERROR(); - Log::Info("SHADER LOG: %s\n\n", shdLogBuf); - Memory::Free(shdLogBuf); - } - #endif - - if (!compileStatus) { - // compiling failed - ::glDeleteShader(glShader); - ORYOL_GL_CHECK_ERROR(); - glShader = 0; - } - return glShader; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initShader(shader& shd) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateShaderState(); - - #if ORYOL_OPENGLES2 - const ShaderLang::Code slang = ShaderLang::GLSL100; - #elif ORYOL_OPENGLES3 - const ShaderLang::Code slang = glCaps::IsFlavour(glCaps::GLES3) ? ShaderLang::GLSLES3 : ShaderLang::GLSL100; - #elif ORYOL_OPENGL_CORE_PROFILE - const ShaderLang::Code slang = ShaderLang::GLSL330; - #else - #error "Unsupported GL profile" - #endif - const ShaderSetup& setup = shd.Setup; - - o_assert_dbg(setup.VertexShaderSource(slang).IsValid()); - o_assert_dbg(setup.FragmentShaderSource(slang).IsValid()); - - // compile vertex shader - const String& vsSource = setup.VertexShaderSource(slang); - GLuint glVertexShader = compileShader(ShaderStage::VS, vsSource.AsCStr(), vsSource.Length()); - o_assert_dbg(0 != glVertexShader); - - // compile fragment shader - const String& fsSource = setup.FragmentShaderSource(slang); - GLuint glFragmentShader = compileShader(ShaderStage::FS, fsSource.AsCStr(), fsSource.Length()); - o_assert_dbg(0 != glFragmentShader); - - // create GL program object and attach vertex/fragment shader - GLuint glProg = ::glCreateProgram(); - ::glAttachShader(glProg, glVertexShader); - ORYOL_GL_CHECK_ERROR(); - ::glAttachShader(glProg, glFragmentShader); - ORYOL_GL_CHECK_ERROR(); - - // bind vertex attribute locations - #if !ORYOL_GL_USE_GETATTRIBLOCATION - o_assert_dbg(VertexAttr::NumVertexAttrs <= glCaps::IntLimit(glCaps::MaxVertexAttribs)); - const VertexLayout& vsInputLayout = setup.InputLayout(); - for (int i = 0; i < VertexAttr::NumVertexAttrs; i++) { - VertexAttr::Code attr = (VertexAttr::Code)i; - if (vsInputLayout.Contains(attr)) { - ::glBindAttribLocation(glProg, i, VertexAttr::ToString(attr)); - } - } - ORYOL_GL_CHECK_ERROR(); - #endif - - // link the program - ::glLinkProgram(glProg); - ORYOL_GL_CHECK_ERROR(); - - // can discard shaders now if we compiled them ourselves - ::glDeleteShader(glVertexShader); - ::glDeleteShader(glFragmentShader); - - // linking successful? - GLint linkStatus; - ::glGetProgramiv(glProg, GL_LINK_STATUS, &linkStatus); - #if ORYOL_DEBUG - GLint logLength; - ::glGetProgramiv(glProg, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) { - GLchar* logBuffer = (GLchar*) Memory::Alloc(logLength); - ::glGetProgramInfoLog(glProg, logLength, &logLength, logBuffer); - Log::Info("%s\n", logBuffer); - Memory::Free(logBuffer); - } - #endif - ORYOL_GL_CHECK_ERROR(); - if (!linkStatus) { - o_warn("Failed to link program '%s'\n", setup.Locator.Location().AsCStr()); - return ResourceState::Failed; - } - - // linking succeeded, store GL program - shd.glProgram = glProg; - this->pointers.renderer->useProgram(glProg); - - // lookup uniform locations (one vec4[] per uniform block) - const int numUniformBlocks = setup.NumUniformBlocks(); - for (int ubIndex = 0; ubIndex < numUniformBlocks; ubIndex++) { - ShaderStage::Code ubBindStage = setup.UniformBlockBindStage(ubIndex); - int ubBindSlot = setup.UniformBlockBindSlot(ubIndex); - const StringAtom& ubName = setup.UniformBlockType(ubIndex); - GLint glUniformLocation = ::glGetUniformLocation(glProg, ubName.AsCStr()); - if (-1 != glUniformLocation) { - shd.bindUniformBlock(ubBindStage, ubBindSlot, glUniformLocation); - } - else { - Log::Warn("Uniform '%s' not found on shader, will be ignored!\n", ubName.AsCStr()); - } - } - - // resolve texture locations - int glTextureLocation = 0; - const int numTextures = setup.NumTextures(); - for (int texIndex = 0; texIndex < numTextures; texIndex++) { - ShaderStage::Code bindStage = setup.TexBindStage(texIndex); - int bindSlot = setup.TexBindSlot(texIndex); - const StringAtom& name = setup.TexName(texIndex); - const GLint glUniformLocation = ::glGetUniformLocation(glProg, name.AsCStr()); - if (-1 != glUniformLocation) { - shd.bindSampler(bindStage, bindSlot, glTextureLocation); - // set the sampler index in the shader program, this will never change - ::glUniform1i(glUniformLocation, glTextureLocation); - glTextureLocation++; - } - else { - Log::Warn("Sampler uniform '%s' not found on shader, will be ignored!\n", name.AsCStr()); - } - } - ORYOL_GL_CHECK_ERROR(); - - #if ORYOL_GL_USE_GETATTRIBLOCATION - // resolve attrib locations - for (int32 i = 0; i < VertexAttr::NumVertexAttrs; i++) { - GLint loc = ::glGetAttribLocation(glProg, VertexAttr::ToString((VertexAttr::Code)i)); - shd.bindAttribLocation((VertexAttr::Code)i, loc); - } - #endif - - this->pointers.renderer->invalidateShaderState(); - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -glFactory::destroyShader(shader& shd) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateShaderState(); - if (0 != shd.glProgram) { - ::glDeleteProgram(shd.glProgram); - ORYOL_GL_CHECK_ERROR(); - } - shd.Clear(); -} - -//------------------------------------------------------------------------------ -// map Oryol VertexFormats to GL vertex attributes -struct { - GLint size; - GLenum type; - GLboolean normalized; -} const vertexFormatTable[VertexFormat::NumVertexFormats] = { - { 1, GL_FLOAT, GL_FALSE }, // VertexFormat::Float - { 2, GL_FLOAT, GL_FALSE }, // VertexFormat::Float2 - { 3, GL_FLOAT, GL_FALSE }, // VertexFormat::Float3 - { 4, GL_FLOAT, GL_FALSE }, // VertexFormat::Float4 - { 4, GL_BYTE, GL_FALSE }, // VertexFormat::Byte4 - { 4, GL_BYTE, GL_TRUE }, // VertexFormat::Byte4N - { 4, GL_UNSIGNED_BYTE, GL_FALSE }, // VertexFormat::UByte4 - { 4, GL_UNSIGNED_BYTE, GL_TRUE }, // VertexFormat::UByte4N - { 2, GL_SHORT, GL_FALSE }, // VertexFormat::Short2 - { 2, GL_SHORT, GL_TRUE }, // VertexFormat::Short2N - { 4, GL_SHORT, GL_FALSE }, // VertexFormat::Short4 - { 4, GL_SHORT, GL_TRUE }, // VertexFormat::Short4N - { 4, GL_UNSIGNED_INT_2_10_10_10_REV, GL_TRUE }, // VertexFormat::UInt10_2N -}; - -//------------------------------------------------------------------------------ -static void -initVertexAttrs(pipeline& pip) { - // first disable all vertex attrs - for (int attrIndex = 0; attrIndex < VertexAttr::NumVertexAttrs; attrIndex++) { - pip.glAttrs[attrIndex] = pipeline::vertexAttr(); - pip.glAttrs[attrIndex].index = attrIndex; - } - - // convert input mesh vertex layout components to GL vertex attr - const VertexLayout& vsInputLayout = pip.shd->Setup.InputLayout(); - for (int layoutIndex = 0; layoutIndex < GfxConfig::MaxNumInputMeshes; layoutIndex++) { - const VertexLayout& layout = pip.Setup.Layouts[layoutIndex]; - const int numComps = layout.NumComponents(); - if (numComps > 0) { - for (int compIndex = 0; compIndex < numComps; compIndex++) { - const VertexLayout::Component& comp = layout.ComponentAt(compIndex); - if (vsInputLayout.Contains(comp.Attr)) { - o_assert_dbg(comp.Format < VertexFormat::NumVertexFormats); - auto& glAttr = pip.glAttrs[comp.Attr]; - o_assert_dbg(!glAttr.enabled); - glAttr.enabled = GL_TRUE; - glAttr.vbIndex = layoutIndex; - if (VertexStepFunction::PerVertex == layout.StepFunction) { - glAttr.divisor = 0; - } - else { - glAttr.divisor = layout.StepRate; - } - glAttr.stride = layout.ByteSize(); - glAttr.offset = layout.ComponentByteOffset(compIndex); - glAttr.size = vertexFormatTable[comp.Format].size; - glAttr.type = vertexFormatTable[comp.Format].type; - glAttr.normalized = vertexFormatTable[comp.Format].normalized; - } - } - } - } -} - -//------------------------------------------------------------------------------ -ResourceState::Code -glFactory::initPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - - gfxFactoryBase::initPipeline(pip); - o_assert_dbg(pip.shd); - initVertexAttrs(pip); - pip.glPrimType = glTypes::asGLPrimitiveType(pip.Setup.PrimType); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -glFactory::destroyPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - this->pointers.renderer->invalidateMeshState(); - pip.Clear(); -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glFactory.h b/code/Modules/Gfx/private/gl/glFactory.h deleted file mode 100644 index 9420d508d..000000000 --- a/code/Modules/Gfx/private/gl/glFactory.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glFactory.h - @ingroup _priv - @brief GL implementation of gfxFactory -*/ -#include "Gfx/private/gfxFactoryBase.h" -#include "Gfx/private/gl/gl_decl.h" - -namespace Oryol { -namespace _priv { - -class mesh; -class texture; -class renderPass; -class shader; -class pipeline; - -class glFactory : public gfxFactoryBase { -public: - /// initialize new mesh object - ResourceState::Code initMesh(mesh& msh, const void* data, int size); - /// destroy a mesh object - void destroyMesh(mesh& msh); - /// initialize a new texture object - ResourceState::Code initTexture(texture& tex, const void* data, int size); - /// destroy a texture object - void destroyTexture(texture& tex); - /// initialize a new render pass object - ResourceState::Code initRenderPass(renderPass& rp); - /// destroy a render pass object - void destroyRenderPass(renderPass& rp); - /// initialize a new shader object - ResourceState::Code initShader(shader& shd); - /// destroy a shader object - void destroyShader(shader& shd); - /// initialize a new pipeline object - ResourceState::Code initPipeline(pipeline& pip); - /// destroy a pipeline object - void destroyPipeline(pipeline& pip); - - /// helper method to setup a mesh object as fullscreen quad - ResourceState::Code initFullscreenQuad(mesh& mesh); - /// helper method to create a standard mesh - ResourceState::Code initStdMesh(mesh& mesh, const void* data, int size); - /// helper method to create vertex or index buffer - GLuint createBuffer(GLenum type, const void* data, uint32_t dataSize, Usage::Code usage); -}; - -} -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glRenderer.cc b/code/Modules/Gfx/private/gl/glRenderer.cc deleted file mode 100644 index 9a15f8298..000000000 --- a/code/Modules/Gfx/private/gl/glRenderer.cc +++ /dev/null @@ -1,1073 +0,0 @@ -//------------------------------------------------------------------------------ -// glRenderer.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "Core/Core.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/private/resource.h" -#include "gl_impl.h" -#include "glRenderer.h" -#include "glTypes.h" -#include "glCaps.h" -#include "glm/vec4.hpp" -#include "glm/gtc/type_ptr.hpp" - -namespace Oryol { -namespace _priv { - -GLenum glRenderer::mapCompareFunc[CompareFunc::NumCompareFuncs] = { - GL_NEVER, - GL_LESS, - GL_EQUAL, - GL_LEQUAL, - GL_GREATER, - GL_NOTEQUAL, - GL_GEQUAL, - GL_ALWAYS -}; - -GLenum glRenderer::mapStencilOp[StencilOp::NumStencilOperations] = { - GL_KEEP, - GL_ZERO, - GL_REPLACE, - GL_INCR, - GL_DECR, - GL_INVERT, - GL_INCR_WRAP, - GL_DECR_WRAP -}; - -GLenum glRenderer::mapBlendFactor[BlendFactor::NumBlendFactors] = { - GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA_SATURATE, - GL_CONSTANT_COLOR, - GL_ONE_MINUS_CONSTANT_COLOR, - GL_CONSTANT_ALPHA, - GL_ONE_MINUS_CONSTANT_ALPHA, -}; - -GLenum glRenderer::mapBlendOp[BlendOperation::NumBlendOperations] = { - GL_FUNC_ADD, - GL_FUNC_SUBTRACT, - GL_FUNC_REVERSE_SUBTRACT, -}; - -GLenum glRenderer::mapCullFace[Face::NumFaceCodes] = { - GL_FRONT, - GL_BACK, - GL_FRONT_AND_BACK, -}; - -//------------------------------------------------------------------------------ -glRenderer::glRenderer() { - this->blendColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - this->samplers.Fill(0); - this->glAttrVBs.Fill(0); -} - -//------------------------------------------------------------------------------ -glRenderer::~glRenderer() { - o_assert_dbg(!this->valid); -} - -//------------------------------------------------------------------------------ -void -glRenderer::setup(const GfxSetup& setup, const gfxPointers& ptrs) { - o_assert_dbg(!this->valid); - - this->valid = true; - this->pointers = ptrs; - this->gfxSetup = setup; - this->frameIndex = 0; - - #if ORYOL_GL_USE_GETATTRIBLOCATION - o_warn("glRenderer: ORYOL_GL_USE_GETATTRIBLOCATION is ON\n"); - #endif - - // in case we are on a Core Profile, create a global Vertex Array Object - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - ::glGenVertexArrays(1, &this->globalVAO); - ::glBindVertexArray(this->globalVAO); - ORYOL_GL_CHECK_ERROR(); - } - #endif - - #if !(ORYOL_OPENGLES2 || ORYOL_OPENGLES3) - ::glEnable(GL_PROGRAM_POINT_SIZE); - ORYOL_GL_CHECK_ERROR(); - #endif - - this->setupDepthStencilState(); - this->setupBlendState(); - this->setupRasterizerState(); - this->invalidateMeshState(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::discard() { - o_assert_dbg(this->valid); - - this->invalidateMeshState(); - this->invalidateShaderState(); - this->invalidateTextureState(); - this->curRenderPass = nullptr; - this->curPipeline = nullptr; - - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - ::glDeleteVertexArrays(1, &this->globalVAO); - this->globalVAO = 0; - } - #endif - - this->pointers = gfxPointers(); - this->valid = false; -} - -//------------------------------------------------------------------------------ -bool -glRenderer::isValid() const { - return this->valid; -} - -//------------------------------------------------------------------------------ -void -glRenderer::resetStateCache() { - o_assert_dbg(this->valid); - - this->setupDepthStencilState(); - this->setupBlendState(); - this->setupRasterizerState(); - this->invalidateMeshState(); - this->invalidateShaderState(); - this->invalidateTextureState(); -} - -//------------------------------------------------------------------------------ -bool -glRenderer::queryFeature(GfxFeature::Code feat) const { - o_assert_dbg(this->valid); - - switch (feat) { - case GfxFeature::TextureCompressionDXT: - return glCaps::HasFeature(glCaps::TextureCompressionDXT); - case GfxFeature::TextureCompressionPVRTC: - return glCaps::HasFeature(glCaps::TextureCompressionPVRTC); - case GfxFeature::TextureCompressionATC: - return glCaps::HasFeature(glCaps::TextureCompressionATC); - case GfxFeature::TextureCompressionETC2: - return glCaps::HasFeature(glCaps::TextureCompressionETC2); - case GfxFeature::TextureFloat: - return glCaps::HasFeature(glCaps::TextureFloat); - case GfxFeature::TextureHalfFloat: - return glCaps::HasFeature(glCaps::TextureHalfFloat); - case GfxFeature::Instancing: - return glCaps::HasFeature(glCaps::InstancedArrays); - case GfxFeature::OriginBottomLeft: - case GfxFeature::NativeTexture: - return true; - case GfxFeature::MSAARenderTargets: - return glCaps::HasFeature(glCaps::MSAARenderTargets); - case GfxFeature::PackedVertexFormat_10_2: - return glCaps::HasFeature(glCaps::PackedVertexFormat_10_2); - case GfxFeature::MultipleRenderTarget: - return glCaps::HasFeature(glCaps::MultipleRenderTarget); - case GfxFeature::Texture3D: - return glCaps::HasFeature(glCaps::Texture3D); - case GfxFeature::TextureArray: - return glCaps::HasFeature(glCaps::TextureArray); - default: - return false; - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::commitFrame() { - o_assert_dbg(this->valid); - this->rpValid = false; - this->curRenderPass = nullptr; - this->curPipeline = nullptr; - this->curPrimaryMesh = nullptr; - this->frameIndex++; -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyViewPort(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->valid); - - // flip origin top/bottom if requested (this is a D3D/GL compatibility thing) - y = originTopLeft ? (this->rpAttrs.FramebufferHeight - (y + height)) : y; - - if ((x != this->viewPortX) || - (y != this->viewPortY) || - (width != this->viewPortWidth) || - (height != this->viewPortHeight)) { - - this->viewPortX = x; - this->viewPortY = y; - this->viewPortWidth = width; - this->viewPortHeight = height; - #if ORYOL_IOS - // fix iOS high-dpi coordinates (only for default rendertarget) - if (!this->curRenderPass && this->gfxSetup.HighDPI) { - x*=2; y*=2; width*=2; height*=2; - } - #endif - ::glViewport(x, y, width, height); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyScissorRect(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->valid); - - // flip origin top/bottom if requested (this is a D3D/GL compatibility thing) - y = originTopLeft ? (this->rpAttrs.FramebufferHeight - (y + height)) : y; - - if ((x != this->scissorX) || - (y != this->scissorY) || - (width != this->scissorWidth) || - (height != this->scissorHeight)) { - - this->scissorX = x; - this->scissorY = y; - this->scissorWidth = width; - this->scissorHeight = height; - #if ORYOL_IOS - // fix iOS high-dpi coordinates (only for default rendertarget) - if (!this->curRenderPass && this->gfxSetup.HighDPI) { - x*=2; y*=2; width*=2; height*=2; - } - #endif - ::glScissor(x, y, width, height); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::beginPass(renderPass* pass, const PassAction* action) { - o_assert_dbg(this->valid); - o_assert_dbg(action); - ORYOL_GL_CHECK_ERROR(); - - if (nullptr == pass) { - this->rpAttrs = this->pointers.displayMgr->GetDisplayAttrs(); - } - else { - o_assert_dbg(pass->colorTextures[0]); - this->rpAttrs = DisplayAttrs::FromTextureAttrs(pass->colorTextures[0]->textureAttrs); - } - - o_assert_dbg(nullptr == this->curRenderPass); - if (nullptr == pass) { - this->pointers.displayMgr->glBindDefaultFramebuffer(); - } - else { - ::glBindFramebuffer(GL_FRAMEBUFFER, pass->glFramebuffer); - ORYOL_GL_CHECK_ERROR(); - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - int numAtts = 0; - GLenum att[GfxConfig::MaxNumColorAttachments] = { }; - for (; numAtts < GfxConfig::MaxNumColorAttachments; numAtts++) { - if (pass->colorTextures[numAtts]) { - att[numAtts] = GL_COLOR_ATTACHMENT0 + numAtts; - } - else { - break; - } - } - ::glDrawBuffers(numAtts, att); - ORYOL_GL_CHECK_ERROR(); - } - #endif - } - ORYOL_GL_CHECK_ERROR(); - this->curRenderPass = pass; - this->rpValid = true; - - // prepare state for clear operations - this->applyViewPort(0, 0, this->rpAttrs.FramebufferWidth, this->rpAttrs.FramebufferHeight, false); - if (this->rasterizerState.ScissorTestEnabled) { - this->rasterizerState.ScissorTestEnabled = false; - ::glDisable(GL_SCISSOR_TEST); - } - if (PixelChannel::RGBA != this->blendState.ColorWriteMask) { - this->blendState.ColorWriteMask = PixelChannel::RGBA; - ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - if (!this->depthStencilState.DepthWriteEnabled) { - this->depthStencilState.DepthWriteEnabled = true; - ::glDepthMask(GL_TRUE); - } - if (this->depthStencilState.StencilWriteMask != 0xFF) { - this->depthStencilState.StencilWriteMask = 0xFF; - ::glStencilMask(0xFF); - } - ORYOL_GL_CHECK_ERROR(); - - // perform clear actions on render targets - // FIXME: GL_EXT_discard_framebuffer for DontCare - if ((nullptr == pass) || glCaps::IsFlavour(glCaps::GLES2)) { - // special case: default render pass or no MRT support - GLbitfield clearMask = 0; - if (action->Flags & PassAction::ClearC0) { - clearMask |= GL_COLOR_BUFFER_BIT; - const auto& c = action->Color[0]; - ::glClearColor(c.x, c.y, c.z, c.w); - } - if (action->Flags & PassAction::ClearDS) { - clearMask |= GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT; - #if (ORYOL_OPENGLES2 || ORYOL_OPENGLES3) - ::glClearDepthf(action->Depth); - #else - ::glClearDepth(action->Depth); - #endif - ::glClearStencil(action->Stencil); - } - if (0 != clearMask) { - ::glClear(clearMask); - } - ORYOL_GL_CHECK_ERROR(); - } - #if !ORYOL_OPENGLES2 - else { - o_assert_dbg(pass); - // GLES3 / GL3 potential MRT - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (pass->colorTextures[i]) { - if (action->Flags & (PassAction::ClearC0<Color[i]); - ::glClearBufferfv(GL_COLOR, i, c); - } - } - } - if (pass->depthStencilTexture && (action->Flags & PassAction::ClearDS)) { - ::glClearBufferfi(GL_DEPTH_STENCIL, 0, action->Depth, action->Stencil); - } - ORYOL_GL_CHECK_ERROR(); - } - #endif -} - -//------------------------------------------------------------------------------ -void -glRenderer::endPass() { - o_assert_dbg(this->valid); - - // perform the MSAA resolve if necessary - #if !ORYOL_OPENGLES2 - if (!glCaps::IsFlavour(glCaps::GLES2)) { - const renderPass* rp = this->curRenderPass; - if (rp) { - const bool isMSAA = 0 != rp->colorTextures[0]->glMSAARenderbuffer; - if (isMSAA) { - ::glBindFramebuffer(GL_READ_FRAMEBUFFER, rp->glFramebuffer); - o_assert_dbg(rp->colorTextures[0]); - const int w = rp->colorTextures[0]->textureAttrs.Width; - const int h = rp->colorTextures[0]->textureAttrs.Height; - for (int attIndex = 0; attIndex < GfxConfig::MaxNumColorAttachments; attIndex++) { - if (rp->colorTextures[attIndex]) { - o_assert_dbg(rp->glMSAAResolveFramebuffers[attIndex]); - ::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, rp->glMSAAResolveFramebuffers[attIndex]); - ::glReadBuffer(GL_COLOR_ATTACHMENT0+attIndex); - const GLenum att = GL_COLOR_ATTACHMENT0; - ::glDrawBuffers(1, &att); - ::glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - break; - } - } - ORYOL_GL_CHECK_ERROR(); - } - } - } - #endif - this->pointers.displayMgr->glBindDefaultFramebuffer(); - ORYOL_GL_CHECK_ERROR(); - this->curRenderPass = nullptr; - this->rpValid = false; -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyDrawState(pipeline* pip, mesh** meshes, int numMeshes) { - o_assert_dbg(this->valid); - o_assert_dbg(pip); - o_assert_dbg(meshes && (numMeshes > 0)); - - // do debug validation before record/playback, simplifies debugging - const PipelineSetup& setup = pip->Setup; - #if ORYOL_DEBUG - o_assert2(setup.BlendState.ColorFormat == this->rpAttrs.ColorPixelFormat, "ColorFormat in BlendState must match current render target!\n"); - o_assert2(setup.BlendState.DepthFormat == this->rpAttrs.DepthPixelFormat, "DepthFormat in BlendState must match current render target!\n"); - o_assert2(setup.RasterizerState.SampleCount == this->rpAttrs.SampleCount, "SampleCount in RasterizerState must match current render target!\n"); - if (this->curRenderPass) { - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - const texture* tex = this->curRenderPass->colorTextures[i]; - if (tex) { - o_assert2(setup.BlendState.ColorFormat == tex->textureAttrs.ColorFormat, "ColorFormat in BlendState must match MRT color attachments!\n"); - o_assert2(setup.RasterizerState.SampleCount == tex->textureAttrs.SampleCount, "SampleCount in RasterizerState must match MRT color attachments!\n"); - } - } - const texture* dsTex = this->curRenderPass->depthStencilTexture; - if (dsTex) { - o_assert2(setup.BlendState.DepthFormat == dsTex->textureAttrs.DepthFormat, "DepthFormat in BlendState must match depth/stencil attachment!\n"); - } - } - #endif - - // if any of the meshes is still loading, cancel the next draw state - for (int i = 0; i < numMeshes; i++) { - if (nullptr == meshes[i]) { - this->curPipeline = nullptr; - return; - } - } - - // draw state is valid, ready for rendering - this->curPipeline = pip; - o_assert_dbg(pip->shd); - - // apply DepthStencilState changes - if (setup.DepthStencilState != this->depthStencilState) { - - const DepthStencilState& curState = this->depthStencilState; - const DepthStencilState& newState = setup.DepthStencilState; - - // apply common depth-stencil state if changed - bool depthStencilChanged = false; - if (curState.Hash != newState.Hash) { - const CompareFunc::Code depthCmpFunc = newState.DepthCmpFunc; - if (depthCmpFunc != curState.DepthCmpFunc) { - o_assert_range_dbg(int(depthCmpFunc), CompareFunc::NumCompareFuncs); - ::glDepthFunc(mapCompareFunc[depthCmpFunc]); - } - const bool depthWriteEnabled = newState.DepthWriteEnabled; - if (depthWriteEnabled != curState.DepthWriteEnabled) { - ::glDepthMask(depthWriteEnabled); - } - const bool stencilEnabled = newState.StencilEnabled; - if (stencilEnabled != curState.StencilEnabled) { - if (stencilEnabled) ::glEnable(GL_STENCIL_TEST); - else ::glDisable(GL_STENCIL_TEST); - } - depthStencilChanged = true; - } - - // apply front and back stencil state - bool frontChanged = false; - const StencilState& newFront = newState.StencilFront; - const StencilState& curFront = curState.StencilFront; - if (curFront.Hash != newFront.Hash) { - frontChanged = true; - this->applyStencilState(newState, curState, GL_FRONT); - } - bool backChanged = false; - const StencilState& newBack = newState.StencilBack; - const StencilState& curBack = curState.StencilBack; - if (curBack.Hash != newBack.Hash) { - backChanged = true; - this->applyStencilState(newState, curState, GL_BACK); - } - - // update state cache - if (depthStencilChanged || frontChanged || backChanged) { - this->depthStencilState = newState; - } - } - if (setup.BlendState != this->blendState) { - - const BlendState& curState = this->blendState; - const BlendState& newState = setup.BlendState; - - if (newState.BlendEnabled != curState.BlendEnabled) { - if (newState.BlendEnabled) ::glEnable(GL_BLEND); - else ::glDisable(GL_BLEND); - } - - if ((newState.SrcFactorRGB != curState.SrcFactorRGB) || - (newState.DstFactorRGB != curState.DstFactorRGB) || - (newState.SrcFactorAlpha != curState.SrcFactorAlpha) || - (newState.DstFactorAlpha != curState.DstFactorAlpha)) { - - o_assert_dbg(newState.SrcFactorRGB < BlendFactor::NumBlendFactors); - o_assert_dbg(newState.DstFactorRGB < BlendFactor::NumBlendFactors); - o_assert_dbg(newState.SrcFactorAlpha < BlendFactor::NumBlendFactors); - o_assert_dbg(newState.DstFactorAlpha < BlendFactor::NumBlendFactors); - - ::glBlendFuncSeparate(mapBlendFactor[newState.SrcFactorRGB], - mapBlendFactor[newState.DstFactorRGB], - mapBlendFactor[newState.SrcFactorAlpha], - mapBlendFactor[newState.DstFactorAlpha]); - } - if ((newState.OpRGB != curState.OpRGB) || - (newState.OpAlpha != curState.OpAlpha)) { - - o_assert_dbg(curState.OpRGB < BlendOperation::NumBlendOperations); - o_assert_dbg(curState.OpAlpha < BlendOperation::NumBlendOperations); - - ::glBlendEquationSeparate(mapBlendOp[newState.OpRGB], mapBlendOp[newState.OpAlpha]); - } - - if (newState.ColorWriteMask != curState.ColorWriteMask) { - ::glColorMask((newState.ColorWriteMask & PixelChannel::R) != 0, - (newState.ColorWriteMask & PixelChannel::G) != 0, - (newState.ColorWriteMask & PixelChannel::B) != 0, - (newState.ColorWriteMask & PixelChannel::A) != 0); - } - - this->blendState = newState; - ORYOL_GL_CHECK_ERROR(); - } - if (setup.BlendColor != this->blendColor) { - this->blendColor = setup.BlendColor; - ::glBlendColor(this->blendColor.x, this->blendColor.y, this->blendColor.z, this->blendColor.w); - } - if (setup.RasterizerState != this->rasterizerState) { - - const RasterizerState& curState = this->rasterizerState; - const RasterizerState& newState = setup.RasterizerState; - - const bool cullFaceEnabled = newState.CullFaceEnabled; - if (cullFaceEnabled != curState.CullFaceEnabled) { - if (cullFaceEnabled) ::glEnable(GL_CULL_FACE); - else ::glDisable(GL_CULL_FACE); - } - const Face::Code cullFace = newState.CullFace; - if (cullFace != curState.CullFace) { - o_assert_range_dbg(cullFace, Face::NumFaceCodes); - ::glCullFace(mapCullFace[cullFace]); - } - const bool scissorTestEnabled = newState.ScissorTestEnabled; - if (scissorTestEnabled != curState.ScissorTestEnabled) { - if (scissorTestEnabled) ::glEnable(GL_SCISSOR_TEST); - else ::glDisable(GL_SCISSOR_TEST); - } - const bool ditherEnabled = newState.DitherEnabled; - if (ditherEnabled != curState.DitherEnabled) { - if (ditherEnabled) ::glEnable(GL_DITHER); - else ::glDisable(GL_DITHER); - } - #if !(ORYOL_OPENGLES2 || ORYOL_OPENGLES3) - const uint16_t sampleCount = newState.SampleCount; - if (sampleCount != curState.SampleCount) { - if (sampleCount > 1) ::glEnable(GL_MULTISAMPLE); - else ::glDisable(GL_MULTISAMPLE); - } - #endif - this->rasterizerState = newState; - ORYOL_GL_CHECK_ERROR(); - } - - // bind program and uniform buffers - this->useProgram(pip->shd->glProgram); - - // need to store primary mesh with primitive group defs for later draw call - this->curPrimaryMesh = meshes[0]; - - // apply meshes - #if !ORYOL_GL_USE_GETATTRIBLOCATION - // this is the default vertex attribute code path for most desktop and mobile platforms - const auto& ib = this->curPrimaryMesh->buffers[mesh::ib]; - this->bindIndexBuffer(ib.glBuffers[ib.activeSlot]); // can be 0 if mesh has no index buffer - for (int attrIndex = 0; attrIndex < VertexAttr::NumVertexAttrs; attrIndex++) { - const auto& attr = pip->glAttrs[attrIndex]; - o_assert_dbg(attr.vbIndex < numMeshes); - auto& curAttr = this->glAttrs[attrIndex]; - const mesh* msh = meshes[attr.vbIndex]; - o_assert_dbg(msh); - const auto& vb = msh->buffers[mesh::vb]; - const GLuint glVB = vb.glBuffers[vb.activeSlot]; - - bool vbChanged = (glVB != this->glAttrVBs[attrIndex]); - bool attrChanged = (attr != curAttr); - if (vbChanged || attrChanged) { - if (attr.enabled) { - this->glAttrVBs[attrIndex] = glVB; - this->bindVertexBuffer(glVB); - ::glVertexAttribPointer(attr.index, attr.size, attr.type, attr.normalized, attr.stride, (const GLvoid*)(GLintptr)attr.offset); - ORYOL_GL_CHECK_ERROR(); - if (!curAttr.enabled) { - ::glEnableVertexAttribArray(attr.index); - ORYOL_GL_CHECK_ERROR(); - } - } - else { - if (curAttr.enabled) { - ::glDisableVertexAttribArray(attr.index); - ORYOL_GL_CHECK_ERROR(); - } - } - if (curAttr.divisor != attr.divisor) { - glCaps::VertexAttribDivisor(attr.index, attr.divisor); - ORYOL_GL_CHECK_ERROR(); - } - curAttr = attr; - } - } - #else - // this uses glGetAttribLocation for platforms which don't support - // glBindAttribLocation (e.g. RaspberryPi) - // FIXME: currently this doesn't use state-caching - const auto& ib = this->curPrimaryMesh->buffers[mesh::ib]; - this->bindIndexBuffer(ib.glBuffers[ib.activeSlot]); // can be 0 - int maxUsedAttrib = 0; - for (int attrIndex = 0; attrIndex < VertexAttr::NumVertexAttrs; attrIndex++) { - const auto& attr = pip->glAttrs[attrIndex]; - const GLint glAttribIndex = pip->shd->getAttribLocation((VertexAttr::Code)attrIndex); - if (glAttribIndex >= 0) { - o_assert_dbg(attr.enabled); - const mesh* msh = meshes[attr.vbIndex]; - const auto& vb = msh->buffers[mesh::vb]; - const GLuint glVB = vb.glBuffers[vb.activeSlot]; - this->bindVertexBuffer(glVB); - ::glVertexAttribPointer(glAttribIndex, attr.size, attr.type, attr.normalized, attr.stride, (const GLvoid*)(GLintptr)attr.offset); - ORYOL_GL_CHECK_ERROR(); - ::glEnableVertexAttribArray(glAttribIndex); - ORYOL_GL_CHECK_ERROR(); - glCaps::VertexAttribDivisor(glAttribIndex, attr.divisor); - ORYOL_GL_CHECK_ERROR(); - maxUsedAttrib++; - } - } - int maxNumAttribs = glCaps::IntLimit(glCaps::MaxVertexAttribs); - if (VertexAttr::NumVertexAttrs < maxNumAttribs) { - maxNumAttribs = VertexAttr::NumVertexAttrs; - } - for (int i = maxUsedAttrib; i < maxNumAttribs; i++) { - ::glDisableVertexAttribArray(i); - ORYOL_GL_CHECK_ERROR(); - } - #endif - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::draw(int baseElementIndex, int numElements, int numInstances) { - o_assert_dbg(this->valid); - o_assert_dbg(numInstances >= 1); - - o_assert2_dbg(this->rpValid, "Not inside BeginPass / EndPass!"); - if (nullptr == this->curPipeline) { - return; - } - ORYOL_GL_CHECK_ERROR(); - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - const IndexType::Code indexType = msh->indexBufferAttrs.Type; - const GLenum glPrimType = this->curPipeline->glPrimType; - if (IndexType::None != indexType) { - // indexed geometry - const int indexByteSize = IndexType::ByteSize(indexType); - const GLvoid* indices = (const GLvoid*) (GLintptr) (baseElementIndex * indexByteSize); - const GLenum glIndexType = glTypes::asGLIndexType(indexType); - if (numInstances == 1) { - ::glDrawElements(glPrimType, numElements, glIndexType, indices); - } - else { - glCaps::DrawElementsInstanced(glPrimType, numElements, glIndexType, indices, numInstances); - } - } - else { - // non-indexed geometry - if (numInstances == 1) { - ::glDrawArrays(glPrimType, baseElementIndex, numElements); - } - else { - glCaps::DrawArraysInstanced(glPrimType, baseElementIndex, numElements, numInstances); - } - } - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::draw(int primGroupIndex, int numInstances) { - o_assert_dbg(this->valid); - o_assert2_dbg(this->rpValid, "Not inside BeginPass / EndPass!"); - if (nullptr == this->curPipeline) { - return; - } - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - if (primGroupIndex >= msh->numPrimGroups) { - // this may happen if trying to render a placeholder which doesn't - // have as many materials as the original mesh, anyway, this isn't - // a serious error - return; - } - const PrimitiveGroup& primGroup = msh->primGroups[primGroupIndex]; - this->draw(primGroup.BaseElement, primGroup.NumElements, numInstances); -} - -//------------------------------------------------------------------------------ -static GLuint -obtainUpdateBuffer(mesh::buffer& buf, int frameIndex) { - // helper function to get the right GL buffer for a vertex- - // or index-buffer update, this is implemented with - // double-buffer to prevent a sync-stall with the GPU - - // restrict buffer updates to once per frame per mesh, this isn't - // strictly required on GL, but we want the same restrictions across all 3D APIs - o_assert2(buf.updateFrameIndex != frameIndex, "Only one data update allowed per buffer and frame!\n"); - buf.updateFrameIndex = frameIndex; - - // rotate slot index to next dynamic vertex buffer - // to implement double/multi-buffering because the previous buffer - // might still be in-flight on the GPU - // NOTE: buf.numSlots can also be 1 if this is a Dynamic buffer (not a Stream buffer) - if (++buf.activeSlot >= buf.numSlots) { - buf.activeSlot = 0; - } - return buf.glBuffers[buf.activeSlot]; -} - -//------------------------------------------------------------------------------ -void -glRenderer::updateVertices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != msh); - o_assert_dbg(nullptr != data); - o_assert_dbg((numBytes > 0) && (numBytes <= msh->vertexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->vertexBufferAttrs.BufferUsage); - - auto& vb = msh->buffers[mesh::vb]; - GLuint glBuffer = obtainUpdateBuffer(vb, (int)this->frameIndex); - o_assert_dbg(0 != glBuffer); - this->bindVertexBuffer(glBuffer); - ::glBufferSubData(GL_ARRAY_BUFFER, 0, numBytes, data); - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::updateIndices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != msh); - o_assert_dbg(nullptr != data); - - o_assert_dbg(IndexType::None != msh->indexBufferAttrs.Type); - o_assert_dbg((numBytes > 0) && (numBytes <= msh->indexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->indexBufferAttrs.BufferUsage); - - auto& ib = msh->buffers[mesh::ib]; - GLuint glBuffer = obtainUpdateBuffer(ib, (int)this->frameIndex); - o_assert_dbg(0 != glBuffer); - this->bindIndexBuffer(glBuffer); - ::glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, numBytes, data); - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -static GLuint -obtainUpdateTexture(texture* tex, int frameIndex) { - // same as obtainUpdateBuffer, but for texture - o_assert2(tex->updateFrameIndex != frameIndex, "Only one data update allowed per texture and frame!\n"); - tex->updateFrameIndex = frameIndex; - o_assert_dbg(tex->numSlots > 1); - if (++tex->activeSlot >= tex->numSlots) { - tex->activeSlot = 0; - } - return tex->glTextures[tex->activeSlot]; -} - -//------------------------------------------------------------------------------ -void -glRenderer::updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != tex); - o_assert_dbg(nullptr != data); - ORYOL_GL_CHECK_ERROR(); - - // only accept 2D textures for now - const TextureAttrs& attrs = tex->textureAttrs; - o_assert_dbg(TextureType::Texture2D == attrs.Type); - o_assert_dbg(Usage::Immutable != attrs.TextureUsage); - o_assert_dbg(!PixelFormat::IsCompressedFormat(attrs.ColorFormat)); - o_assert_dbg(offsetsAndSizes.NumMipMaps <= attrs.NumMipMaps); - o_assert_dbg(offsetsAndSizes.NumFaces == 1); - - GLuint glTex = obtainUpdateTexture(tex, int(this->frameIndex)); - this->bindTexture(0, tex->glTarget, glTex); - uint8_t* srcPtr = (uint8_t*)data; - GLenum glTexImageFormat = glTypes::asGLTexImageFormat(attrs.ColorFormat); - GLenum glTexImageType = glTypes::asGLTexImageType(attrs.ColorFormat); - for (int mipIndex = 0; mipIndex < offsetsAndSizes.NumMipMaps; mipIndex++) { - o_assert_dbg(offsetsAndSizes.Sizes[0][mipIndex] > 0); - int mipWidth = attrs.Width >> mipIndex; - if (mipWidth == 0) mipWidth = 1; - int mipHeight = attrs.Height >> mipIndex; - if (mipHeight == 0) mipHeight = 1; - ::glTexSubImage2D(tex->glTarget, // target - mipIndex, // level - 0, // xoffset - 0, // yoffset - mipWidth, // width - mipHeight, // height - glTexImageFormat, // format - glTexImageType, // type - srcPtr + offsetsAndSizes.Offsets[0][mipIndex]); - ORYOL_GL_CHECK_ERROR(); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::invalidateMeshState() { - o_assert_dbg(this->valid); - - ::glBindBuffer(GL_ARRAY_BUFFER, 0); - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - this->vertexBuffer = 0; - this->indexBuffer = 0; - for (int i = 0; i < VertexAttr::NumVertexAttrs; i++) { - this->glAttrs[i] = pipeline::vertexAttr(); - this->glAttrVBs[i] = 0; - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::bindVertexBuffer(GLuint vb) { - o_assert_dbg(this->valid); - - if (vb != this->vertexBuffer) { - this->vertexBuffer = vb; - ::glBindBuffer(GL_ARRAY_BUFFER, vb); - ORYOL_GL_CHECK_ERROR(); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::bindIndexBuffer(GLuint ib) { - o_assert_dbg(this->valid); - - if (ib != this->indexBuffer) { - this->indexBuffer = ib; - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib); - ORYOL_GL_CHECK_ERROR(); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::invalidateShaderState() { - o_assert_dbg(this->valid); - - ORYOL_GL_CHECK_ERROR(); - ::glUseProgram(0); - ORYOL_GL_CHECK_ERROR(); - this->program = 0; -} - -//------------------------------------------------------------------------------ -void -glRenderer::useProgram(GLuint prog) { - o_assert_dbg(this->valid); - if (prog != this->program) { - this->program = prog; - ::glUseProgram(prog); - ORYOL_GL_CHECK_ERROR(); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::invalidateTextureState() { - o_assert_dbg(this->valid); - for (int i = 0; i < MaxTextureSamplers; i++) { - this->samplers[i] = 0; - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::bindTexture(int samplerIndex, GLenum target, GLuint tex) { - o_assert_dbg(this->valid); - o_assert_range_dbg(samplerIndex, MaxTextureSamplers); - #if ORYOL_OPENGLES2 - o_assert_dbg((target == GL_TEXTURE_2D) || (target == GL_TEXTURE_CUBE_MAP)); - #else - o_assert_dbg((target == GL_TEXTURE_2D) || (target == GL_TEXTURE_CUBE_MAP) || - (target == GL_TEXTURE_3D) || (target == GL_TEXTURE_2D_ARRAY)); - #endif - - if (tex != this->samplers[samplerIndex]) { - this->samplers[samplerIndex] = tex; - ::glActiveTexture(GL_TEXTURE0 + samplerIndex); - ORYOL_GL_CHECK_ERROR(); - ::glBindTexture(target, tex); - ORYOL_GL_CHECK_ERROR(); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::setupDepthStencilState() { - o_assert_dbg(this->valid); - - this->depthStencilState = DepthStencilState(); - - ::glEnable(GL_DEPTH_TEST); - ::glDepthFunc(GL_ALWAYS); - ::glDepthMask(GL_FALSE); - ::glDisable(GL_STENCIL_TEST); - ::glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF); - ::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - ::glStencilMask(0xFFFFFFFF); - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyStencilState(const DepthStencilState& newState, const DepthStencilState& curState, GLenum glFace) { - o_assert_dbg(this->valid); - - const StencilState& newStencilState = (glFace == GL_FRONT) ? newState.StencilFront : newState.StencilBack; - const StencilState& curStencilState = (glFace == GL_FRONT) ? curState.StencilFront : curState.StencilBack; - - const CompareFunc::Code cmpFunc = newStencilState.CmpFunc; - const uint32_t readMask = newState.StencilReadMask; - const int stencilRef = newState.StencilRef; - if ((cmpFunc != curStencilState.CmpFunc) || (readMask != curState.StencilReadMask) || (stencilRef != curState.StencilRef)) { - o_assert_range_dbg(int(cmpFunc), CompareFunc::NumCompareFuncs); - ::glStencilFuncSeparate(glFace, mapCompareFunc[cmpFunc], stencilRef, readMask); - } - - const StencilOp::Code sFailOp = newStencilState.FailOp; - const StencilOp::Code dFailOp = newStencilState.DepthFailOp; - const StencilOp::Code passOp = newStencilState.PassOp; - if ((sFailOp != curStencilState.FailOp) || (dFailOp != curStencilState.DepthFailOp) || (passOp != curStencilState.PassOp)) { - o_assert_range_dbg(int(sFailOp), StencilOp::NumStencilOperations); - o_assert_range_dbg(int(dFailOp), StencilOp::NumStencilOperations); - o_assert_range_dbg(int(passOp), StencilOp::NumStencilOperations); - ::glStencilOpSeparate(glFace, mapStencilOp[sFailOp], mapStencilOp[dFailOp], mapStencilOp[passOp]); - } - - const uint32_t writeMask = newState.StencilWriteMask; - if (writeMask != curState.StencilWriteMask) { - ::glStencilMaskSeparate(glFace, writeMask); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::setupBlendState() { - o_assert_dbg(this->valid); - - this->blendState = BlendState(); - ::glDisable(GL_BLEND); - ::glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); - ::glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - ::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - this->blendColor = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); - ::glBlendColor(1.0f, 1.0f, 1.0f, 1.0f); - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::setupRasterizerState() { - o_assert_dbg(this->valid); - - this->rasterizerState = RasterizerState(); - - ::glDisable(GL_CULL_FACE); - ::glFrontFace(GL_CW); - ::glCullFace(GL_BACK); - ::glDisable(GL_POLYGON_OFFSET_FILL); - ::glDisable(GL_SCISSOR_TEST); - ::glEnable(GL_DITHER); - #if !(ORYOL_OPENGLES2 || ORYOL_OPENGLES3) - ::glEnable(GL_MULTISAMPLE); - #endif - ORYOL_GL_CHECK_ERROR(); -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t typeHash, const uint8_t* ptr, int byteSize) { - o_assert_dbg(this->valid); - // bytesize must be a multiple of sizeof(vec4) - o_assert_dbg((byteSize & 15) == 0); - if (!this->curPipeline) { - // currently no valid draw state set - return; - } - - // get the uniform layout object for this uniform block - const shader* shd = this->curPipeline->shd; - o_assert_dbg(shd); - - #if ORYOL_DEBUG - // check whether the provided struct is type-compatible with the - // expected uniform-block-layout, the size-check shouldn't be necessary - // since the hash should already bail out, but it doesn't hurt either - int ubIndex = shd->Setup.UniformBlockIndexByStageAndSlot(bindStage, bindSlot); - o_assert(InvalidIndex != ubIndex); - const uint32_t ubTypeHash = shd->Setup.UniformBlockTypeHash(ubIndex); - const int ubByteSize = shd->Setup.UniformBlockByteSize(ubIndex); - o_assert(ubTypeHash == typeHash); - o_assert(ubByteSize >= byteSize); - #endif - - GLint glLoc = shd->getUniformBlockLocation(bindStage, bindSlot); - if (-1 != glLoc) { - int vec4Count = byteSize / 16; - ::glUniform4fv(glLoc, vec4Count, (const GLfloat*)ptr); - } -} - -//------------------------------------------------------------------------------ -void -glRenderer::applyTextures(ShaderStage::Code bindStage, Oryol::_priv::texture **textures, int numTextures) { - o_assert_dbg(this->valid); - o_assert_dbg(((ShaderStage::VS == bindStage) && (numTextures <= GfxConfig::MaxNumVertexTextures)) || - ((ShaderStage::FS == bindStage) && (numTextures <= GfxConfig::MaxNumFragmentTextures))); - if (nullptr == this->curPipeline) { - return; - } - - // if any of the provided texture pointers are not valid, this means - // that a texture hasn't been loaded yet (or has failed loading), in this - // case, disable rendering for next draw call - for (int i = 0; i < numTextures; i++) { - if (nullptr == textures[i]) { - this->curPipeline = nullptr; - return; - } - } - - // apply textures and samplers - const shader* shd = this->curPipeline->shd; - o_assert_dbg(shd); - for (int i = 0; i < numTextures; i++) { - const texture* tex = textures[i]; - const int samplerIndex = shd->getSamplerIndex(bindStage, i); - if (-1 != samplerIndex) { - this->bindTexture(samplerIndex, tex->glTarget, tex->glTextures[tex->activeSlot]); - } - } -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glRenderer.h b/code/Modules/Gfx/private/gl/glRenderer.h deleted file mode 100644 index 5dd7e166f..000000000 --- a/code/Modules/Gfx/private/gl/glRenderer.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::glRenderer - @brief OpenGL wrapper and state cache -*/ -#include "Core/Types.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/gfxPointers.h" -#include "Gfx/private/gl/gl_decl.h" -#include "glm/vec4.hpp" -#include "Gfx/private/resource.h" - -namespace Oryol { -namespace _priv { - -class glRenderer { -public: - /// constructor - glRenderer(); - /// destructor - ~glRenderer(); - - /// setup the renderer - void setup(const GfxSetup& setup, const gfxPointers& ptrs); - /// discard the renderer - void discard(); - /// return true if renderer has been setup - bool isValid() const; - - /// reset GL state cache - void resetStateCache(); - /// test if a feature is supported - bool queryFeature(GfxFeature::Code feat) const; - /// commit current frame - void commitFrame(); - /// get the current render pass attributes - const DisplayAttrs& renderPassAttrs() const; - - /// begin rendering pass (pass can be nullptr for default framebuffer) - void beginPass(renderPass* pass, const PassAction* action); - /// end current rendering pass - void endPass(); - - /// apply viewport - void applyViewPort(int x, int y, int width, int height, bool originTopLeft); - /// apply scissor rect - void applyScissorRect(int x, int y, int width, int height, bool originTopLeft); - /// apply draw state - void applyDrawState(pipeline* pip, mesh** meshes, int numMeshes); - /// apply a shader uniform block (called after applyDrawState) - void applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t typeHash, const uint8_t* ptr, int byteSize); - /// apply a group of textures - void applyTextures(ShaderStage::Code bindStage, texture** textures, int numTextures); - - /// submit a draw call with primitive group index in current mesh - void draw(int primGroupIndex, int numInstances); - /// submit a draw call with element range - void draw(int baseElementIndex, int numElements, int numInstances); - - /// update vertex data - void updateVertices(mesh* msh, const void* data, int numBytes); - /// update index data - void updateIndices(mesh* msh, const void* data, int numBytes); - /// update texture pixel data - void updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes); - - /// invalidate bound mesh state - void invalidateMeshState(); - /// bind vertex buffer with state caching - void bindVertexBuffer(GLuint vb); - /// bind index buffer with state caching - void bindIndexBuffer(GLuint ib); - - /// invalidate shader state - void invalidateShaderState(); - /// invoke glUseProgram (if changed) - void useProgram(GLuint prog); - - /// invalidate texture state - void invalidateTextureState(); - /// bind a texture to a sampler index - void bindTexture(int samplerIndex, GLenum target, GLuint tex); - - /// setup the initial depth-stencil-state - void setupDepthStencilState(); - /// setup the initial blend-state - void setupBlendState(); - /// setup rasterizer state - void setupRasterizerState(); - /// apply front/back side stencil state - void applyStencilState(const DepthStencilState& state, const DepthStencilState& curState, GLenum glFace); - - bool valid = false; - gfxPointers pointers; - #if !ORYOL_OPENGLES2 - GLuint globalVAO = 0; - #endif - uint64_t frameIndex = 0; - - static GLenum mapCompareFunc[CompareFunc::NumCompareFuncs]; - static GLenum mapStencilOp[StencilOp::NumStencilOperations]; - static GLenum mapBlendFactor[BlendFactor::NumBlendFactors]; - static GLenum mapBlendOp[BlendOperation::NumBlendOperations]; - static GLenum mapCullFace[Face::NumFaceCodes]; - - bool rpValid = false; - DisplayAttrs rpAttrs; - GfxSetup gfxSetup; - - // high-level state cache - renderPass* curRenderPass = nullptr; - pipeline* curPipeline = nullptr; - mesh* curPrimaryMesh = nullptr; - - // GL state cache - BlendState blendState; - DepthStencilState depthStencilState; - RasterizerState rasterizerState; - - GLint scissorX = 0; - GLint scissorY = 0; - GLsizei scissorWidth = 0; - GLsizei scissorHeight = 0; - GLint viewPortX = 0; - GLint viewPortY = 0; - GLsizei viewPortWidth = 0; - GLsizei viewPortHeight = 0; - - glm::vec4 blendColor; - - GLuint vertexBuffer = 0; - GLuint indexBuffer = 0; - GLuint program = 0; - - static const int MaxTextureSamplers = 16; - StaticArray samplers; - StaticArray glAttrs; - StaticArray glAttrVBs; -}; - -//------------------------------------------------------------------------------ -inline const DisplayAttrs& -glRenderer::renderPassAttrs() const { - return this->rpAttrs; -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glResource.cc b/code/Modules/Gfx/private/gl/glResource.cc deleted file mode 100644 index e9bff06e4..000000000 --- a/code/Modules/Gfx/private/gl/glResource.cc +++ /dev/null @@ -1,130 +0,0 @@ -//------------------------------------------------------------------------------ -// glResource.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "glResource.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -glMesh::~glMesh() { - #if ORYOL_DEBUG - for (const auto& buf : this->buffers) { - for (int i = 0; i < MaxNumSlots; i++) { - o_assert_dbg(0 == buf.glBuffers[i]); - } - } - #endif -} - -//------------------------------------------------------------------------------ -void -glMesh::Clear() { - for (auto& buf : this->buffers) { - buf = buffer(); - } - meshBase::Clear(); -} - -//------------------------------------------------------------------------------ -void -glPipeline::Clear() { - this->glAttrs.Fill(glPipeline::vertexAttr()); - this->glPrimType = 0; - pipelineBase::Clear(); -} - -//------------------------------------------------------------------------------ -glShader::glShader() { - this->Clear(); -} - -//------------------------------------------------------------------------------ -glShader::~glShader() { - o_assert_dbg(0 == this->glProgram); -} - -//------------------------------------------------------------------------------ -void -glShader::Clear() { - this->glProgram = 0; - this->samplerMappings.Fill(InvalidIndex); - #if ORYOL_GL_USE_GETATTRIBLOCATION - this->attribMapping.Fill(-1); - #endif - this->uniformBlockMappings.Fill(0); - shaderBase::Clear(); -} - -//------------------------------------------------------------------------------ -void -glShader::bindUniformBlock(ShaderStage::Code bindStage, int bindSlot, GLint glUniformLocation) { - this->uniformBlockMappings[uniformBlockArrayIndex(bindStage, bindSlot)] = glUniformLocation; -} - -//------------------------------------------------------------------------------ -void -glShader::bindSampler(ShaderStage::Code bindStage, int bindSlot, int textureIndex) { - this->samplerMappings[samplerArrayIndex(bindStage, bindSlot)] = textureIndex; -} - -//------------------------------------------------------------------------------ -#if ORYOL_GL_USE_GETATTRIBLOCATION -void -glShader::bindAttribLocation(VertexAttr::Code attr, GLint location) { - this->attribMapping[attr] = location; -} -#endif - -//------------------------------------------------------------------------------ -glTexture::glTexture() { - this->glTextures.Fill(0); -} - -//------------------------------------------------------------------------------ -glTexture::~glTexture() { - o_assert_dbg(0 == this->glTarget); - o_assert_dbg(0 == this->glDepthRenderbuffer); - o_assert_dbg(0 == this->glMSAARenderbuffer); - #if ORYOL_DEBUG - for (const auto& glTex : this->glTextures) { - o_assert_dbg(0 == glTex); - } - #endif -} - -//------------------------------------------------------------------------------ -void -glTexture::Clear() { - textureBase::Clear(); - this->glTarget = 0; - this->glDepthRenderbuffer = 0; - this->glMSAARenderbuffer = 0; - this->updateFrameIndex = -1; - this->numSlots = 1; - this->activeSlot = 0; - this->glTextures.Fill(0); -} - -//------------------------------------------------------------------------------ -glRenderPass::glRenderPass() { - this->glMSAAResolveFramebuffers.Fill(0); -} - -//------------------------------------------------------------------------------ -glRenderPass::~glRenderPass() { - o_assert_dbg(0 == this->glFramebuffer); -} - -//------------------------------------------------------------------------------ -void -glRenderPass::Clear() { - this->glFramebuffer = 0; - this->glMSAAResolveFramebuffers.Fill(0); - renderPassBase::Clear(); -} - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/gl/glResource.h b/code/Modules/Gfx/private/gl/glResource.h deleted file mode 100644 index 79f9c1e54..000000000 --- a/code/Modules/Gfx/private/gl/glResource.h +++ /dev/null @@ -1,215 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -#include "Gfx/private/resourceBase.h" -#include "Core/Containers/StaticArray.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/gl/gl_decl.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glMesh - @ingroup _priv - @brief GL implementation of mesh -*/ -class glMesh : public meshBase { -public: - /// destructor - ~glMesh(); - - /// clear the object (called from meshFactory::DestroyResource()) - void Clear(); - - static const int MaxNumSlots = 2; - struct buffer { - buffer() : updateFrameIndex(-1), numSlots(1), activeSlot(0) { - this->glBuffers.Fill(0); - } - int updateFrameIndex; - uint8_t numSlots; - uint8_t activeSlot; - StaticArray glBuffers; - }; - static const int vb = 0; - static const int ib = 1; - StaticArray buffers; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glPipeline - @ingroup _priv - @brief GL implementation of pipeline -*/ -class glPipeline : public pipelineBase { -public: - /// clear the object (called from pipelineFactory::DestroyResource() - void Clear(); - - struct vertexAttr { - /// test for equality - bool operator==(const vertexAttr& rhs) const { - return (this->index == rhs.index) && - (this->enabled == rhs.enabled) && - (this->vbIndex == rhs.vbIndex) && - (this->divisor == rhs.divisor) && - (this->stride == rhs.stride) && - (this->size == rhs.size) && - (this->normalized == rhs.normalized) && - (this->offset == rhs.offset) && - (this->type == rhs.type); - }; - /// test for inequality - bool operator!=(const vertexAttr& rhs) const { - return !operator==(rhs); - }; - - uint8_t index = 0; - uint8_t enabled = 0; - uint8_t vbIndex = 0; - uint8_t divisor = 0; - uint8_t stride = 0; - uint8_t size = 0; - uint8_t normalized = 0; - uint32_t offset = 0; - GLenum type = 0; - }; - StaticArray glAttrs; - GLenum glPrimType = 0; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glShader - @ingroup _priv - @brief GL implementation of shader -*/ -class glShader : public shaderBase { -public: - /// constructor - glShader(); - /// destructor - ~glShader(); - - /// clear the object - void Clear(); - - /// bind a uniform location to a slot index - void bindUniformBlock(ShaderStage::Code bindStage, int bindSlot, GLint glUniformLocation); - /// bind a sampler uniform location to a slot index - void bindSampler(ShaderStage::Code bindStage, int textureIndex, int samplerIndex); - #if ORYOL_GL_USE_GETATTRIBLOCATION - /// bind a vertex attribute location - void bindAttribLocation(VertexAttr::Code attrib, GLint attribLocation); - /// get a vertex attribute location - GLint getAttribLocation(VertexAttr::Code attrib) const; - #endif - - /// compute uniform block index - static int uniformBlockArrayIndex(ShaderStage::Code bindStage, int bindSlot); - /// get location of GL uniform block vec4 array - GLint getUniformBlockLocation(ShaderStage::Code bindStage, int bindSlot) const; - /// get the data size of an uniform block - GLint getUniformBlockSize(ShaderStage::Code bindStage, int bindSlot) const; - /// get sampler index (InvalidIndex if not exists) - int getSamplerIndex(ShaderStage::Code bindStage, int bindSlot) const; - /// compute sampler array index - static int samplerArrayIndex(ShaderStage::Code bindStage, int bindSlot); - - /// the GL shader program - GLuint glProgram = 0; - - static const int MaxTextures = GfxConfig::MaxNumVertexTextures+GfxConfig::MaxNumFragmentTextures; - static const int MaxUBsPerStage = GfxConfig::MaxNumUniformBlocksPerStage; - static const int MaxStages = ShaderStage::NumShaderStages; - - StaticArray uniformBlockMappings; - StaticArray samplerMappings; - #if ORYOL_GL_USE_GETATTRIBLOCATION - StaticArray attribMapping; - #endif -}; - -//------------------------------------------------------------------------------ -inline int -glShader::uniformBlockArrayIndex(ShaderStage::Code bindStage, int bindSlot) { - return bindSlot + bindStage*MaxUBsPerStage; -} - -//------------------------------------------------------------------------------ -inline GLint -glShader::getUniformBlockLocation(ShaderStage::Code bindStage, int bindSlot) const { - return this->uniformBlockMappings[uniformBlockArrayIndex(bindStage, bindSlot)]; -} - -//------------------------------------------------------------------------------ -inline int -glShader::samplerArrayIndex(ShaderStage::Code bindStage, int bindSlot) { - return bindSlot + (bindStage==ShaderStage::FS ? GfxConfig::MaxNumVertexTextures:0); -} - -//------------------------------------------------------------------------------ -inline int -glShader::getSamplerIndex(ShaderStage::Code bindStage, int bindSlot) const { - return this->samplerMappings[samplerArrayIndex(bindStage, bindSlot)]; -} - -//------------------------------------------------------------------------------ -#if ORYOL_GL_USE_GETATTRIBLOCATION -inline GLint -glShader::getAttribLocation(VertexAttr::Code attrib) const { - return this->attribMapping[attrib]; -} -#endif - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glTexture - @ingroup _priv - @brief GL implementation of texture class -*/ -class glTexture : public textureBase { -public: - /// constructor - glTexture(); - /// destructor - ~glTexture(); - - /// clear the object - void Clear(); - - GLenum glTarget = 0; - GLuint glDepthRenderbuffer = 0; - GLuint glMSAARenderbuffer = 0; - - static const int MaxNumSlots = 2; - int updateFrameIndex = -1; - uint8_t numSlots = 1; - uint8_t activeSlot = 0; - StaticArray glTextures; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glRenderPass - @ingroup _priv - @brief GL implementation of renderPass -*/ -class glRenderPass : public renderPassBase { -public: - /// constructor - glRenderPass(); - /// destructor - ~glRenderPass(); - - /// clear the object - void Clear(); - - GLuint glFramebuffer = 0; - StaticArray glMSAAResolveFramebuffers; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glTypes.cc b/code/Modules/Gfx/private/gl/glTypes.cc deleted file mode 100644 index 8e18ac7df..000000000 --- a/code/Modules/Gfx/private/gl/glTypes.cc +++ /dev/null @@ -1,327 +0,0 @@ -//------------------------------------------------------------------------------ -// glTypes.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "gl_impl.h" -#include "Core/Assertion.h" -#include "glTypes.h" -#include "glCaps.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTexImageFormat(PixelFormat::Code c) { - switch (c) { - case PixelFormat::RGBA8: - case PixelFormat::R5G5B5A1: - case PixelFormat::RGBA4: - case PixelFormat::RGBA32F: - case PixelFormat::RGBA16F: - case PixelFormat::R10G10B10A2: - return GL_RGBA; - - case PixelFormat::RGB8: - case PixelFormat::R5G6B5: - return GL_RGB; - - case PixelFormat::L8: - case PixelFormat::R32F: - case PixelFormat::R16F: - if (glCaps::IsFlavour(glCaps::GLES2)) { - return GL_LUMINANCE; - } - else - { - return GL_RED; - } - - case PixelFormat::DEPTH: - return GL_DEPTH_COMPONENT; - - case PixelFormat::DEPTHSTENCIL: - return GL_DEPTH_STENCIL; - - case PixelFormat::DXT1: - return 0x83F1; // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - case PixelFormat::DXT3: - return 0x83F2; // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - case PixelFormat::DXT5: - return 0x83F3; // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - case PixelFormat::PVRTC2_RGB: - return 0x8C01; // GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG - case PixelFormat::PVRTC4_RGB: - return 0x8C00; // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG - case PixelFormat::PVRTC2_RGBA: - return 0x8C03; // GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG - case PixelFormat::PVRTC4_RGBA: - return 0x8C02; // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG - case PixelFormat::ETC2_RGB8: - return 0x9274; // GL_COMPRESSED_RGB8_ETC2 - case PixelFormat::ETC2_SRGB8: - return 0x9275; // GL_COMPRESSED_SRGB8_ETC2 - - default: - o_error("glTypes::asGLTexImageFormat(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTexImageInternalFormat(PixelFormat::Code c) { - #if ORYOL_OPENGLES2 - return glTypes::asGLTexImageFormat(c); - #else - if (glCaps::IsFlavour(glCaps::GLES2)) { - return glTypes::asGLTexImageFormat(c); - } - else - { - switch (c) { - case PixelFormat::RGBA8: - return GL_RGBA8; - case PixelFormat::R5G5B5A1: - return GL_RGB5_A1; - case PixelFormat::RGBA4: - return GL_RGBA4; - case PixelFormat::R10G10B10A2: - return GL_RGB10_A2; - case PixelFormat::RGBA32F: - return GL_RGBA32F; - case PixelFormat::RGBA16F: - return GL_RGBA16F; - case PixelFormat::R32F: - return GL_R32F; - case PixelFormat::R16F: - return GL_R16F; - case PixelFormat::RGB8: - return GL_RGB8; - case PixelFormat::L8: - return GL_R8; - case PixelFormat::DEPTH: - return GL_DEPTH_COMPONENT16; - case PixelFormat::DEPTHSTENCIL: - return GL_DEPTH24_STENCIL8; - case PixelFormat::R5G6B5: - #if ORYOL_OPENGLES3 - return GL_RGB565; - #else - return GL_RGB5; - #endif - case PixelFormat::DXT1: - return 0x83F1; // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - case PixelFormat::DXT3: - return 0x83F2; // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - case PixelFormat::DXT5: - return 0x83F3; // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - case PixelFormat::PVRTC2_RGB: - return 0x8C01; // GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG - case PixelFormat::PVRTC4_RGB: - return 0x8C00; // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG - case PixelFormat::PVRTC2_RGBA: - return 0x8C03; // GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG - case PixelFormat::PVRTC4_RGBA: - return 0x8C02; // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG - case PixelFormat::ETC2_RGB8: - return 0x9274; // GL_COMPRESSED_RGB8_ETC2 - case PixelFormat::ETC2_SRGB8: - return 0x9275; // GL_COMPRESSED_SRGB8_ETC2 - - default: - o_error("glTypes::asGLTexImageFormat(): invalid param!\n"); - return 0; - } - } - #endif -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTexImageType(PixelFormat::Code c) { - switch (c) { - case PixelFormat::RGBA32F: - case PixelFormat::R32F: - return GL_FLOAT; - - case PixelFormat::RGBA16F: - case PixelFormat::R16F: - return GL_HALF_FLOAT; - - case PixelFormat::RGBA8: - case PixelFormat::RGB8: - case PixelFormat::L8: - return GL_UNSIGNED_BYTE; - - #if !ORYOL_OPENGLES2 - case PixelFormat::R10G10B10A2: - return GL_UNSIGNED_INT_2_10_10_10_REV; - #endif - - case PixelFormat::R5G5B5A1: - return GL_UNSIGNED_SHORT_5_5_5_1; - - case PixelFormat::R5G6B5: - return GL_UNSIGNED_SHORT_5_6_5; - - case PixelFormat::RGBA4: - return GL_UNSIGNED_SHORT_4_4_4_4; - - case PixelFormat::DEPTH: - return GL_UNSIGNED_SHORT; - - case PixelFormat::DEPTHSTENCIL: - return GL_UNSIGNED_INT_24_8; - - default: - o_error("glTypes::asGLTexImageType(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLDepthAttachmentFormat(PixelFormat::Code c) { - o_assert_dbg(PixelFormat::IsValidTextureDepthFormat(c)); - switch (c) { - case PixelFormat::DEPTH: - return GL_DEPTH_COMPONENT16; - case PixelFormat::DEPTHSTENCIL: - return GL_DEPTH24_STENCIL8; - default: - o_error("glTypes::asGLRenderbufferFormat(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLIndexType(IndexType::Code c) { - switch (c) { - case IndexType::Index16: return GL_UNSIGNED_SHORT; - case IndexType::Index32: return GL_UNSIGNED_INT; - default: - o_error("glTypes::asGLIndexType(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLPrimitiveType(PrimitiveType::Code c) { - switch (c) { - case PrimitiveType::Points: return GL_POINTS; - case PrimitiveType::Lines: return GL_LINES; - case PrimitiveType::LineStrip: return GL_LINE_STRIP; - case PrimitiveType::Triangles: return GL_TRIANGLES; - case PrimitiveType::TriangleStrip: return GL_TRIANGLE_STRIP; - default: - o_error("glTypes::asGLPrimitiveType(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLPrimitiveMode(PrimitiveType::Code c) { - switch (c) { - case PrimitiveType::Points: - return GL_POINTS; - case PrimitiveType::Lines: - case PrimitiveType::LineStrip: - return GL_LINES; - case PrimitiveType::Triangles: - case PrimitiveType::TriangleStrip: - return GL_TRIANGLES; - default: - o_error("glTypes::asGLPrimitiveMode(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLShaderStage(ShaderStage::Code c) { - switch (c) { - case ShaderStage::VS: return GL_VERTEX_SHADER; - case ShaderStage::FS: return GL_FRAGMENT_SHADER; - default: - o_error("glTypes::asGLShaderType(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTexFilterMode(TextureFilterMode::Code c) { - switch (c) { - case TextureFilterMode::Nearest: return GL_NEAREST; - case TextureFilterMode::Linear: return GL_LINEAR; - case TextureFilterMode::NearestMipmapNearest: return GL_NEAREST_MIPMAP_NEAREST; - case TextureFilterMode::NearestMipmapLinear: return GL_NEAREST_MIPMAP_LINEAR; - case TextureFilterMode::LinearMipmapNearest: return GL_LINEAR_MIPMAP_NEAREST; - case TextureFilterMode::LinearMipmapLinear: return GL_LINEAR_MIPMAP_LINEAR; - default: - o_error("glTypes::asGLTexFilterMode(): invalid param!\n"); - return 0; - }; -}; - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTexWrapMode(TextureWrapMode::Code c) { - switch (c) { - case TextureWrapMode::ClampToEdge: return GL_CLAMP_TO_EDGE; - case TextureWrapMode::Repeat: return GL_REPEAT; - case TextureWrapMode::MirroredRepeat: return GL_MIRRORED_REPEAT; - default: - o_error("glTypes::asGLTexWrapMode(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLTextureTarget(TextureType::Code c) { - switch (c) { - case TextureType::Texture2D: return GL_TEXTURE_2D; - case TextureType::TextureCube: return GL_TEXTURE_CUBE_MAP; - #if !ORYOL_OPENGLES2 - case TextureType::Texture3D: return GL_TEXTURE_3D; - case TextureType::TextureArray: return GL_TEXTURE_2D_ARRAY; - #endif - default: - o_error("glTypes::asGLTextureTarget(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLBufferUsage(Usage::Code c) { - switch (c) { - case Usage::Immutable: return GL_STATIC_DRAW; - case Usage::Dynamic: return GL_DYNAMIC_DRAW; - case Usage::Stream: return GL_STREAM_DRAW; - default: - o_error("glTypes::asGLBufferUsage(): invalid param!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -GLenum -glTypes::asGLCubeFaceTarget(int faceIndex) { - switch (faceIndex) { - case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; - case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - default: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; - } -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/glTypes.h b/code/Modules/Gfx/private/gl/glTypes.h deleted file mode 100644 index 7d1f98dfd..000000000 --- a/code/Modules/Gfx/private/gl/glTypes.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::glTypes - @ingroup _priv - @brief OpenGL type conversion helpers -*/ -#include "Core/Types.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/private/gl/gl_decl.h" - -namespace Oryol { -namespace _priv { - -class glTypes { -public: - /// convert Oryol pixel format to glTexImage format - static GLenum asGLTexImageFormat(PixelFormat::Code c); - /// convert Oryol pixel format to glTexImage format, this is == TexImageFormat on GLES but may be different on desktop GL - static GLenum asGLTexImageInternalFormat(PixelFormat::Code c); - /// convert Oryol pixel format to glTexImage type - static GLenum asGLTexImageType(PixelFormat::Code c); - /// convert Oryol pixel format to depth attachment format - static GLenum asGLDepthAttachmentFormat(PixelFormat::Code c); - /// convert Oryol index type to GL index type - static GLenum asGLIndexType(IndexType::Code c); - /// convert Oryol primitive type to GL primitive type - static GLenum asGLPrimitiveType(PrimitiveType::Code c); - /// convert Oryol primitive type to GL primitive mode (points, lines, triangles) - static GLenum asGLPrimitiveMode(PrimitiveType::Code c); - /// convert Oryol shader type to GL shader type - static GLenum asGLShaderStage(ShaderStage::Code c); - /// convert Oryol texture filter to GL texture filter - static GLenum asGLTexFilterMode(TextureFilterMode::Code c); - /// convert Oryol texture wrap mode to GL texture wrap mode - static GLenum asGLTexWrapMode(TextureWrapMode::Code c); - /// convert Oryol texture type to GL texture target - static GLenum asGLTextureTarget(TextureType::Code c); - /// convert Oryol usage to GL buffer usage - static GLenum asGLBufferUsage(Usage::Code c); - /// convert cubemap face index to texture target - static GLenum asGLCubeFaceTarget(int faceIndex); -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/gl/gl_decl.h b/code/Modules/Gfx/private/gl/gl_decl.h deleted file mode 100644 index 5c9042af0..000000000 --- a/code/Modules/Gfx/private/gl/gl_decl.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - Minimal GL declaration file. Include this in headers for fast compilation - and move actual GL code out of headers. -*/ - -#ifndef GL_VERSION_1_1 -typedef unsigned int GLenum; -typedef unsigned int GLbitfield; -typedef unsigned int GLuint; -typedef int GLint; -typedef int GLsizei; -typedef unsigned char GLboolean; -typedef signed char GLbyte; -typedef short GLshort; -typedef unsigned char GLubyte; -typedef unsigned short GLushort; -typedef unsigned long GLulong; -typedef float GLfloat; -typedef float GLclampf; -typedef double GLdouble; -typedef double GLclampd; -typedef void GLvoid; -#ifndef GL_INVALID_INDEX -#define GL_INVALID_INDEX 0xFFFFFFFFu -#endif -#endif - diff --git a/code/Modules/Gfx/private/gl/gl_impl.h b/code/Modules/Gfx/private/gl/gl_impl.h deleted file mode 100644 index cf850383c..000000000 --- a/code/Modules/Gfx/private/gl/gl_impl.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - Central GL header file, this is BIG, make sure to only include - this in implementation files, not header files. -*/ -#if ORYOL_RASPBERRYPI -#define GL_GLEXT_PROTOTYPES -#include "GLES2/gl2.h" -#include "GLES2/gl2ext.h" -#elif ORYOL_WINDOWS || ORYOL_LINUX || ORYOL_MACOS -#include "Gfx/private/flextgl/flextGL.h" -#elif ORYOL_IOS -#include -#include -#elif ORYOL_EMSCRIPTEN - #if ORYOL_OPENGLES2 - #define GL_GLEXT_PROTOTYPES - #include - #include - #else - #include - #endif -#elif ORYOL_ANDROID -#define GL_GLEXT_PROTOTYPES -#include -#include -#else -#error "Missing platform for GL header include!" -#endif - -#ifndef GL_UNSIGNED_INT_24_8 - #define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES -#endif -#ifndef GL_TEXTURE_3D - #define GL_TEXTURE_3D GL_TEXTURE_3D_OES -#endif -#ifndef GL_DEPTH_STENCIL - #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES -#endif -#ifndef GL_DEPTH24_STENCIL8 - #define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES -#endif -#ifndef GL_HALF_FLOAT - #define GL_HALF_FLOAT GL_HALF_FLOAT_OES -#endif -#ifndef GL_INT_2_10_10_10_REV - #define GL_INT_2_10_10_10_REV 0x8D9F -#endif -#ifndef GL_UNSIGNED_INT_2_10_10_10_REV - #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 -#endif -#ifndef GL_RED - #define GL_RED 0x1903 -#endif -#ifndef GL_LUMINANCE - #define GL_LUMINANCE 0x1909 -#endif - -// Oryol GL error checking macro -#if ORYOL_DEBUG -#define ORYOL_GL_CHECK_ERROR() o_assert(glGetError() == GL_NO_ERROR) -#else -#define ORYOL_GL_CHECK_ERROR() ((void)0) -#endif diff --git a/code/Modules/Gfx/private/glfw/glfwDisplayMgr.cc b/code/Modules/Gfx/private/gl/glfwDisplayMgr.cc similarity index 66% rename from code/Modules/Gfx/private/glfw/glfwDisplayMgr.cc rename to code/Modules/Gfx/private/gl/glfwDisplayMgr.cc index b43908951..cce04d310 100644 --- a/code/Modules/Gfx/private/glfw/glfwDisplayMgr.cc +++ b/code/Modules/Gfx/private/gl/glfwDisplayMgr.cc @@ -3,13 +3,12 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "Core/Core.h" -#include "Gfx/private/gl/gl_impl.h" -#include "Gfx/private/gl/glCaps.h" #include "glfwDisplayMgr.h" #include "Core/Log.h" #include "Core/String/StringBuilder.h" #define GLFW_INCLUDE_NONE #include "GLFW/glfw3.h" +#include "flextGL.h" namespace Oryol { namespace _priv { @@ -42,10 +41,10 @@ glfwDisplayMgr::getGlfwWindow() { //------------------------------------------------------------------------------ void -glfwDisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { +glfwDisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); - displayMgrBase::SetupDisplay(setup, ptrs); + displayMgrBase::SetupDisplay(desc); // setup GLFW if (!glfwInit()) { @@ -54,21 +53,15 @@ glfwDisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { glfwSetErrorCallback(glfwErrorCallback); // setup the GLFW main window - this->createMainWindow(setup); + this->createMainWindow(desc); // and make the window's GL context current glfwMakeContextCurrent(glfwWindow); - glfwSwapInterval(setup.SwapInterval); + glfwSwapInterval(desc.swapInterval); // setup extensions and platform-dependent constants - ORYOL_GL_CHECK_ERROR(); flextInit(glfwWindow); - ORYOL_GL_CHECK_ERROR(); - glCaps::Setup(glCaps::GL_3_3_CORE); - #if ORYOL_DEBUG - glCaps::EnableDebugOutput(glCaps::SeverityMedium); - #endif - + // now set the actual display attributes int fbWidth = 0, fbHeight = 0; int posX = 0, posY = 0; @@ -76,13 +69,9 @@ glfwDisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { glfwGetFramebufferSize(glfwWindow, &fbWidth, &fbHeight); glfwGetWindowPos(glfwWindow, &posX, &posY); glfwGetWindowSize(glfwWindow, &width, &height); - this->displayAttrs.FramebufferWidth = fbWidth; - this->displayAttrs.FramebufferHeight = fbHeight; - this->displayAttrs.WindowPosX = posX; - this->displayAttrs.WindowPosY = posY; - this->displayAttrs.WindowWidth = width; - this->displayAttrs.WindowHeight = height; - + this->displayAttrs.Width = fbWidth; + this->displayAttrs.Height = fbHeight; + // set framebuffer size changed callback glfwSetFramebufferSizeCallback(glfwWindow, glwfFramebufferSizeChanged); } @@ -95,7 +84,6 @@ glfwDisplayMgr::DiscardDisplay() { this->destroyMainWindow(); glfwTerminate(); - glCaps::Discard(); displayMgrBase::DiscardDisplay(); } @@ -125,13 +113,6 @@ glfwDisplayMgr::Present() { displayMgrBase::Present(); } -//------------------------------------------------------------------------------ -void -glfwDisplayMgr::glBindDefaultFramebuffer() { - ::glBindFramebuffer(GL_FRAMEBUFFER, 0); - ORYOL_GL_CHECK_ERROR(); -} - //------------------------------------------------------------------------------ void glfwDisplayMgr::glfwErrorCallback(int error, const char* desc) { @@ -141,21 +122,15 @@ glfwDisplayMgr::glfwErrorCallback(int error, const char* desc) { //------------------------------------------------------------------------------ void glfwDisplayMgr::glwfFramebufferSizeChanged(GLFWwindow* win, int width, int height) { - - // update display attributes (ignore window-minimized) if ((width != 0) && (height != 0)) { - self->displayAttrs.FramebufferWidth = width; - self->displayAttrs.FramebufferHeight = height; - int winWidth, winHeight; - glfwGetWindowSize(glfwWindow, &winWidth, &winHeight); - self->displayAttrs.WindowWidth = winWidth; - self->displayAttrs.WindowHeight = winHeight; + self->displayAttrs.Width = width; + self->displayAttrs.Height = height; } } //------------------------------------------------------------------------------ void -glfwDisplayMgr::createMainWindow(const GfxSetup& setup) { +glfwDisplayMgr::createMainWindow(const GfxDesc& desc) { o_assert_dbg(nullptr == glfwDisplayMgr::glfwWindow); #if ORYOL_MACOS @@ -167,14 +142,14 @@ glfwDisplayMgr::createMainWindow(const GfxSetup& setup) { // this is necessary after the 29-Oct-2017 fips-glfw update to get the old behaviour glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE); #else - glfwWindowHint(GLFW_RED_BITS, PixelFormat::NumBits(setup.ColorFormat, PixelChannel::Red)); - glfwWindowHint(GLFW_GREEN_BITS, PixelFormat::NumBits(setup.ColorFormat, PixelChannel::Green)); - glfwWindowHint(GLFW_BLUE_BITS, PixelFormat::NumBits(setup.ColorFormat, PixelChannel::Blue)); - glfwWindowHint(GLFW_ALPHA_BITS, PixelFormat::NumBits(setup.ColorFormat, PixelChannel::Alpha)); + glfwWindowHint(GLFW_RED_BITS, PixelFormat::NumBits(desc.colorFormat, PixelChannel::Red)); + glfwWindowHint(GLFW_GREEN_BITS, PixelFormat::NumBits(desc.colorFormat, PixelChannel::Green)); + glfwWindowHint(GLFW_BLUE_BITS, PixelFormat::NumBits(desc.colorFormat, PixelChannel::Blue)); + glfwWindowHint(GLFW_ALPHA_BITS, PixelFormat::NumBits(desc.colorFormat, PixelChannel::Alpha)); #endif - glfwWindowHint(GLFW_DEPTH_BITS, PixelFormat::NumBits(setup.DepthFormat, PixelChannel::Depth)); - glfwWindowHint(GLFW_STENCIL_BITS, PixelFormat::NumBits(setup.DepthFormat, PixelChannel::Stencil)); - glfwWindowHint(GLFW_SAMPLES, setup.SampleCount > 1 ? setup.SampleCount : 0); + glfwWindowHint(GLFW_DEPTH_BITS, PixelFormat::NumBits(desc.depthFormat, PixelChannel::Depth)); + glfwWindowHint(GLFW_STENCIL_BITS, PixelFormat::NumBits(desc.depthFormat, PixelChannel::Stencil)); + glfwWindowHint(GLFW_SAMPLES, desc.sampleCount > 1 ? desc.sampleCount : 0); #if ORYOL_DEBUG glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); #endif @@ -185,14 +160,14 @@ glfwDisplayMgr::createMainWindow(const GfxSetup& setup) { // windowed or fullscreen mode? GLFWmonitor* glfwMonitor = nullptr; - if (!setup.Windowed) { + if (!desc.windowed) { glfwMonitor = glfwGetPrimaryMonitor(); } // now actually create the window - StringBuilder strBuilder(setup.Title); + StringBuilder strBuilder(desc.title); strBuilder.Append(" (GL)"); - glfwDisplayMgr::glfwWindow = glfwCreateWindow(setup.Width, setup.Height, strBuilder.AsCStr(), glfwMonitor, 0); + glfwDisplayMgr::glfwWindow = glfwCreateWindow(desc.width, desc.height, strBuilder.AsCStr(), glfwMonitor, 0); o_assert(nullptr != glfwDisplayMgr::glfwWindow); } diff --git a/code/Modules/Gfx/private/glfw/glfwDisplayMgr.h b/code/Modules/Gfx/private/gl/glfwDisplayMgr.h similarity index 84% rename from code/Modules/Gfx/private/glfw/glfwDisplayMgr.h rename to code/Modules/Gfx/private/gl/glfwDisplayMgr.h index 49cffdfc7..4f7539db2 100644 --- a/code/Modules/Gfx/private/glfw/glfwDisplayMgr.h +++ b/code/Modules/Gfx/private/gl/glfwDisplayMgr.h @@ -10,7 +10,6 @@ GL context management, and consuming window input events. */ #include "Gfx/private/displayMgrBase.h" -#include "Gfx/private/gl/gl_decl.h" struct GLFWwindow; @@ -25,7 +24,7 @@ class glfwDisplayMgr : public displayMgrBase { ~glfwDisplayMgr(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& gfxDesc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// process window system events (call near start of frame) @@ -35,9 +34,6 @@ class glfwDisplayMgr : public displayMgrBase { /// check whether the window system requests to quit the application bool QuitRequested() const; - /// bind the default frame buffer - void glBindDefaultFramebuffer(); - /// get glfwWindow handle static GLFWwindow* getGlfwWindow(); @@ -46,7 +42,7 @@ class glfwDisplayMgr : public displayMgrBase { /// framebuffer size changed callback for GLFW static void glwfFramebufferSizeChanged(GLFWwindow* win, int width, int height); /// create the main window - void createMainWindow(const GfxSetup& setup); + void createMainWindow(const GfxDesc& desc); /// destroy the main window void destroyMainWindow(); diff --git a/code/Modules/Gfx/private/ios/iosDisplayMgr.h b/code/Modules/Gfx/private/gl/iosDisplayMgr.h similarity index 66% rename from code/Modules/Gfx/private/ios/iosDisplayMgr.h rename to code/Modules/Gfx/private/gl/iosDisplayMgr.h index 26af1b95e..4f2e97192 100644 --- a/code/Modules/Gfx/private/ios/iosDisplayMgr.h +++ b/code/Modules/Gfx/private/gl/iosDisplayMgr.h @@ -6,8 +6,7 @@ @brief display manager for IOS */ #include "Gfx/private/displayMgrBase.h" -#include "Gfx/private/gl/gl_decl.h" -#include "Core/private/ios/iosBridge.h" +#include "Gfx/private/gl/gl.h" namespace Oryol { namespace _priv { @@ -20,19 +19,16 @@ class iosDisplayMgr : public displayMgrBase { ~iosDisplayMgr(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// present the current rendered frame void Present(); - /// bind the default frame buffer - void glBindDefaultFramebuffer(); - static iosDisplayMgr* self; - GLuint glDefaultFramebuffer; - GLint glFramebufferWidth; - GLint glFramebufferHeight; + bool useGLES2 = false; + GLint glFramebufferWidth = 0; + GLint glFramebufferHeight = 0; }; } // namespace _priv diff --git a/code/Modules/Gfx/private/ios/iosDisplayMgr.mm b/code/Modules/Gfx/private/gl/iosDisplayMgr.mm similarity index 76% rename from code/Modules/Gfx/private/ios/iosDisplayMgr.mm rename to code/Modules/Gfx/private/gl/iosDisplayMgr.mm index 914a76ae8..f6ff981c7 100644 --- a/code/Modules/Gfx/private/ios/iosDisplayMgr.mm +++ b/code/Modules/Gfx/private/gl/iosDisplayMgr.mm @@ -2,9 +2,8 @@ // iosDisplayMgr.mm //------------------------------------------------------------------------------ #include "Pre.h" -#include "iosDisplayMgr.h" -#include "Gfx/private/gl/gl_impl.h" -#include "Gfx/private/gl/glCaps.h" +#include "iosDisplayMgr.h" +#include "Core/private/ios/iosBridge.h" #include namespace Oryol { @@ -13,10 +12,7 @@ iosDisplayMgr* iosDisplayMgr::self = nullptr; //------------------------------------------------------------------------------ -iosDisplayMgr::iosDisplayMgr() : -glDefaultFramebuffer(0), -glFramebufferWidth(0), -glFramebufferHeight(0) { +iosDisplayMgr::iosDisplayMgr() { o_assert(nullptr == self); self = this; } @@ -32,21 +28,21 @@ //------------------------------------------------------------------------------ void -iosDisplayMgr::SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs) { +iosDisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); Log::Info("iosDisplayMgr::SetupDisplay() called!\n"); - displayMgrBase::SetupDisplay(gfxSetup, ptrs); + displayMgrBase::SetupDisplay(gfxDesc); // modify the color/depth/stencil format and content scaling of the GLKView GLKView* glkView = _priv::iosBridge::ptr()->glkView; - if (gfxSetup.HighDPI) { + if (gfxDesc.highDPI) { glkView.contentScaleFactor = 2.0f; } else { glkView.contentScaleFactor = 1.0f; } - switch (gfxSetup.ColorFormat) { + switch (gfxDesc.colorFormat) { case PixelFormat::R5G6B5: glkView.drawableColorFormat = GLKViewDrawableColorFormatRGB565; break; @@ -61,7 +57,7 @@ glkView.drawableColorFormat = GLKViewDrawableColorFormatRGB565; break; } - switch (gfxSetup.DepthFormat) { + switch (gfxDesc.depthFormat) { case PixelFormat::None: glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; @@ -91,32 +87,27 @@ else { glkView.drawableMultisample = GLKViewDrawableMultisampleNone; } - */ + */ if ([glkView.context API] == kEAGLRenderingAPIOpenGLES2) { - glCaps::Setup(glCaps::GLES2); - } else { - glCaps::Setup(glCaps::GLES3); - } - + this->useGLES2 = true; + } else { + this->useGLES2 = false; + } // update the displayAttrs with the actual frame buffer size this->glFramebufferWidth = (int) glkView.drawableWidth; this->glFramebufferHeight = (int) glkView.drawableHeight; Log::Info("iosDisplayMgr: actual framebuffer size w=%d, h=%d\n", this->glFramebufferWidth, this->glFramebufferHeight); - this->displayAttrs.FramebufferWidth = this->glFramebufferWidth; - this->displayAttrs.FramebufferHeight = this->glFramebufferHeight; - this->displayAttrs.WindowWidth = this->glFramebufferWidth; - this->displayAttrs.WindowHeight = this->glFramebufferHeight; + this->displayAttrs.Width = this->glFramebufferWidth; + this->displayAttrs.Height = this->glFramebufferHeight; } //------------------------------------------------------------------------------ void iosDisplayMgr::DiscardDisplay() { o_assert(this->IsDisplayValid()); - this->glDefaultFramebuffer = 0; this->glFramebufferWidth = 0; this->glFramebufferHeight = 0; - glCaps::Discard(); displayMgrBase::DiscardDisplay(); } @@ -128,12 +119,5 @@ displayMgrBase::Present(); } -//------------------------------------------------------------------------------ -void -iosDisplayMgr::glBindDefaultFramebuffer() { - GLKView* glkView = _priv::iosBridge::ptr()->glkView; - [glkView bindDrawable]; -} - } // namespace _priv } // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlDisplayMgr.h b/code/Modules/Gfx/private/metal/mtlDisplayMgr.h similarity index 71% rename from code/Modules/Gfx/private/mtl/mtlDisplayMgr.h rename to code/Modules/Gfx/private/metal/mtlDisplayMgr.h index 8918b6d8a..a14b71b9d 100644 --- a/code/Modules/Gfx/private/mtl/mtlDisplayMgr.h +++ b/code/Modules/Gfx/private/metal/mtlDisplayMgr.h @@ -18,18 +18,25 @@ class mtlDisplayMgr : public displayMgrBase { ~mtlDisplayMgr(); /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs); + void SetupDisplay(const GfxDesc& desc); /// discard the display, rendering cannot happen after void DiscardDisplay(); /// check whether the window system requests to quit the application bool QuitRequested() const; /// configure the app window - void configureWindow(const GfxSetup& setup); + void configureWindow(const GfxDesc& desc); /// callback for window-resize #if ORYOL_MACOS static void onFramebufferSize(int w, int h); #endif + /// get Metal device as bridged pointer + static const void* mtlDevice(); + /// get a new MTLRenderPassDescriptor as bridged pointer + static const void* mtlRenderPassDescriptor(); + /// get a new MTLDrawable as bridged pointer + static const void* mtlDrawable(); + /// ptr to self for onFramebufferSize static mtlDisplayMgr* self; }; diff --git a/code/Modules/Gfx/private/mtl/mtlDisplayMgr.mm b/code/Modules/Gfx/private/metal/mtlDisplayMgr.mm similarity index 65% rename from code/Modules/Gfx/private/mtl/mtlDisplayMgr.mm rename to code/Modules/Gfx/private/metal/mtlDisplayMgr.mm index a556d5fd0..d28596ed2 100644 --- a/code/Modules/Gfx/private/mtl/mtlDisplayMgr.mm +++ b/code/Modules/Gfx/private/metal/mtlDisplayMgr.mm @@ -3,12 +3,22 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "mtlDisplayMgr.h" -#include "mtlTypes.h" #include "Core/String/StringBuilder.h" +#if ORYOL_MACOS +#include "Core/private/osx/osxBridge.h" +#else +#include "Core/private/ios/iosBridge.h" +#endif namespace Oryol { namespace _priv { +#if ORYOL_MACOS +typedef osxBridge osBridge; +#else +typedef iosBridge osBridge; +#endif + mtlDisplayMgr* mtlDisplayMgr::self = nullptr; //------------------------------------------------------------------------------ @@ -28,12 +38,12 @@ //------------------------------------------------------------------------------ void -mtlDisplayMgr::SetupDisplay(const GfxSetup& setup, const gfxPointers& ptrs) { +mtlDisplayMgr::SetupDisplay(const GfxDesc& desc) { o_assert(!this->IsDisplayValid()); - displayMgrBase::SetupDisplay(setup, ptrs); + displayMgrBase::SetupDisplay(desc); - this->configureWindow(setup); + this->configureWindow(desc); #if ORYOL_MACOS osBridge::ptr()->showWindow(); osBridge::ptr()->callbacks.fbsize = mtlDisplayMgr::onFramebufferSize; @@ -59,24 +69,24 @@ //------------------------------------------------------------------------------ void -mtlDisplayMgr::configureWindow(const GfxSetup& setup) { +mtlDisplayMgr::configureWindow(const GfxDesc& desc) { #if ORYOL_MACOS - StringBuilder strBuilder(setup.Title); + StringBuilder strBuilder(desc.Title); strBuilder.Append(" (Metal)"); NSWindow* window = osxBridge::ptr()->appWindow; [window setTitle:[NSString stringWithUTF8String:strBuilder.AsCStr()]]; - [window setContentSize:NSMakeSize(setup.Width, setup.Height)]; + [window setContentSize:NSMakeSize(desc.Width, desc.Height)]; [window center]; osBridge* bridge = osBridge::ptr(); - if (!setup.HighDPI) { - CGSize drawableSize = { (CGFloat) setup.Width, (CGFloat) setup.Height }; + if (!desc.HighDPI) { + CGSize drawableSize = { (CGFloat) desc.Width, (CGFloat) desc.Height }; [bridge->mtkView setDrawableSize:drawableSize]; } #elif ORYOL_IOS osBridge* bridge = osBridge::ptr(); - if (gfxSetup.HighDPI) { + if (desc.HighDPI()) { [bridge->mtkView setContentScaleFactor:2.0f]; bridge->mouseScale = 2.0f; } @@ -89,26 +99,19 @@ [[bridge->mtkView layer] setMagnificationFilter:kCAFilterNearest]; // get actual rendering size - #if ORYOL_IOS - const CGRect winContentRect = [bridge->mtkView frame]; - #else - const NSRect winContentRect = [bridge->mtkView frame]; - #endif CGSize fbSize = [bridge->mtkView drawableSize]; int fbWidth = (int) fbSize.width; int fbHeight = (int) fbSize.height; #if ORYOL_OSX - if (fbWidth == setup.Width * 2) { + if (fbWidth == desc.Width * 2) { // we're on a Retina display bridge->mouseScale = 2.0; } #endif Log::Info("mtlDisplayMgr: actual framebuffer size w=%d, h=%d\n", fbWidth, fbHeight); - this->displayAttrs.FramebufferWidth = fbWidth; - this->displayAttrs.FramebufferHeight = fbHeight; - this->displayAttrs.WindowWidth = winContentRect.size.width; - this->displayAttrs.WindowHeight = winContentRect.size.height; - [osBridge::ptr()->mtkView setSampleCount:setup.SampleCount]; + this->displayAttrs.Width = fbWidth; + this->displayAttrs.Height = fbHeight; + [osBridge::ptr()->mtkView setSampleCount:desc.SampleCount]; } //------------------------------------------------------------------------------ @@ -119,13 +122,29 @@ if (self) { // need to get the actual size CGSize fbSize = [osxBridge::ptr()->mtkView drawableSize]; - self->displayAttrs.FramebufferWidth = (int) fbSize.width; - self->displayAttrs.FramebufferHeight = (int) fbSize.height; - self->displayAttrs.WindowWidth = w; - self->displayAttrs.WindowHeight = h; + self->displayAttrs.Width = (int) fbSize.width; + self->displayAttrs.Height = (int) fbSize.height; } } #endif +//------------------------------------------------------------------------------ +const void* +mtlDisplayMgr::mtlDevice() { + return (__bridge const void*) osBridge::ptr()->mtlDevice; +} + +//------------------------------------------------------------------------------ +const void* +mtlDisplayMgr::mtlRenderPassDescriptor() { + return (__bridge const void*) [osBridge::ptr()->mtkView currentRenderPassDescriptor]; +} + +//------------------------------------------------------------------------------ +const void* +mtlDisplayMgr::mtlDrawable() { + return (__bridge const void*) [osBridge::ptr()->mtkView currentDrawable]; +} + } // namespace _priv } // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlFactory.h b/code/Modules/Gfx/private/mtl/mtlFactory.h deleted file mode 100644 index 1498716f8..000000000 --- a/code/Modules/Gfx/private/mtl/mtlFactory.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlFactory - @ingroup _priv - @brief Metal implementation of gfxFactory -*/ -#include "Gfx/private/gfxFactoryBase.h" -#include "Gfx/private/mtl/mtl_decl.h" -#include "Core/Containers/Queue.h" -#include "Core/Containers/Map.h" - -namespace Oryol { -namespace _priv { - -class mesh; -class texture; -class renderPass; -class shader; -class pipeline; - -class mtlFactory : public gfxFactoryBase { -public: - /// setup the factory - void setup(const gfxPointers& ptrs); - /// discard the factory - void discard(); - /// initialize new mesh object - ResourceState::Code initMesh(mesh& msh, const void* data, int size); - /// destroy a mesh object - void destroyMesh(mesh& msh); - /// initialize a new texture object - ResourceState::Code initTexture(texture& tex, const void* data, int size); - /// destroy a texture object - void destroyTexture(texture& tex); - /// initialize a new shader object - ResourceState::Code initShader(shader& shd); - /// destroy a shader object - void destroyShader(shader& shd); - /// initialize a new pipeline object - ResourceState::Code initPipeline(pipeline& pip); - /// destroy a pipeline object - void destroyPipeline(pipeline& pip); - - /// add an id to be released when no longer used by GPU - void releaseDeferred(ORYOL_OBJC_ID obj); - /// garbage collect deferred-released Metal resources - void garbageCollect(); - - /// helper method to setup a mesh object as fullscreen quad - ResourceState::Code initFullscreenQuad(mesh& mesh); - /// helper method to create a standard mesh - ResourceState::Code initStdMesh(mesh& mesh, const void* data, int size); - /// helper method to create a vertex buffer object - ORYOL_OBJC_TYPED_ID(MTLBuffer) createBuffer(const void* data, uint32_t dataSize, Usage::Code usage); - - /// helper method to create a sampler state object and set in texture object - void createSamplerState(texture& tex); - /// helper method to release sampler state object of texture - void releaseSamplerState(texture& tex); - - struct freeItem { - freeItem(); - freeItem(int frameIndex, ORYOL_OBJC_ID obj); - ~freeItem(); - - int frameIndex; - ORYOL_OBJC_ID obj; - }; - Queue releaseQueue; - - struct SamplerCacheItem { - ORYOL_OBJC_TYPED_ID(MTLSamplerState) mtlSamplerState; - int useCount; - }; - Map samplerCache; -}; - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/mtl/mtlFactory.mm b/code/Modules/Gfx/private/mtl/mtlFactory.mm deleted file mode 100644 index 5738d241e..000000000 --- a/code/Modules/Gfx/private/mtl/mtlFactory.mm +++ /dev/null @@ -1,625 +0,0 @@ -//------------------------------------------------------------------------------ -// mtlFactory.mm -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "mtl_impl.h" -#include "mtlFactory.h" -#include "mtlTypes.h" -#include "Gfx/private/renderer.h" -#include "Gfx/private/resource.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -void -mtlFactory::setup(const gfxPointers& ptrs) { - o_assert(!this->isValid); - gfxFactoryBase::setup(ptrs); - this->samplerCache.Reserve(64); - this->releaseQueue.Reserve(256); -} - -//------------------------------------------------------------------------------ -void -mtlFactory::discard() { - o_assert(this->isValid); - this->releaseQueue.Clear(); - this->samplerCache.Clear(); - gfxFactoryBase::discard(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initMesh(mesh& msh, const void* data, int size) { - o_assert_dbg(this->isValid); - if (msh.Setup.ShouldSetupFullScreenQuad()) { - return this->initFullscreenQuad(msh); - } - else { - return this->initStdMesh(msh, data, size); - } -} - -//------------------------------------------------------------------------------ -void -mtlFactory::destroyMesh(mesh& msh) { - o_assert_dbg(this->isValid); - for (auto& buf : msh.buffers) { - for (auto& mtlBuf : buf.mtlBuffers) { - if (nil != mtlBuf) { - this->releaseDeferred(mtlBuf); - } - } - } - msh.Clear(); -} - -//------------------------------------------------------------------------------ -id -mtlFactory::createBuffer(const void* data, uint32 bufSize, Usage::Code usage) { - o_assert_dbg(bufSize > 0); - MTLResourceOptions options = mtlTypes::asBufferResourceOptions(usage); - id buf; - if (data) { - buf = [this->pointers.renderer->mtlDevice newBufferWithBytes:data length:bufSize options:options]; - } - else { - buf = [this->pointers.renderer->mtlDevice newBufferWithLength:bufSize options:options]; - } - return buf; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initStdMesh(mesh& msh, const void* data, int size) { - o_assert_dbg(nil == msh.buffers[mesh::vb].mtlBuffers[0]); - o_assert_dbg(nil == msh.buffers[mesh::ib].mtlBuffers[0]); - o_assert_dbg(1 == msh.buffers[mesh::vb].numSlots); - o_assert_dbg(1 == msh.buffers[mesh::ib].numSlots); - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = msh.Setup.NumVertices; - vbAttrs.BufferUsage = msh.Setup.VertexUsage; - vbAttrs.Layout = msh.Setup.Layout; - msh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = msh.Setup.NumIndices; - ibAttrs.Type = msh.Setup.IndicesType; - ibAttrs.BufferUsage = msh.Setup.IndexUsage; - msh.indexBufferAttrs = ibAttrs; - - msh.numPrimGroups = msh.Setup.NumPrimitiveGroups(); - o_assert_dbg(msh.numPrimGroups < GfxConfig::MaxNumPrimGroups); - for (int i = 0; i < msh.numPrimGroups; i++) { - msh.primGroups[i] = msh.Setup.PrimitiveGroup(i); - } - - const uint8* ptr = (const uint8*) data; - - // create vertex buffer - if (msh.Setup.NumVertices > 0) { - const auto& vbAttrs = msh.vertexBufferAttrs; - const int vbSize = vbAttrs.NumVertices * msh.Setup.Layout.ByteSize(); - msh.buffers[mesh::vb].numSlots = Usage::Immutable == vbAttrs.BufferUsage ? 1 : 2; - const uint8* vertices = nullptr; - if (data) { - o_assert((msh.Setup.VertexDataOffset >= 0) && (size > 0)); - vertices = ptr + msh.Setup.VertexDataOffset; - o_assert_dbg((ptr + size) >= (vertices + vbSize)); - } - for (uint8 slotIndex = 0; slotIndex < msh.buffers[mesh::vb].numSlots; slotIndex++) { - msh.buffers[mesh::vb].mtlBuffers[slotIndex] = this->createBuffer(vertices, vbSize, vbAttrs.BufferUsage); - o_assert_dbg(nil != msh.buffers[mesh::vb].mtlBuffers[slotIndex]); - } - } - - // create optional index buffer - if (msh.indexBufferAttrs.Type != IndexType::None) { - const auto& ibAttrs = msh.indexBufferAttrs; - const int ibSize = ibAttrs.NumIndices * IndexType::ByteSize(ibAttrs.Type); - msh.buffers[mesh::ib].numSlots = Usage::Immutable == ibAttrs.BufferUsage ? 1 : 2; - const uint8* indices = nullptr; - if (data) { - o_assert((msh.Setup.IndexDataOffset >= 0) && (size > 0)); - indices = ptr + msh.Setup.IndexDataOffset; - o_assert_dbg((ptr + size) >= (indices + ibSize)); - } - for (uint8 slotIndex = 0; slotIndex < msh.buffers[mesh::ib].numSlots; slotIndex++) { - msh.buffers[mesh::ib].mtlBuffers[slotIndex] = this->createBuffer(indices, ibSize, ibAttrs.BufferUsage); - o_assert_dbg(nil != msh.buffers[mesh::ib].mtlBuffers[slotIndex]); - } - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initFullscreenQuad(mesh& msh) { - - VertexBufferAttrs vbAttrs; - vbAttrs.NumVertices = 4; - vbAttrs.BufferUsage = Usage::Immutable; - vbAttrs.Layout.Add(VertexAttr::Position, VertexFormat::Float3); - vbAttrs.Layout.Add(VertexAttr::TexCoord0, VertexFormat::Float2); - msh.vertexBufferAttrs = vbAttrs; - - IndexBufferAttrs ibAttrs; - ibAttrs.NumIndices = 6; - ibAttrs.Type = IndexType::Index16; - ibAttrs.BufferUsage = Usage::Immutable; - msh.indexBufferAttrs = ibAttrs; - - msh.numPrimGroups = 1; - msh.primGroups[0] = PrimitiveGroup(0, 6); - - const float topV = msh.Setup.FullScreenQuadFlipV ? 0.0f : 1.0f; - const float botV = msh.Setup.FullScreenQuadFlipV ? 1.0f : 0.0f; - float vertices[] = { - -1.0f, +1.0f, 0.0f, 0.0f, topV, // top-left corner - +1.0f, +1.0f, 0.0f, 1.0f, topV, // top-right corner - +1.0f, -1.0f, 0.0f, 1.0f, botV, // bottom-right corner - -1.0f, -1.0f, 0.0f, 0.0f, botV, // bottom-left corner - }; - - uint16_t indices[] = { - 0, 2, 1, // topleft -> bottomright -> topright - 0, 3, 2, // topleft -> bottomleft -> bottomright - }; - - msh.buffers[mesh::vb].mtlBuffers[0] = this->createBuffer(vertices, sizeof(vertices), Usage::Immutable); - msh.buffers[mesh::ib].mtlBuffers[0] = this->createBuffer(indices, sizeof(indices), Usage::Immutable); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initTexture(texture& tex, const void* data, int size) { - o_assert_dbg(this->isValid); - o_assert_dbg(nil == tex.mtlTextures[0]); - o_assert_dbg(nil == tex.mtlSamplerState); - - const TextureSetup& setup = tex.Setup; - #if ORYOL_DEBUG - o_assert(setup.NumMipMaps > 0); - if (setup.IsRenderTarget) { - o_assert(PixelFormat::IsValidRenderTargetColorFormat(setup.ColorFormat)); - } - #endif - - // create one or two texture objects - tex.numSlots = Usage::Immutable == setup.TextureUsage ? 1 : 2; - - // create metal texture object - MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; - texDesc.textureType = mtlTypes::asTextureType(setup.Type); - if (setup.IsRenderTarget) { - texDesc.pixelFormat = mtlTypes::asRenderTargetColorFormat(setup.ColorFormat); - } - else { - texDesc.pixelFormat = mtlTypes::asTextureFormat(setup.ColorFormat); - } - if (MTLPixelFormatInvalid == texDesc.pixelFormat) { - o_warn("mtlTextureFactory: texture pixel format not supported in Metal!\n"); - return ResourceState::Failed; - } - texDesc.width = setup.Width; - texDesc.height = setup.Height; - if (setup.Type == TextureType::Texture3D) { - texDesc.depth = setup.Depth; - } - else { - texDesc.depth = 1; - } - texDesc.mipmapLevelCount = setup.NumMipMaps; - if (setup.Type == TextureType::TextureArray) { - texDesc.arrayLength = setup.Depth; - } - else { - texDesc.arrayLength = 1; - } - texDesc.usage = MTLTextureUsageShaderRead; - if (setup.IsRenderTarget) { - texDesc.resourceOptions = MTLResourceStorageModePrivate; - texDesc.cpuCacheMode = MTLCPUCacheModeDefaultCache; - texDesc.storageMode = MTLStorageModePrivate; - texDesc.usage |= MTLTextureUsageRenderTarget; - } - - const int numFaces = setup.Type == TextureType::TextureCube ? 6 : 1; - const int numSlices = setup.Type == TextureType::TextureArray ? setup.Depth : 1; - for (int slotIndex = 0; slotIndex < tex.numSlots; slotIndex++) { - tex.mtlTextures[slotIndex] = [this->pointers.renderer->mtlDevice newTextureWithDescriptor:texDesc]; - o_assert(nil != tex.mtlTextures[slotIndex]); - - // copy optional data bytes into texture - if (data) { - o_assert_dbg(size > 0); - const uint8* srcPtr = (const uint8*) data; - for (int faceIndex = 0; faceIndex < numFaces; faceIndex++) { - for (int mipIndex = 0; mipIndex < setup.ImageData.NumMipMaps; mipIndex++) { - o_assert_dbg(mipIndex <= setup.NumMipMaps); - int mipWidth = std::max(setup.Width >> mipIndex, 1); - int mipHeight = std::max(setup.Height >> mipIndex, 1); - // special case PVRTC formats: bytesPerRow must be 0 - int bytesPerRow = 0; - int bytesPerImage = 0; - if (!PixelFormat::IsPVRTC(setup.ColorFormat)) { - bytesPerRow = PixelFormat::RowPitch(setup.ColorFormat, mipWidth); - } - MTLRegion region; - if (setup.Type == TextureType::Texture3D) { - int mipDepth = std::max(setup.Depth >> mipIndex, 1); - region = MTLRegionMake3D(0, 0, 0, mipWidth, mipHeight, mipDepth); - bytesPerImage = bytesPerRow * mipHeight; - if (bytesPerImage < 4096) { - o_warn("FIXME: bytesPerImage < 4096, this must be fixed in code by copying the data\n" - "into a temp buffer with the right image alignment!\n"); - } - } - else { - region = MTLRegionMake2D(0, 0, mipWidth, mipHeight); - } - for (int sliceIndex = 0; sliceIndex < numSlices; sliceIndex++) { - const int mtlSliceIndex = setup.Type == TextureType::TextureCube ? faceIndex : sliceIndex; - const int sliceDataOffset = sliceIndex * PixelFormat::ImagePitch(setup.ColorFormat, mipWidth, mipHeight); - [tex.mtlTextures[slotIndex] replaceRegion:region - mipmapLevel:mipIndex - slice:mtlSliceIndex - withBytes:srcPtr + setup.ImageData.Offsets[faceIndex][mipIndex] + sliceDataOffset - bytesPerRow:bytesPerRow - bytesPerImage:bytesPerImage]; - } - } - } - } - } - - if (setup.IsRenderTarget) { - // prepare texture descriptor for optional MSAA and depth texture - texDesc.textureType = MTLTextureType2D; - texDesc.depth = 1; - texDesc.mipmapLevelCount = 1; - texDesc.arrayLength = 1; - - // create optional MSAA texture where offscreen rendering will go to, - // the 'default' Metal texture will serve as resolve-texture - if (setup.SampleCount > 1) { - texDesc.textureType = MTLTextureType2DMultisample; - texDesc.sampleCount = setup.SampleCount; - tex.mtlMSAATex = [this->pointers.renderer->mtlDevice newTextureWithDescriptor:texDesc]; - o_assert(nil != tex.mtlMSAATex); - } - - // create optional depth buffer texture (may be MSAA) - if (setup.HasDepth()) { - o_assert_dbg(setup.IsRenderTarget); - o_assert_dbg(PixelFormat::IsValidRenderTargetDepthFormat(setup.DepthFormat)); - o_assert_dbg(PixelFormat::None != setup.DepthFormat); - texDesc.pixelFormat = mtlTypes::asRenderTargetDepthFormat(setup.DepthFormat); - tex.mtlDepthTex = [this->pointers.renderer->mtlDevice newTextureWithDescriptor:texDesc]; - o_assert(nil != tex.mtlDepthTex); - } - } - - // create sampler object - this->createSamplerState(tex); - o_assert(nil != tex.mtlSamplerState); - - // setup texture attributes - TextureAttrs attrs; - attrs.Locator = setup.Locator; - attrs.Type = setup.Type; - attrs.ColorFormat = setup.ColorFormat; - attrs.DepthFormat = setup.DepthFormat; - attrs.SampleCount = setup.SampleCount; - attrs.TextureUsage = setup.TextureUsage; - attrs.Width = setup.Width; - attrs.Height = setup.Height; - attrs.Depth = setup.Depth; - attrs.NumMipMaps = setup.NumMipMaps; - attrs.IsRenderTarget = setup.IsRenderTarget; - attrs.HasDepthBuffer = setup.HasDepth(); - tex.textureAttrs = attrs; - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -mtlFactory::destroyTexture(texture& tex) { - o_assert_dbg(this->isValid); - for (auto& mtlTex : tex.mtlTextures) { - if (nil != mtlTex) { - this->releaseDeferred(mtlTex); - } - } - if (nil != tex.mtlDepthTex) { - this->releaseDeferred(tex.mtlDepthTex); - } - if (nil != tex.mtlMSAATex) { - this->releaseDeferred(tex.mtlMSAATex); - } - if (nil != tex.mtlSamplerState) { - this->releaseSamplerState(tex); - } - tex.Clear(); -} - -//------------------------------------------------------------------------------ -void -mtlFactory::createSamplerState(texture& tex) { - o_assert_dbg(nil == tex.mtlSamplerState); - - // check if an identical state already exists - const int cacheIndex = this->samplerCache.FindIndex(tex.Setup.Sampler.Hash); - if (InvalidIndex != cacheIndex) { - // re-use existing sampler-state object - SamplerCacheItem& item = this->samplerCache.ValueAtIndex(cacheIndex); - item.useCount++; - tex.mtlSamplerState = item.mtlSamplerState; - } - else { - // create new sampler-state object - MTLSamplerDescriptor* desc = [[MTLSamplerDescriptor alloc] init]; - desc.sAddressMode = mtlTypes::asSamplerAddressMode(tex.Setup.Sampler.WrapU); - desc.tAddressMode = mtlTypes::asSamplerAddressMode(tex.Setup.Sampler.WrapV); - if (TextureType::Texture3D == tex.Setup.Type) { - desc.rAddressMode = mtlTypes::asSamplerAddressMode(tex.Setup.Sampler.WrapW); - } - desc.minFilter = mtlTypes::asSamplerMinMagFilter(tex.Setup.Sampler.MinFilter); - desc.magFilter = mtlTypes::asSamplerMinMagFilter(tex.Setup.Sampler.MagFilter); - desc.mipFilter = mtlTypes::asSamplerMipFilter(tex.Setup.Sampler.MinFilter); - desc.lodMinClamp = 0.0f; - desc.lodMaxClamp = FLT_MAX; - desc.maxAnisotropy = 1; - desc.normalizedCoordinates = YES; - tex.mtlSamplerState = [this->pointers.renderer->mtlDevice newSamplerStateWithDescriptor:desc]; - o_assert(nil != tex.mtlSamplerState); - - // add new cache entry - SamplerCacheItem item; - item.mtlSamplerState = tex.mtlSamplerState; - item.useCount = 1; - this->samplerCache.Add(tex.Setup.Sampler.Hash, item); - } -} - -//------------------------------------------------------------------------------ -void -mtlFactory::releaseSamplerState(texture& tex) { - o_assert_dbg(nil != tex.mtlSamplerState); - - // find cache entry (linear search is ok, since total number of - // sampler state will be low - for (int index = this->samplerCache.Size()-1; index >= 0; index--) { - SamplerCacheItem& item = this->samplerCache.ValueAtIndex(index); - if (item.mtlSamplerState == tex.mtlSamplerState) { - o_assert_dbg(item.useCount > 0); - if (--item.useCount == 0) { - this->releaseDeferred(item.mtlSamplerState); - this->samplerCache.EraseIndex(index); - } - break; - } - } -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initShader(shader& shd) { - o_assert_dbg(this->isValid); - o_assert_dbg(nil == shd.mtlVertexShaderLibrary); - o_assert_dbg(nil == shd.mtlFragmentShaderLibrary); - - const ShaderLang::Code slang = ShaderLang::Metal; - const ShaderSetup& setup = shd.Setup; - const void* vsLibraryByteCode = nullptr; - uint32_t vsLibraryByteCodeSize = 0; - setup.VertexShaderByteCode(slang, vsLibraryByteCode, vsLibraryByteCodeSize); - o_assert_dbg(vsLibraryByteCode && (vsLibraryByteCodeSize > 0)); - const void* fsLibraryByteCode = nullptr; - uint32_t fsLibraryByteCodeSize = 0; - setup.FragmentShaderByteCode(slang, fsLibraryByteCode, fsLibraryByteCodeSize); - o_assert_dbg(fsLibraryByteCode && (fsLibraryByteCodeSize > 0)); - - // first create the shader library (one library per bundle) - NSError* err = 0; - dispatch_data_t vsLibData = dispatch_data_create(vsLibraryByteCode, vsLibraryByteCodeSize, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); - shd.mtlVertexShaderLibrary = [this->pointers.renderer->mtlDevice newLibraryWithData:vsLibData error:&err]; - o_assert(nil == err); - dispatch_data_t fsLibData = dispatch_data_create(fsLibraryByteCode, fsLibraryByteCodeSize, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); - shd.mtlFragmentShaderLibrary = [this->pointers.renderer->mtlDevice newLibraryWithData:fsLibData error:&err]; - o_assert(nil == err); - - // create vertex and fragment shader function objects - const StringAtom& vsName = setup.VertexShaderFunc(slang); - shd.mtlVertexShader = [shd.mtlVertexShaderLibrary newFunctionWithName:[NSString stringWithUTF8String:vsName.AsCStr()]]; - const StringAtom& fsName = setup.FragmentShaderFunc(slang); - shd.mtlFragmentShader = [shd.mtlFragmentShaderLibrary newFunctionWithName:[NSString stringWithUTF8String:fsName.AsCStr()]]; - - // get the vertex shader attribute locations - shd.vsAttrIndices.Fill(InvalidIndex); - for (MTLVertexAttribute* mtlAttr in shd.mtlVertexShader.vertexAttributes) { - if (mtlAttr.active) { - VertexAttr::Code attr = VertexAttr::FromString(mtlAttr.name.UTF8String); - if (VertexAttr::InvalidVertexAttr != attr) { - shd.vsAttrIndices[attr] = int(mtlAttr.attributeIndex); - } - else { - o_error("Invalid vertex attribute name '%s'!\n", mtlAttr.name.UTF8String); - } - } - } - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -mtlFactory::destroyShader(shader& shd) { - o_assert_dbg(this->isValid); - if (nil != shd.mtlVertexShader) { - this->releaseDeferred(shd.mtlVertexShader); - } - if (nil != shd.mtlFragmentShader) { - this->releaseDeferred(shd.mtlFragmentShader); - } - if (nil != shd.mtlVertexShaderLibrary) { - this->releaseDeferred(shd.mtlVertexShaderLibrary); - } - if (nil != shd.mtlFragmentShaderLibrary) { - this->releaseDeferred(shd.mtlFragmentShaderLibrary); - } - shd.Clear(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -mtlFactory::initPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - o_assert_dbg(nil == pip.mtlRenderPipelineState); - o_assert_dbg(nil == pip.mtlDepthStencilState); - - gfxFactoryBase::initPipeline(pip); - o_assert_dbg(pip.shd); - - // create vertex-descriptor object - MTLVertexDescriptor* vtxDesc = [MTLVertexDescriptor vertexDescriptor]; - for (int meshSlotIndex = 0; meshSlotIndex < GfxConfig::MaxNumInputMeshes; meshSlotIndex++) { - // NOTE: vertex buffers are located after constant buffers - const int vbSlotIndex = meshSlotIndex + GfxConfig::MaxNumUniformBlocksPerStage; - const VertexLayout& meshLayout = pip.Setup.Layouts[meshSlotIndex]; - for (int meshCompIndex = 0; meshCompIndex < meshLayout.NumComponents(); meshCompIndex++) { - const auto& comp = meshLayout.ComponentAt(meshCompIndex); - // find the vertex attribute index in the shader - int mtlAttrIndex = pip.shd->vsAttrIndices[comp.Attr]; - if (InvalidIndex != mtlAttrIndex) { - vtxDesc.attributes[mtlAttrIndex].format = mtlTypes::asVertexFormat(comp.Format); - vtxDesc.attributes[mtlAttrIndex].bufferIndex = vbSlotIndex; - vtxDesc.attributes[mtlAttrIndex].offset = meshLayout.ComponentByteOffset(meshCompIndex); - } - } - vtxDesc.layouts[vbSlotIndex].stride = meshLayout.ByteSize(); - vtxDesc.layouts[vbSlotIndex].stepFunction = mtlTypes::asVertexStepFunc(meshLayout.StepFunction); - vtxDesc.layouts[vbSlotIndex].stepRate = meshLayout.StepRate; - } - - // create renderpipeline-state - const BlendState& blendState = pip.Setup.BlendState; - MTLRenderPipelineDescriptor* rpDesc = [[MTLRenderPipelineDescriptor alloc] init]; - o_assert(blendState.MRTCount <= GfxConfig::MaxNumColorAttachments); - for (int i = 0; i < blendState.MRTCount; i++) { - rpDesc.colorAttachments[i].pixelFormat = mtlTypes::asRenderTargetColorFormat(blendState.ColorFormat); - rpDesc.colorAttachments[i].writeMask = mtlTypes::asColorWriteMask(blendState.ColorWriteMask); - rpDesc.colorAttachments[i].blendingEnabled = blendState.BlendEnabled; - rpDesc.colorAttachments[i].alphaBlendOperation = mtlTypes::asBlendOp(blendState.OpAlpha); - rpDesc.colorAttachments[i].rgbBlendOperation = mtlTypes::asBlendOp(blendState.OpRGB); - rpDesc.colorAttachments[i].destinationAlphaBlendFactor = mtlTypes::asBlendFactor(blendState.DstFactorAlpha); - rpDesc.colorAttachments[i].destinationRGBBlendFactor = mtlTypes::asBlendFactor(blendState.DstFactorRGB); - rpDesc.colorAttachments[i].sourceAlphaBlendFactor = mtlTypes::asBlendFactor(blendState.SrcFactorAlpha); - rpDesc.colorAttachments[i].sourceRGBBlendFactor = mtlTypes::asBlendFactor(blendState.SrcFactorRGB); - } - rpDesc.depthAttachmentPixelFormat = mtlTypes::asRenderTargetDepthFormat(blendState.DepthFormat); - rpDesc.stencilAttachmentPixelFormat = mtlTypes::asRenderTargetStencilFormat(blendState.DepthFormat); - rpDesc.fragmentFunction = pip.shd->mtlFragmentShader; - rpDesc.vertexFunction = pip.shd->mtlVertexShader; - rpDesc.vertexDescriptor = vtxDesc; - rpDesc.rasterizationEnabled = YES; - rpDesc.alphaToCoverageEnabled = pip.Setup.RasterizerState.AlphaToCoverageEnabled; - rpDesc.alphaToOneEnabled = NO; - rpDesc.sampleCount = pip.Setup.RasterizerState.SampleCount; - NSError* err = NULL; - pip.mtlRenderPipelineState = [this->pointers.renderer->mtlDevice newRenderPipelineStateWithDescriptor:rpDesc error:&err]; - if (!pip.mtlRenderPipelineState) { - o_error("mtlPipelineFactory: failed to create MTLRenderPipelineState with:\n %s\n", err.localizedDescription.UTF8String); - } - - // create depth-stencil-state - const DepthStencilState& dss = pip.Setup.DepthStencilState; - MTLDepthStencilDescriptor* dsDesc = [[MTLDepthStencilDescriptor alloc] init]; - dsDesc.depthCompareFunction = mtlTypes::asCompareFunc(dss.DepthCmpFunc); - dsDesc.depthWriteEnabled = dss.DepthWriteEnabled; - if (dss.StencilEnabled) { - dsDesc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; - dsDesc.backFaceStencil.stencilFailureOperation = mtlTypes::asStencilOp(dss.StencilBack.FailOp); - dsDesc.backFaceStencil.depthFailureOperation = mtlTypes::asStencilOp(dss.StencilBack.DepthFailOp); - dsDesc.backFaceStencil.depthStencilPassOperation = mtlTypes::asStencilOp(dss.StencilBack.PassOp); - dsDesc.backFaceStencil.stencilCompareFunction = mtlTypes::asCompareFunc(dss.StencilBack.CmpFunc); - dsDesc.backFaceStencil.readMask = dss.StencilReadMask; - dsDesc.backFaceStencil.writeMask = dss.StencilWriteMask; - - dsDesc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; - dsDesc.frontFaceStencil.stencilFailureOperation = mtlTypes::asStencilOp(dss.StencilFront.FailOp); - dsDesc.frontFaceStencil.depthFailureOperation = mtlTypes::asStencilOp(dss.StencilFront.DepthFailOp); - dsDesc.frontFaceStencil.depthStencilPassOperation = mtlTypes::asStencilOp(dss.StencilFront.PassOp); - dsDesc.frontFaceStencil.stencilCompareFunction = mtlTypes::asCompareFunc(dss.StencilFront.CmpFunc); - dsDesc.frontFaceStencil.readMask = dss.StencilReadMask; - dsDesc.frontFaceStencil.writeMask = dss.StencilWriteMask; - } - pip.mtlDepthStencilState = [this->pointers.renderer->mtlDevice newDepthStencilStateWithDescriptor:dsDesc]; - o_assert(nil != pip.mtlDepthStencilState); - - return ResourceState::Valid; -} - -//------------------------------------------------------------------------------ -void -mtlFactory::destroyPipeline(pipeline& pip) { - o_assert_dbg(this->isValid); - if (nil != pip.mtlRenderPipelineState) { - this->releaseDeferred(pip.mtlRenderPipelineState); - } - if (nil != pip.mtlDepthStencilState) { - this->releaseDeferred(pip.mtlDepthStencilState); - } - gfxFactoryBase::destroyPipeline(pip); -} - -//------------------------------------------------------------------------------ -mtlFactory::freeItem::freeItem() : -frameIndex(0), -obj(nil) { - // empty -} - -//------------------------------------------------------------------------------ -mtlFactory::freeItem::freeItem(int frameIndex_, ORYOL_OBJC_ID obj_) : -frameIndex(frameIndex_), -obj(obj_) { - // empty -} - -//------------------------------------------------------------------------------ -mtlFactory::freeItem::~freeItem() { - if (nil != this->obj) { - ORYOL_OBJC_RELEASE(this->obj); - this->obj = nil; - } -} - -//------------------------------------------------------------------------------ -void -mtlFactory::garbageCollect() { - o_assert_dbg(this->isValid); - const int frameIndex = this->pointers.renderer->frameIndex; - const int safeNumFrames = GfxConfig::MaxInflightFrames + 1; - if (frameIndex > safeNumFrames) { - const int minReleaseFrame = frameIndex - GfxConfig::MaxInflightFrames; - while (!this->releaseQueue.Empty() && (this->releaseQueue.Front().frameIndex < minReleaseFrame)) { - this->releaseQueue.Dequeue(); - } - } -} - -//------------------------------------------------------------------------------ -void -mtlFactory::releaseDeferred(ORYOL_OBJC_ID obj) { - o_assert_dbg(this->isValid); - const int frameIndex = this->pointers.renderer->frameIndex; - this->releaseQueue.Enqueue(frameIndex, obj); -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlRenderer.h b/code/Modules/Gfx/private/mtl/mtlRenderer.h deleted file mode 100644 index 71405198f..000000000 --- a/code/Modules/Gfx/private/mtl/mtlRenderer.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlRenderer - @ingroup _priv - @brief Metal implementation of class 'renderer' -*/ -#include "Core/Types.h" -#include "Core/Containers/StaticArray.h" -#include "Gfx/GfxTypes.h" -#include "Gfx/GfxConfig.h" -#include "Gfx/private/gfxPointers.h" -#include "Gfx/private/resource.h" -#include "Gfx/private/mtl/mtl_decl.h" -#include "glm/vec4.hpp" - -namespace Oryol { -namespace _priv { - -class mtlRenderer { -public: - /// constructor - mtlRenderer(); - /// destructor - ~mtlRenderer(); - - /// setup the renderer - void setup(const GfxSetup& setup, const gfxPointers& ptrs); - /// discard the renderer - void discard(); - /// return true if renderer has been setup - bool isValid() const; - - /// reset the internal state cache - void resetStateCache(); - /// test if a feature is supported - bool queryFeature(GfxFeature::Code feat) const; - /// commit current frame - void commitFrame(); - /// get the current render pass attributes - const DisplayAttrs& renderPassAttrs() const; - - /// begin rendering pass (both ptrs can be nullptr) - void beginPass(renderPass* pass, const PassAction* action); - /// end current rendering pass - void endPass(); - - /// apply viewport - void applyViewPort(int x, int y, int width, int height, bool originTopLeft); - /// apply scissor rect - void applyScissorRect(int x, int y, int width, int height, bool originTopLeft); - /// apply draw state - void applyDrawState(pipeline* pip, mesh** meshes, int numMeshes); - /// apply a shader uniform block - void applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t typeHash, const uint8_t* ptr, int byteSize); - /// apply a texture block - void applyTextures(ShaderStage::Code bindStage, texture** textures, int numTextures); - - /// submit a draw call with primitive group index in current mesh - void draw(int primGroupIndex, int numInstances); - /// submit a draw call with direct primitive group - void draw(int baseElementIndex, int numElements, int numInstances); - - /// update vertex data - void updateVertices(mesh* msh, const void* data, int numBytes); - /// update index data - void updateIndices(mesh* msh, const void* data, int numBytes); - /// update texture data - void updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes); - - /// check if command buffer exists, create if not - void checkCreateCommandBuffer(); - - #if ORYOL_MACOS - static const int MtlUniformAlignment = 256; - #else - static const int MtlUniformAlignment = 16; - #endif - - bool valid; - GfxSetup gfxSetup; - gfxPointers pointers; - - int frameIndex; - int curFrameRotateIndex; - - bool rpValid; - DisplayAttrs rpAttrs; - - pipeline* curPipeline; - mesh* curPrimaryMesh; - unsigned long curMTLPrimitiveType; - unsigned long curMTLIndexType; - - ORYOL_OBJC_TYPED_ID(MTLDevice) mtlDevice; - ORYOL_OBJC_TYPED_ID(MTLCommandQueue) commandQueue; - ORYOL_OBJC_TYPED_ID(MTLCommandBuffer) curCommandBuffer; - ORYOL_OBJC_TYPED_ID(MTLRenderCommandEncoder) curRenderCmdEncoder; - - // rotated global uniform buffers - uint8_t* curUniformBufferPtr; - int curUniformBufferOffset; - StaticArray uniformBuffers; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlRenderer.mm b/code/Modules/Gfx/private/mtl/mtlRenderer.mm deleted file mode 100644 index 4114fa7e8..000000000 --- a/code/Modules/Gfx/private/mtl/mtlRenderer.mm +++ /dev/null @@ -1,655 +0,0 @@ -//------------------------------------------------------------------------------ -// mtlRenderer.mm -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "mtlRenderer.h" -#include "mtlTypes.h" -#include "Gfx/private/displayMgr.h" -#include "Gfx/private/resourcePools.h" -#include "Gfx/private/resource.h" -#include "Gfx/GfxTypes.h" - -namespace Oryol { -namespace _priv { - -dispatch_semaphore_t mtlInflightSemaphore; - -//------------------------------------------------------------------------------ -mtlRenderer::mtlRenderer() : -valid(false), -frameIndex(0), -curFrameRotateIndex(0), -rpValid(false), -curPipeline(nullptr), -curPrimaryMesh(nullptr), -curMTLPrimitiveType(MTLPrimitiveTypeTriangle), -curMTLIndexType(MTLIndexTypeUInt16), -mtlDevice(nil), -commandQueue(nil), -curCommandBuffer(nil), -curRenderCmdEncoder(nil), -curUniformBufferPtr(nullptr), -curUniformBufferOffset(0) { - // empty -} - -//------------------------------------------------------------------------------ -mtlRenderer::~mtlRenderer() { - o_assert_dbg(!this->valid); -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::setup(const GfxSetup& setup, const gfxPointers& ptrs) { - o_assert_dbg(!this->valid); - - this->valid = true; - this->pointers = ptrs; - this->gfxSetup = setup; - - // frame-sync semaphore - mtlInflightSemaphore = dispatch_semaphore_create(GfxConfig::MaxInflightFrames); - - // setup central metal objects - this->mtlDevice = osBridge::ptr()->mtlDevice; - this->commandQueue = [this->mtlDevice newCommandQueue]; - - // create global rotated uniform buffers - for (int i = 0; i < GfxConfig::MaxInflightFrames; i++) { - this->uniformBuffers[i] = [this->mtlDevice - newBufferWithLength:setup.GlobalUniformBufferSize - options:mtlTypes::asBufferResourceOptions(Usage::Stream)]; - } - this->curFrameRotateIndex = 0; - this->curUniformBufferOffset = 0; -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::discard() { - o_assert_dbg(this->valid); - - // wait for the final frame to finish - for (int i = 0; i < GfxConfig::MaxInflightFrames; i++) { - dispatch_semaphore_wait(mtlInflightSemaphore, DISPATCH_TIME_FOREVER); - } - for (int i = 0; i < GfxConfig::MaxInflightFrames; i++) { - this->uniformBuffers[i] = nil; - } - this->commandQueue = nil; - this->mtlDevice = nil; - this->pointers = gfxPointers(); - this->valid = false; -} - -//------------------------------------------------------------------------------ -bool -mtlRenderer::isValid() const { - return this->valid; -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::resetStateCache() { - o_assert_dbg(this->valid); - o_warn("mtlRenderer::resetStateCache()\n"); -} - -//------------------------------------------------------------------------------ -bool -mtlRenderer::queryFeature(GfxFeature::Code feat) const { - switch(feat) { - #if ORYOL_MACOS - case GfxFeature::TextureCompressionDXT: - #else - case GfxFeature::TextureCompressionPVRTC: - #endif - case GfxFeature::TextureFloat: - case GfxFeature::Instancing: - case GfxFeature::OriginTopLeft: - case GfxFeature::MSAARenderTargets: - case GfxFeature::PackedVertexFormat_10_2: - case GfxFeature::MultipleRenderTarget: - case GfxFeature::Texture3D: - case GfxFeature::TextureArray: - return true; - default: - return false; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::commitFrame() { - o_assert_dbg(this->valid); - o_assert_dbg(nil == this->curRenderCmdEncoder); - o_assert_dbg(nil != this->curCommandBuffer); - - this->rpValid = false; - - // commit the global uniform buffer updates - #if ORYOL_MACOS - [this->uniformBuffers[this->curFrameRotateIndex] didModifyRange:NSMakeRange(0, this->curUniformBufferOffset)]; - #endif - [this->curCommandBuffer presentDrawable:[osBridge::ptr()->mtkView currentDrawable]]; - __block dispatch_semaphore_t blockSema = mtlInflightSemaphore; - [this->curCommandBuffer addCompletedHandler:^(id buffer) { - dispatch_semaphore_signal(blockSema); - }]; - [this->curCommandBuffer commit]; - - // rotate to next uniform buffer - if (++this->curFrameRotateIndex >= GfxConfig::MaxInflightFrames) { - this->curFrameRotateIndex = 0; - } - - this->frameIndex++; - this->curUniformBufferOffset = 0; - this->curRenderCmdEncoder = nil; - this->curCommandBuffer = nil; - this->curUniformBufferPtr = nullptr; - this->curPipeline = nullptr; - this->curPrimaryMesh = nullptr; -} - -//------------------------------------------------------------------------------ -const DisplayAttrs& -mtlRenderer::renderPassAttrs() const { - o_assert_dbg(this->valid); - return this->rpAttrs; -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::applyViewPort(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->valid); - if (nil == this->curRenderCmdEncoder) { - return; - } - - MTLViewport vp; - vp.originX = (double) x; - vp.originY = (double) (originTopLeft ? y : (this->rpAttrs.FramebufferHeight - (y + height))); - vp.width = (double) width; - vp.height = (double) height; - vp.znear = 0.0; - vp.zfar = 1.0; - [this->curRenderCmdEncoder setViewport:vp]; -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::applyScissorRect(int x, int y, int width, int height, bool originTopLeft) { - o_assert_dbg(this->valid); - o_assert_dbg(width >= 0); - o_assert_dbg(height >= 0); - - if (nil == this->curRenderCmdEncoder) { - return; - } - - // clip against frame buffer size - x = std::min(std::max(0, x), this->rpAttrs.FramebufferWidth - 1); - y = std::min(std::max(0, y), this->rpAttrs.FramebufferHeight - 1); - if ((x + width) > this->rpAttrs.FramebufferWidth) { - width = this->rpAttrs.FramebufferWidth - x; - } - if ((y + height) > this->rpAttrs.FramebufferHeight) { - height = this->rpAttrs.FramebufferHeight - y; - } - if (width <= 0) { - width = 1; - } - if (height <= 0) { - height = 1; - } - - MTLScissorRect rect; - rect.x = x; - rect.y = originTopLeft ? y : this->rpAttrs.FramebufferHeight - (y + height); - rect.width = width; - rect.height = height; - - // need to clip against render target - [this->curRenderCmdEncoder setScissorRect:rect]; -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::checkCreateCommandBuffer() { - o_assert_dbg(this->valid); - if (nil == this->curCommandBuffer) { - // block until the oldest frame in flight has finished - dispatch_semaphore_wait(mtlInflightSemaphore, DISPATCH_TIME_FOREVER); - // get a new command buffer - this->curCommandBuffer = [this->commandQueue commandBufferWithUnretainedReferences]; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::beginPass(renderPass* pass, const PassAction* action) { - o_assert_dbg(this->valid); - o_assert_dbg(nil == this->curRenderCmdEncoder); - o_assert_dbg(action); - - // create command buffer if this is the first call in the current frame - this->checkCreateCommandBuffer(); - - // get the base pointer for the uniform buffer, this only happens once per frame - if (nullptr == this->curUniformBufferPtr) { - this->curUniformBufferPtr = (uint8*)[this->uniformBuffers[this->curFrameRotateIndex] contents]; - } - - // default, or offscreen render target? - MTLRenderPassDescriptor* passDesc = nil; - if (nullptr == pass) { - // default render target - passDesc = [osBridge::ptr()->mtkView currentRenderPassDescriptor]; - this->rpAttrs = this->pointers.displayMgr->GetDisplayAttrs(); - } - else { - passDesc = [MTLRenderPassDescriptor renderPassDescriptor]; - o_assert_dbg(pass->colorTextures[0]); - this->rpAttrs = DisplayAttrs::FromTextureAttrs(pass->colorTextures[0]->textureAttrs); - } - if (passDesc) { - this->rpValid = true; - } - else { - // pass descriptor will not be valid if window is minimized - return; - } - - // initialize renderpass descriptor - if (pass) { - // offscreen, might be multiple-rendertarget - for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { - if (pass->colorTextures[i]) { - const auto& colorAtt = pass->Setup.ColorAttachments[i]; - bool isMSAA = pass->colorTextures[i]->textureAttrs.SampleCount > 1; - passDesc.colorAttachments[i].loadAction = mtlTypes::asLoadAction(action, i, false); - passDesc.colorAttachments[i].storeAction = isMSAA ? MTLStoreActionMultisampleResolve:MTLStoreActionStore; - const glm::vec4& c = action->Color[i]; - passDesc.colorAttachments[i].clearColor = MTLClearColorMake(c.x, c.y, c.z, c.w); - if (isMSAA) { - // render to MSAA render target... - o_assert_dbg(pass->colorTextures[i]->mtlMSAATex); - passDesc.colorAttachments[i].texture = pass->colorTextures[i]->mtlMSAATex; - passDesc.colorAttachments[i].resolveTexture = pass->colorTextures[i]->mtlTextures[0]; - passDesc.colorAttachments[i].resolveLevel = colorAtt.MipLevel; - } - else { - // render to non-MSAA render target... - passDesc.colorAttachments[i].texture = pass->colorTextures[i]->mtlTextures[0]; - passDesc.colorAttachments[i].level = colorAtt.MipLevel; - } - switch (pass->colorTextures[i]->textureAttrs.Type) { - case TextureType::TextureCube: - case TextureType::TextureArray: - passDesc.colorAttachments[i].slice = pass->Setup.ColorAttachments[i].Slice; - break; - case TextureType::Texture3D: - passDesc.colorAttachments[i].depthPlane = pass->Setup.ColorAttachments[i].Slice; - break; - default: - break; - } - } - } - if (PixelFormat::IsDepthFormat(this->rpAttrs.DepthPixelFormat)) { - passDesc.depthAttachment.texture = pass->depthStencilTexture->mtlDepthTex; - passDesc.depthAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.depthAttachment.clearDepth = action->Depth; - } - else if (PixelFormat::IsDepthStencilFormat(this->rpAttrs.DepthPixelFormat)) { - passDesc.depthAttachment.texture = pass->depthStencilTexture->mtlDepthTex; - passDesc.depthAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.depthAttachment.clearDepth = action->Depth; - passDesc.stencilAttachment.texture = pass->depthStencilTexture->mtlDepthTex; - passDesc.stencilAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.stencilAttachment.clearStencil = action->Stencil; - } - } - else { - // default framebuffer - passDesc.colorAttachments[0].loadAction = mtlTypes::asLoadAction(action, 0, false); - const glm::vec4& c = action->Color[0]; - passDesc.colorAttachments[0].clearColor = MTLClearColorMake(c.x, c.y, c.z, c.w); - if (PixelFormat::IsDepthFormat(this->gfxSetup.DepthFormat)) { - passDesc.depthAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.depthAttachment.clearDepth = action->Depth; - } - else if (PixelFormat::IsDepthStencilFormat(this->gfxSetup.DepthFormat)) { - passDesc.depthAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.depthAttachment.clearDepth = action->Depth; - passDesc.stencilAttachment.loadAction = mtlTypes::asLoadAction(action, 0, true); - passDesc.stencilAttachment.clearStencil = action->Stencil; - } - } - - // create command encoder for this render pass - // this might return nil if the window is minimized - this->curRenderCmdEncoder = [this->curCommandBuffer renderCommandEncoderWithDescriptor:passDesc]; - if (nil != this->curRenderCmdEncoder) { - for (int bindSlot = 0; bindSlot < GfxConfig::MaxNumUniformBlocksPerStage; bindSlot++) { - [this->curRenderCmdEncoder - setVertexBuffer:this->uniformBuffers[this->curFrameRotateIndex] - offset:0 - atIndex:bindSlot]; - [this->curRenderCmdEncoder - setFragmentBuffer:this->uniformBuffers[this->curFrameRotateIndex] - offset:0 - atIndex:bindSlot]; - } - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::endPass() { - o_assert_dbg(this->valid); - if (nil != this->curRenderCmdEncoder) { - [this->curRenderCmdEncoder endEncoding]; - this->curRenderCmdEncoder = nil; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::applyDrawState(pipeline* pip, mesh** meshes, int numMeshes) { - o_assert_dbg(this->valid); - o_assert_dbg(pip); - o_assert_dbg(meshes && (numMeshes > 0)); - - if (nil == this->curRenderCmdEncoder) { - return; - } - // if any of the meshes are still loading, cancel the next draw-call - for (int i = 0; i < numMeshes; i++) { - if (nullptr == meshes[i]) { - this->curPipeline = nullptr; - return; - } - } - o_assert_dbg(pip->mtlRenderPipelineState); - o_assert_dbg(pip->mtlDepthStencilState); - o_assert2_dbg(pip->Setup.BlendState.ColorFormat == this->rpAttrs.ColorPixelFormat, "ColorFormat in BlendState must match current render target!\n"); - o_assert2_dbg(pip->Setup.BlendState.DepthFormat == this->rpAttrs.DepthPixelFormat, "DepthFormat in BlendSTate must match current render target!\n"); - o_assert2_dbg(pip->Setup.RasterizerState.SampleCount == this->rpAttrs.SampleCount, "SampleCount in RasterizerState must match current render target!\n"); - - // need to store draw state and first mesh for later draw call - this->curPipeline = pip; - this->curPrimaryMesh = meshes[0]; - o_assert_dbg(this->curPrimaryMesh); - - // apply general state - const glm::vec4& bc = pip->Setup.BlendColor; - const RasterizerState& rs = pip->Setup.RasterizerState; - const DepthStencilState& dss = pip->Setup.DepthStencilState; - [this->curRenderCmdEncoder setBlendColorRed:bc.x green:bc.y blue:bc.z alpha:bc.w]; - [this->curRenderCmdEncoder setCullMode:mtlTypes::asCullMode(rs.CullFaceEnabled, rs.CullFace)]; - [this->curRenderCmdEncoder setStencilReferenceValue:dss.StencilRef]; - - // apply state objects - [this->curRenderCmdEncoder setRenderPipelineState:pip->mtlRenderPipelineState]; - [this->curRenderCmdEncoder setDepthStencilState:pip->mtlDepthStencilState]; - - // apply vertex buffers - for (int meshIndex = 0; meshIndex < GfxConfig::MaxNumInputMeshes; meshIndex++) { - const mesh* msh = meshIndex < numMeshes ? meshes[meshIndex] : nullptr; - // NOTE: vertex buffers are located after constant buffers - const int vbSlotIndex = meshIndex + GfxConfig::MaxNumUniformBlocksPerStage; - if (msh) { - // note: vb.mtlBuffers[vb.activeSlot] can be nil! - const auto& vb = msh->buffers[mesh::vb]; - [this->curRenderCmdEncoder setVertexBuffer:vb.mtlBuffers[vb.activeSlot] offset:0 atIndex:vbSlotIndex]; - } - else { - [this->curRenderCmdEncoder setVertexBuffer:nil offset:0 atIndex:vbSlotIndex]; - } - } - - // store additional state for following draw calls - this->curMTLPrimitiveType = mtlTypes::asPrimitiveType(pip->Setup.PrimType); - if (this->curPrimaryMesh->indexBufferAttrs.Type != IndexType::None) { - this->curMTLIndexType = mtlTypes::asIndexType(this->curPrimaryMesh->indexBufferAttrs.Type); - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::applyUniformBlock(ShaderStage::Code bindStage, int bindSlot, uint32_t typeHash, const uint8_t* ptr, int byteSize) { - o_assert_dbg(this->valid); - if (nil == this->curRenderCmdEncoder) { - return; - } - if (nullptr == this->curPipeline) { - return; - } - - #if ORYOL_DEBUG - // check whether the provided struct is type-compatible with the uniform layout - shader* shd = this->curPipeline->shd; - o_assert_dbg(shd); - int ubIndex = shd->Setup.UniformBlockIndexByStageAndSlot(bindStage, bindSlot); - o_assert(InvalidIndex != ubIndex); - const uint32_t ubTypeHash = shd->Setup.UniformBlockTypeHash(ubIndex); - const int ubByteSize = shd->Setup.UniformBlockByteSize(ubIndex); - o_assert(ubTypeHash == typeHash); - o_assert(ubByteSize >= byteSize); - o_assert2_dbg((this->curUniformBufferOffset + byteSize) <= this->gfxSetup.GlobalUniformBufferSize, "Global uniform buffer exhausted!\n"); - o_assert_dbg((this->curUniformBufferOffset & (MtlUniformAlignment-1)) == 0); - #endif - - // write uniforms into global uniform buffer, advance buffer offset - // and set current uniform buffer location on command-encoder - // NOTE: we'll call didModifyRange only ONCE inside commitFrame! - uint8* dstPtr = this->curUniformBufferPtr + this->curUniformBufferOffset; - std::memcpy(dstPtr, ptr, byteSize); - if (ShaderStage::VS == bindStage) { - [this->curRenderCmdEncoder setVertexBufferOffset:this->curUniformBufferOffset atIndex:bindSlot]; - } - else { - [this->curRenderCmdEncoder setFragmentBufferOffset:this->curUniformBufferOffset atIndex:bindSlot]; - } - this->curUniformBufferOffset = Memory::RoundUp(this->curUniformBufferOffset + byteSize, MtlUniformAlignment); -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::applyTextures(ShaderStage::Code bindStage, texture** textures, int numTextures) { - o_assert_dbg(this->valid); - o_assert_dbg(((ShaderStage::VS == bindStage) && (numTextures <= GfxConfig::MaxNumVertexTextures)) || - ((ShaderStage::FS == bindStage) && (numTextures <= GfxConfig::MaxNumFragmentTextures))); - if (nil == this->curRenderCmdEncoder) { - return; - } - if (nullptr == this->curPipeline) { - return; - } - - // if any of the texture pointers is null, this means the texture hasn't loaded - // yet or has failed loading, in this case, disable the next draw call - for (int i = 0; i < numTextures; i++) { - if (nullptr == textures[i]) { - this->curPipeline = nullptr; - return; - } - } - - // apply textures and samplers - if (ShaderStage::VS == bindStage) { - for (int i = 0; i < numTextures; i++) { - texture* tex = textures[i]; - [this->curRenderCmdEncoder setVertexTexture:tex->mtlTextures[tex->activeSlot] atIndex:i]; - [this->curRenderCmdEncoder setVertexSamplerState:tex->mtlSamplerState atIndex:i]; - } - } - else { - for (int i = 0; i < numTextures; i++) { - texture* tex = textures[i]; - [this->curRenderCmdEncoder setFragmentTexture:tex->mtlTextures[tex->activeSlot] atIndex:i]; - [this->curRenderCmdEncoder setFragmentSamplerState:tex->mtlSamplerState atIndex:i]; - } - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::draw(int baseElementIndex, int numElements, int numInstances) { - o_assert_dbg(this->valid); - if (nil == this->curRenderCmdEncoder) { - return; - } - if (nullptr == this->curPipeline) { - return; - } - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - if (IndexType::None == msh->indexBufferAttrs.Type) { - [this->curRenderCmdEncoder drawPrimitives:(MTLPrimitiveType)this->curMTLPrimitiveType - vertexStart:baseElementIndex - vertexCount:numElements - instanceCount:numInstances]; - } - else { - const auto& ib = msh->buffers[mesh::ib]; - o_assert_dbg(nil != ib.mtlBuffers[ib.activeSlot]); - NSUInteger indexBufferOffset = baseElementIndex * IndexType::ByteSize(msh->indexBufferAttrs.Type); - [this->curRenderCmdEncoder drawIndexedPrimitives:(MTLPrimitiveType)this->curMTLPrimitiveType - indexCount:numElements - indexType:(MTLIndexType)this->curMTLIndexType - indexBuffer:ib.mtlBuffers[ib.activeSlot] - indexBufferOffset:indexBufferOffset - instanceCount:numInstances ]; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::draw(int primGroupIndex, int numInstances) { - if (nil == this->curRenderCmdEncoder) { - return; - } - if (nullptr == this->curPipeline) { - return; - } - const mesh* msh = this->curPrimaryMesh; - o_assert_dbg(msh); - if (primGroupIndex >= msh->numPrimGroups) { - // this may happen if rendering a placeholder which doesn't have - // as many materials as the original mesh - return; - } - const PrimitiveGroup& primGroup = msh->primGroups[primGroupIndex]; - this->draw(primGroup.BaseElement, primGroup.NumElements, numInstances); -} - -//------------------------------------------------------------------------------ -void -meshBufferRotateActiveSlot(mesh::buffer& buf, int frameIndex) { - // helper function to get the right double-buffered - // vertex or index buffer for a buffer update - - // restrict buffer updates to once per frame per mesh, this isn't - // strictly required on GL, but we want the same restrictions across all 3D APIs - o_assert2(buf.updateFrameIndex != frameIndex, "Only one data update allowed per buffer and frame!\n"); - buf.updateFrameIndex = frameIndex; - - // if usage is streaming, rotate slot index to next dynamic vertex buffer - // to implement double/multi-buffering because the previous buffer - // might still be in-flight on the GPU - o_assert_dbg(buf.numSlots > 1); - if (++buf.activeSlot >= buf.numSlots) { - buf.activeSlot = 0; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::updateVertices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != msh); - o_assert_dbg(nullptr != data); - o_assert_dbg((numBytes > 0) && (numBytes <= msh->vertexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->vertexBufferAttrs.BufferUsage); - - auto& vb = msh->buffers[mesh::vb]; - meshBufferRotateActiveSlot(vb, this->frameIndex); - o_assert_dbg(nil != vb.mtlBuffers[vb.activeSlot]); - o_assert_dbg(numBytes <= int([vb.mtlBuffers[vb.activeSlot] length])); - void* dstPtr = [vb.mtlBuffers[vb.activeSlot] contents]; - std::memcpy(dstPtr, data, numBytes); - #if ORYOL_MACOS - [vb.mtlBuffers[vb.activeSlot] didModifyRange:NSMakeRange(0, numBytes)]; - #endif -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::updateIndices(mesh* msh, const void* data, int numBytes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != msh); - o_assert_dbg(nullptr != data); - o_assert_dbg((numBytes > 0) && (numBytes <= msh->indexBufferAttrs.ByteSize())); - o_assert_dbg(Usage::Immutable != msh->indexBufferAttrs.BufferUsage); - - auto& ib = msh->buffers[mesh::ib]; - meshBufferRotateActiveSlot(ib, this->frameIndex); - o_assert_dbg(nil != ib.mtlBuffers[ib.activeSlot]); - o_assert_dbg(numBytes <= int([ib.mtlBuffers[ib.activeSlot] length])); - void* dstPtr = [ib.mtlBuffers[ib.activeSlot] contents]; - std::memcpy(dstPtr, data, numBytes); - #if ORYOL_MACOS - [ib.mtlBuffers[ib.activeSlot] didModifyRange:NSMakeRange(0, numBytes)]; - #endif -} - -//------------------------------------------------------------------------------ -void -texRotateActiveSlot(texture* tex, int frameIndex) { - o_assert2(tex->updateFrameIndex != frameIndex, "Only one data update allowed per texture and frame!\n"); - tex->updateFrameIndex = frameIndex; - o_assert_dbg(tex->numSlots > 1); - if (++tex->activeSlot >= tex->numSlots) { - tex->activeSlot = 0; - } -} - -//------------------------------------------------------------------------------ -void -mtlRenderer::updateTexture(texture* tex, const void* data, const ImageDataAttrs& offsetsAndSizes) { - o_assert_dbg(this->valid); - o_assert_dbg(nullptr != tex); - o_assert_dbg(nullptr != data); - - const TextureAttrs& attrs = tex->textureAttrs; - o_assert_dbg(TextureType::Texture2D == attrs.Type); - o_assert_dbg(Usage::Immutable != attrs.TextureUsage); - o_assert_dbg(!PixelFormat::IsCompressedFormat(attrs.ColorFormat)); - o_assert_dbg(offsetsAndSizes.NumMipMaps <= attrs.NumMipMaps); - o_assert_dbg(offsetsAndSizes.NumFaces == 1); - - texRotateActiveSlot(tex, this->frameIndex); - o_assert_dbg(nil != tex->mtlTextures[tex->activeSlot]); - - // copy data bytes into texture - const uint8* srcPtr = (const uint8*) data; - for (int mipIndex = 0; mipIndex < attrs.NumMipMaps; mipIndex++) { - int mipWidth = std::max(attrs.Width >> mipIndex, 1); - int mipHeight = std::max(attrs.Height >> mipIndex, 1); - // special case PVRTC formats: bytesPerRow must be 0 - int bytesPerRow = PixelFormat::RowPitch(attrs.ColorFormat, mipWidth); - MTLRegion region = MTLRegionMake2D(0, 0, mipWidth, mipHeight); - [tex->mtlTextures[tex->activeSlot] replaceRegion:region - mipmapLevel:mipIndex - slice:0 - withBytes:srcPtr+offsetsAndSizes.Offsets[0][mipIndex] - bytesPerRow:bytesPerRow - bytesPerImage:0]; - } -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlResource.h b/code/Modules/Gfx/private/mtl/mtlResource.h deleted file mode 100644 index a7d9e0df3..000000000 --- a/code/Modules/Gfx/private/mtl/mtlResource.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -#include "Gfx/private/resourceBase.h" -#include "Gfx/private/mtl/mtl_decl.h" -#include "Core/Containers/StaticArray.h" -#include "Gfx/GfxConfig.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlMesh - @ingroup _priv - @brief Metal implementation of class mesh -*/ -class mtlMesh : public meshBase { -public: - /// destructor - ~mtlMesh(); - - /// clear the object (called from meshFactory::DestroyResource()) - void Clear(); - - static const int NumSlots = GfxConfig::MaxInflightFrames; - struct buffer { - buffer(); - int updateFrameIndex; - uint8_t numSlots; - uint8_t activeSlot; - StaticArray mtlBuffers; - }; - // indices into buffers array (first entry is vertex buffers, second entry is index buffers - static const int vb = 0; - static const int ib = 1; - StaticArray buffers; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlPipeline - @ingroup _priv - @brief Metal implementation of class pipeline -*/ -class mtlPipeline : public pipelineBase { -public: - /// constructor - mtlPipeline(); - /// destructor - ~mtlPipeline(); - - /// clear the object (called from pipelineFactory::DestroyResource()) - void Clear(); - - /// render-pipeline state - ORYOL_OBJC_TYPED_ID(MTLRenderPipelineState) mtlRenderPipelineState; - /// depth-stencil state - ORYOL_OBJC_TYPED_ID(MTLDepthStencilState) mtlDepthStencilState; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlShader - @ingroup _priv - @brief Metal implementation of class shader -*/ -class mtlShader : public shaderBase { -public: - /// constructor - mtlShader(); - /// destructor - ~mtlShader(); - - /// clear the object - void Clear(); - - ORYOL_OBJC_TYPED_ID(MTLLibrary) mtlVertexShaderLibrary; - ORYOL_OBJC_TYPED_ID(MTLLibrary) mtlFragmentShaderLibrary; - ORYOL_OBJC_TYPED_ID(MTLFunction) mtlVertexShader; - ORYOL_OBJC_TYPED_ID(MTLFunction) mtlFragmentShader; - - /// bind slots for vertex attributes (InvalidIndex if not used) - StaticArray vsAttrIndices; - -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlTexture - @ingroup _priv - @brief Metal implementation of class texture -*/ -class mtlTexture : public textureBase { -public: - /// constructor - mtlTexture(); - /// destructor - ~mtlTexture(); - - /// clear the object - void Clear(); - - static const int NumSlots = GfxConfig::MaxInflightFrames; - int updateFrameIndex; - uint8_t numSlots; - uint8_t activeSlot; - StaticArray mtlTextures; - - ORYOL_OBJC_TYPED_ID(MTLSamplerState) mtlSamplerState; - ORYOL_OBJC_TYPED_ID(MTLTexture) mtlDepthTex; - ORYOL_OBJC_TYPED_ID(MTLTexture) mtlMSAATex; -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlRenderPass - @ingroup _priv - @brief Metal implementation of renderPass -*/ -class mtlRenderPass : public renderPassBase { - // empty -}; - -} // namespace _priv -} // namespace Oryol - - diff --git a/code/Modules/Gfx/private/mtl/mtlResource.mm b/code/Modules/Gfx/private/mtl/mtlResource.mm deleted file mode 100644 index 9db5b873e..000000000 --- a/code/Modules/Gfx/private/mtl/mtlResource.mm +++ /dev/null @@ -1,122 +0,0 @@ -//------------------------------------------------------------------------------ -// mtlResource.mm -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "mtl_impl.h" -#include "mtlResource.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -mtlMesh::buffer::buffer() : -updateFrameIndex(-1), -numSlots(1), -activeSlot(0) { - this->mtlBuffers.Fill(nil); -} - -//------------------------------------------------------------------------------ -mtlMesh::~mtlMesh() { - #if ORYOL_DEBUG - for (const auto& buf : this->buffers) { - for (int i = 0; i < NumSlots; i++) { - o_assert_dbg(buf.mtlBuffers[i] == nil); - } - } - #endif -} - -//------------------------------------------------------------------------------ -void -mtlMesh::Clear() { - for (auto& buf : this->buffers) { - buf = buffer(); - } - meshBase::Clear(); -} - -//------------------------------------------------------------------------------ -mtlPipeline::mtlPipeline() : -mtlRenderPipelineState(nil), -mtlDepthStencilState(nil) { - // empty -} - -//------------------------------------------------------------------------------ -mtlPipeline::~mtlPipeline() { - o_assert_dbg(nil == this->mtlRenderPipelineState); - o_assert_dbg(nil == this->mtlDepthStencilState); -} - -//------------------------------------------------------------------------------ -void -mtlPipeline::Clear() { - this->mtlRenderPipelineState = nil; - this->mtlDepthStencilState = nil; - pipelineBase::Clear(); -} - -//------------------------------------------------------------------------------ -mtlShader::mtlShader() { - this->Clear(); -} - -//------------------------------------------------------------------------------ -mtlShader::~mtlShader() { -#if ORYOL_DEBUG - o_assert_dbg(nil == this->mtlVertexShaderLibrary); - o_assert_dbg(nil == this->mtlFragmentShaderLibrary); - o_assert_dbg(nil == this->mtlVertexShader); - o_assert_dbg(nil == this->mtlFragmentShader); -#endif -} - -//------------------------------------------------------------------------------ -void -mtlShader::Clear() { - this->mtlVertexShaderLibrary = nil; - this->mtlFragmentShaderLibrary = nil; - this->mtlVertexShader = nil; - this->mtlFragmentShader = nil; - this->vsAttrIndices.Fill(InvalidIndex); - shaderBase::Clear(); -} - -//------------------------------------------------------------------------------ -mtlTexture::mtlTexture() : -updateFrameIndex(-1), -numSlots(1), -activeSlot(0), -mtlSamplerState(nil), -mtlDepthTex(nil) { - this->mtlTextures.Fill(nil); -} - -//------------------------------------------------------------------------------ -mtlTexture::~mtlTexture() { - o_assert_dbg(nil == this->mtlSamplerState); - o_assert_dbg(nil == this->mtlDepthTex); - #if ORYOL_DEBUG - for (int i = 0; i < NumSlots; i++) { - o_assert_dbg(this->mtlTextures[i] == nil); - } - #endif -} - -//------------------------------------------------------------------------------ -void -mtlTexture::Clear() { - textureBase::Clear(); - this->updateFrameIndex = -1; - this->numSlots = 1; - this->activeSlot = 0; - this->mtlTextures.Fill(nil); - this->mtlSamplerState = nil; - this->mtlDepthTex = nil; - this->mtlMSAATex = nil; -} - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/mtl/mtlTypes.h b/code/Modules/Gfx/private/mtl/mtlTypes.h deleted file mode 100644 index 09b21a465..000000000 --- a/code/Modules/Gfx/private/mtl/mtlTypes.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::mtlTypes - @ingroup _priv - @brief Oryol to Metal type mapping - - NOTE: only include this file from a Objective-C(++) file! -*/ -#include "Gfx/GfxTypes.h" -#include "Gfx/private/mtl/mtl_decl.h" - -#if !defined(__OBJC__) -#error "mtlTypes.h: Must be included from Obj-C source!" -#endif - -namespace Oryol { -namespace _priv { - -class mtlTypes { -public: - /// convert color render target pixel format - static MTLPixelFormat asRenderTargetColorFormat(PixelFormat::Code fmt); - /// convert depth render target pixel format - static MTLPixelFormat asRenderTargetDepthFormat(PixelFormat::Code fmt); - /// convert stencil render target pixel format - static MTLPixelFormat asRenderTargetStencilFormat(PixelFormat::Code fmt); - /// convert texture pixel format - static MTLPixelFormat asTextureFormat(PixelFormat::Code fmt); - /// convert texture type - static MTLTextureType asTextureType(TextureType::Code type); - /// convert texture address mode - static MTLSamplerAddressMode asSamplerAddressMode(TextureWrapMode::Code mode); - /// convert to sampler min/mag filter - static MTLSamplerMinMagFilter asSamplerMinMagFilter(TextureFilterMode::Code f); - /// convert to sampler mip filter - static MTLSamplerMipFilter asSamplerMipFilter(TextureFilterMode::Code f); - /// convert usage to MTLResourceOptions bit mask - static MTLResourceOptions asBufferResourceOptions(Usage::Code usage); - /// convert compare function enum value - static MTLCompareFunction asCompareFunc(CompareFunc::Code cmp); - /// convert stencil operation enum value - static MTLStencilOperation asStencilOp(StencilOp::Code op); - /// convert color-write-mask - static MTLColorWriteMask asColorWriteMask(PixelChannel::Mask mask); - /// convert blend operation - static MTLBlendOperation asBlendOp(BlendOperation::Code op); - /// convert blend factor - static MTLBlendFactor asBlendFactor(BlendFactor::Code factor); - /// convert vertex format - static MTLVertexFormat asVertexFormat(VertexFormat::Code fmt); - /// convert vertex step function - static MTLVertexStepFunction asVertexStepFunc(VertexStepFunction::Code func); - /// convert cullmode - static MTLCullMode asCullMode(bool cullEnabled, Face::Code face); - /// convert vertex index type - static MTLIndexType asIndexType(IndexType::Code c); - /// convert primitive type - static MTLPrimitiveType asPrimitiveType(PrimitiveType::Code c); - /// convert render pass load action from PassAction flags - static MTLLoadAction asLoadAction(const PassAction* action, int colorIndex, bool depthStencil); -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/mtl/mtlTypes.mm b/code/Modules/Gfx/private/mtl/mtlTypes.mm deleted file mode 100644 index e22c93ca2..000000000 --- a/code/Modules/Gfx/private/mtl/mtlTypes.mm +++ /dev/null @@ -1,359 +0,0 @@ -//------------------------------------------------------------------------------ -// mtlTypes.mm -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "mtlTypes.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -MTLPixelFormat -mtlTypes::asRenderTargetColorFormat(PixelFormat::Code fmt) { - // see the capability table for the list of renderable pixel formats: - // https://developer.apple.com/library/prerelease/ios/documentation/Metal/Reference/MetalConstants_Ref/index.html - // - switch (fmt) { - case PixelFormat::RGBA8: - // this is a bit of a hack since the default backbuffer format is BGRA8 - return MTLPixelFormatBGRA8Unorm; - case PixelFormat::RGBA32F: - return MTLPixelFormatRGBA32Float; - case PixelFormat::RGBA16F: - return MTLPixelFormatRGBA16Float; - case PixelFormat::R10G10B10A2: - return MTLPixelFormatRGB10A2Unorm; - default: - return MTLPixelFormatInvalid; - } -} - -//------------------------------------------------------------------------------ -MTLPixelFormat -mtlTypes::asRenderTargetDepthFormat(PixelFormat::Code fmt) { - switch (fmt) { - case PixelFormat::DEPTH: - return MTLPixelFormatDepth32Float; - case PixelFormat::DEPTHSTENCIL: - // NOTE: MTLPixelFormatDepth24Unorm_Stencil8 isn't universally supported, - // instead always use Depth32Float_Stencil8 - return MTLPixelFormatDepth32Float_Stencil8; - default: - return MTLPixelFormatInvalid; - } -} - -//------------------------------------------------------------------------------ -MTLPixelFormat -mtlTypes::asRenderTargetStencilFormat(PixelFormat::Code fmt) { - switch (fmt) { - case PixelFormat::DEPTHSTENCIL: - return MTLPixelFormatDepth32Float_Stencil8; - default: - return MTLPixelFormatInvalid; - } -} - -//------------------------------------------------------------------------------ -MTLPixelFormat -mtlTypes::asTextureFormat(PixelFormat::Code fmt) { - switch (fmt) { - case PixelFormat::RGBA8: return MTLPixelFormatRGBA8Unorm; - case PixelFormat::R10G10B10A2: return MTLPixelFormatRGB10A2Unorm; - case PixelFormat::RGBA32F: return MTLPixelFormatRGBA32Float; - case PixelFormat::RGBA16F: return MTLPixelFormatRGBA16Float; - case PixelFormat::L8: return MTLPixelFormatR8Unorm; - #if ORYOL_MACOS - case PixelFormat::DXT1: return MTLPixelFormatBC1_RGBA; - case PixelFormat::DXT3: return MTLPixelFormatBC2_RGBA; - case PixelFormat::DXT5: return MTLPixelFormatBC3_RGBA; - #elif ORYOL_IOS - case PixelFormat::PVRTC2_RGB: return MTLPixelFormatPVRTC_RGB_2BPP; - case PixelFormat::PVRTC2_RGBA: return MTLPixelFormatPVRTC_RGBA_2BPP; - case PixelFormat::PVRTC4_RGB: return MTLPixelFormatPVRTC_RGB_4BPP; - case PixelFormat::PVRTC4_RGBA: return MTLPixelFormatPVRTC_RGBA_4BPP; - #endif - default: return MTLPixelFormatInvalid; - } -} - -//------------------------------------------------------------------------------ -MTLTextureType -mtlTypes::asTextureType(TextureType::Code type) { - switch (type) { - case TextureType::Texture2D: return MTLTextureType2D; - case TextureType::Texture3D: return MTLTextureType3D; - case TextureType::TextureCube: return MTLTextureTypeCube; - case TextureType::TextureArray: return MTLTextureType2DArray; - default: - o_error("mtlTypes::asTextureType(): invalid value!\n"); - return MTLTextureType1D; - } -} - -//------------------------------------------------------------------------------ -MTLSamplerAddressMode -mtlTypes::asSamplerAddressMode(TextureWrapMode::Code mode) { - switch (mode) { - case TextureWrapMode::ClampToEdge: return MTLSamplerAddressModeClampToEdge; - case TextureWrapMode::Repeat: return MTLSamplerAddressModeRepeat; - case TextureWrapMode::MirroredRepeat: return MTLSamplerAddressModeMirrorRepeat; - default: - o_error("mtlTypes::asSamplerAddressMode(): invalid value!\n"); - return MTLSamplerAddressModeRepeat; - } -} - -//------------------------------------------------------------------------------ -MTLSamplerMinMagFilter -mtlTypes::asSamplerMinMagFilter(TextureFilterMode::Code f) { - switch (f) { - case TextureFilterMode::Nearest: - case TextureFilterMode::NearestMipmapNearest: - case TextureFilterMode::NearestMipmapLinear: - return MTLSamplerMinMagFilterNearest; - case TextureFilterMode::Linear: - case TextureFilterMode::LinearMipmapNearest: - case TextureFilterMode::LinearMipmapLinear: - return MTLSamplerMinMagFilterLinear; - default: - o_error("mtlTypes::asSamplerMinMagFilter(): invalid value!\n"); - return MTLSamplerMinMagFilterNearest; - } -} - -//------------------------------------------------------------------------------ -MTLSamplerMipFilter -mtlTypes::asSamplerMipFilter(TextureFilterMode::Code f) { - switch (f) { - case TextureFilterMode::Nearest: - case TextureFilterMode::Linear: - return MTLSamplerMipFilterNotMipmapped; - case TextureFilterMode::NearestMipmapNearest: - case TextureFilterMode::LinearMipmapNearest: - return MTLSamplerMipFilterNearest; - case TextureFilterMode::NearestMipmapLinear: - case TextureFilterMode::LinearMipmapLinear: - return MTLSamplerMipFilterLinear; - default: - o_error("mtlTypes::asSamplerMipFilter(): invalid value!\n"); - return MTLSamplerMipFilterNotMipmapped; - } -} - -//------------------------------------------------------------------------------ -MTLResourceOptions -mtlTypes::asBufferResourceOptions(Usage::Code usage) { - switch (usage) { - case Usage::Immutable: return MTLResourceStorageModeShared; - // FIXME: are these the right dynamic access flags? - #if ORYOL_MACOS - case Usage::Dynamic: return MTLResourceCPUCacheModeWriteCombined | MTLResourceStorageModeManaged; - case Usage::Stream: return MTLResourceCPUCacheModeWriteCombined | MTLResourceStorageModeManaged; - #else - case Usage::Dynamic: return MTLResourceCPUCacheModeWriteCombined; - case Usage::Stream: return MTLResourceCPUCacheModeWriteCombined; - #endif - default: - o_error("mtlTypes::asBufferResourceOptions(): invalid value!\n"); - return 0; - } -} - -//------------------------------------------------------------------------------ -MTLCompareFunction -mtlTypes::asCompareFunc(CompareFunc::Code cmp) { - switch (cmp) { - case CompareFunc::Never: return MTLCompareFunctionNever; - case CompareFunc::Less: return MTLCompareFunctionLess; - case CompareFunc::Equal: return MTLCompareFunctionEqual; - case CompareFunc::LessEqual: return MTLCompareFunctionLessEqual; - case CompareFunc::Greater: return MTLCompareFunctionGreater; - case CompareFunc::NotEqual: return MTLCompareFunctionNotEqual; - case CompareFunc::GreaterEqual: return MTLCompareFunctionGreaterEqual; - case CompareFunc::Always: return MTLCompareFunctionAlways; - default: - o_error("mtlTypes::asCompareFunc(): invalid value!\n"); - return MTLCompareFunctionAlways; - } -} - -//------------------------------------------------------------------------------ -MTLStencilOperation -mtlTypes::asStencilOp(StencilOp::Code op) { - switch (op) { - case StencilOp::Keep: return MTLStencilOperationKeep; - case StencilOp::Zero: return MTLStencilOperationZero; - case StencilOp::Replace: return MTLStencilOperationReplace; - case StencilOp::IncrClamp: return MTLStencilOperationIncrementClamp; - case StencilOp::DecrClamp: return MTLStencilOperationDecrementClamp; - case StencilOp::Invert: return MTLStencilOperationInvert; - case StencilOp::IncrWrap: return MTLStencilOperationIncrementWrap; - case StencilOp::DecrWrap: return MTLStencilOperationDecrementWrap; - default: - o_error("mtlTypes::asStencilOp(): invalid value!\n"); - return MTLStencilOperationKeep; - } -} - -//------------------------------------------------------------------------------ -MTLColorWriteMask -mtlTypes::asColorWriteMask(PixelChannel::Mask mask) { - MTLColorWriteMask mtlMask = MTLColorWriteMaskNone; - if (mask & PixelChannel::Red) { - mtlMask |= MTLColorWriteMaskRed; - } - if (mask & PixelChannel::Green) { - mtlMask |= MTLColorWriteMaskGreen; - } - if (mask & PixelChannel::Blue) { - mtlMask |= MTLColorWriteMaskBlue; - } - if (mask & PixelChannel::Alpha) { - mtlMask |= MTLColorWriteMaskAlpha; - } - return mtlMask; -} - -//------------------------------------------------------------------------------ -MTLBlendOperation -mtlTypes::asBlendOp(BlendOperation::Code op) { - switch (op) { - case BlendOperation::Add: return MTLBlendOperationAdd; - case BlendOperation::Subtract: return MTLBlendOperationSubtract; - case BlendOperation::ReverseSubtract: return MTLBlendOperationReverseSubtract; - default: - o_error("mtlTypes::asBlendOp(): invalid value!\n"); - return MTLBlendOperationAdd; - } -} - -//------------------------------------------------------------------------------ -MTLBlendFactor -mtlTypes::asBlendFactor(BlendFactor::Code factor) { - switch (factor) { - case BlendFactor::Zero: return MTLBlendFactorZero; - case BlendFactor::One: return MTLBlendFactorOne; - case BlendFactor::SrcColor: return MTLBlendFactorSourceColor; - case BlendFactor::OneMinusSrcColor: return MTLBlendFactorOneMinusSourceColor; - case BlendFactor::SrcAlpha: return MTLBlendFactorSourceAlpha; - case BlendFactor::OneMinusSrcAlpha: return MTLBlendFactorOneMinusSourceAlpha; - case BlendFactor::DstColor: return MTLBlendFactorDestinationColor; - case BlendFactor::OneMinusDstColor: return MTLBlendFactorOneMinusDestinationColor; - case BlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha; - case BlendFactor::OneMinusDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha; - case BlendFactor::SrcAlphaSaturated: return MTLBlendFactorSourceAlphaSaturated; - case BlendFactor::BlendColor: return MTLBlendFactorBlendColor; - case BlendFactor::OneMinusBlendColor: return MTLBlendFactorOneMinusBlendColor; - case BlendFactor::BlendAlpha: return MTLBlendFactorBlendAlpha; - case BlendFactor::OneMinusBlendAlpha: return MTLBlendFactorOneMinusBlendAlpha; - default: - o_error("mtlTypes::asBlendFactor(): invalid value!\n"); - return MTLBlendFactorOne; - } -} - -//------------------------------------------------------------------------------ -MTLVertexFormat -mtlTypes::asVertexFormat(VertexFormat::Code fmt) { - switch (fmt) { - case VertexFormat::Float: return MTLVertexFormatFloat; - case VertexFormat::Float2: return MTLVertexFormatFloat2; - case VertexFormat::Float3: return MTLVertexFormatFloat3; - case VertexFormat::Float4: return MTLVertexFormatFloat4; - case VertexFormat::Byte4: return MTLVertexFormatChar4; - case VertexFormat::Byte4N: return MTLVertexFormatChar4Normalized; - case VertexFormat::UByte4: return MTLVertexFormatUChar4; - case VertexFormat::UByte4N: return MTLVertexFormatUChar4Normalized; - case VertexFormat::Short2: return MTLVertexFormatShort2; - case VertexFormat::Short2N: return MTLVertexFormatShort2Normalized; - case VertexFormat::Short4: return MTLVertexFormatShort4; - case VertexFormat::Short4N: return MTLVertexFormatShort4Normalized; - case VertexFormat::UInt10_2N: return MTLVertexFormatUInt1010102Normalized; - default: - o_error("mtlTypes::asVertexFormat(): invalid value!\n"); - return MTLVertexFormatFloat; - } -} - -//------------------------------------------------------------------------------ -MTLVertexStepFunction -mtlTypes::asVertexStepFunc(VertexStepFunction::Code func) { - switch (func) { - case VertexStepFunction::PerVertex: return MTLVertexStepFunctionPerVertex; - case VertexStepFunction::PerInstance: return MTLVertexStepFunctionPerInstance; - default: - o_error("mtlTypes::asVertexStepFunc(): invalid value!\n"); - return MTLVertexStepFunctionPerVertex; - } -} - -//------------------------------------------------------------------------------ -MTLCullMode -mtlTypes::asCullMode(bool cullEnabled, Face::Code face) { - if (cullEnabled) { - return (Face::Front == face) ? MTLCullModeFront : MTLCullModeBack; - } - else { - return MTLCullModeNone; - } -} - -//------------------------------------------------------------------------------ -MTLIndexType -mtlTypes::asIndexType(IndexType::Code c) { - switch (c) { - case IndexType::Index16: return MTLIndexTypeUInt16; - case IndexType::Index32: return MTLIndexTypeUInt32; - default: - o_error("mtlTypes::asIndexType(): invalid value!\n"); - return MTLIndexTypeUInt16; - } -} - -//------------------------------------------------------------------------------ -MTLPrimitiveType -mtlTypes::asPrimitiveType(PrimitiveType::Code c) { - switch (c) { - case PrimitiveType::Points: return MTLPrimitiveTypePoint; - case PrimitiveType::Lines: return MTLPrimitiveTypeLine; - case PrimitiveType::LineStrip: return MTLPrimitiveTypeLineStrip; - case PrimitiveType::Triangles: return MTLPrimitiveTypeTriangle; - case PrimitiveType::TriangleStrip: return MTLPrimitiveTypeTriangleStrip; - default: - o_error("mtlTypes::asPrimitiveType(): invalid value!\n"); - return MTLPrimitiveTypePoint; - } -} - -//------------------------------------------------------------------------------ -MTLLoadAction -mtlTypes::asLoadAction(const PassAction* action, int colorIndex, bool depthStencil) { - o_assert_dbg(action); - o_assert_range_dbg(colorIndex, GfxConfig::MaxNumColorAttachments); - if (depthStencil) { - if (action->Flags & PassAction::ClearDS) { - return MTLLoadActionClear; - } - else if (action->Flags & PassAction::LoadDS) { - return MTLLoadActionLoad; - } - else { - return MTLLoadActionDontCare; - } - } - else { - if (action->Flags & (PassAction::ClearC0<Flags & (PassAction::LoadC0<Setup = ShaderSetup(); -} - -//------------------------------------------------------------------------------ -void -textureBase::Clear() { - this->Setup = TextureSetup(); - this->textureAttrs = TextureAttrs(); -} - -//------------------------------------------------------------------------------ -void -meshBase::Clear() { - this->Setup = MeshSetup(); - this->vertexBufferAttrs = VertexBufferAttrs(); - this->indexBufferAttrs = IndexBufferAttrs(); - this->primGroups.Fill(PrimitiveGroup()); - this->numPrimGroups = 0; -} - -//------------------------------------------------------------------------------ -void -pipelineBase::Clear() { - this->Setup = PipelineSetup(); - this->shd = nullptr; -} - -//------------------------------------------------------------------------------ -void -renderPassBase::Clear() { - this->Setup = PassSetup(); - this->colorTextures.Fill(nullptr); - this->depthStencilTexture = nullptr; -} - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/resourceBase.h b/code/Modules/Gfx/private/resourceBase.h deleted file mode 100644 index 9cd9f609f..000000000 --- a/code/Modules/Gfx/private/resourceBase.h +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @file Gfx/Resource/resourceBase.h - @brief Gfx module resource classes -*/ -#include "Resource/ResourceBase.h" -#include "Gfx/GfxTypes.h" - -namespace Oryol { -namespace _priv { - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::shaderBase - @ingroup _priv - @brief shader resource base class -*/ -class shaderBase : public ResourceBase { -public: - /// the original setup object - ShaderSetup Setup; - /// clear the object - void Clear(); -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::textureBase - @ingroup _priv - @brief base class for platform-specific texture implementation -*/ -class textureBase : public ResourceBase { -public: - /// the original setup object - TextureSetup Setup; - /// texture attributes - TextureAttrs textureAttrs; - /// was created from native texture handles - bool nativeHandles = false; - /// clear the object - void Clear(); -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::meshBase - @ingroup _priv - @brief base class for platform-specific mesh implementations -*/ -class meshBase : public ResourceBase { -public: - /// the original setup object - MeshSetup Setup; - /// vertex buffer attributes - VertexBufferAttrs vertexBufferAttrs; - /// index buffer attributes - IndexBufferAttrs indexBufferAttrs; - /// number of primitive groups - int numPrimGroups = 0; - /// primitive groups - StaticArray primGroups; - /// clear the object - void Clear(); -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::pipelineBase - @ingroup _priv - @brief base class for pipeline implementations -*/ -class shader; -class pipelineBase : public ResourceBase { -public: - /// the original setup object - PipelineSetup Setup; - /// shader pointer - shader* shd = nullptr; - /// clear the object - void Clear(); -}; - -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::renderPassBase - @ingroup _priv - @brief base class for render-pass implementations -*/ -class texture; -class renderPassBase : public ResourceBase { -public: - /// constructor - renderPassBase() { - colorTextures.Fill(nullptr); - } - /// the original setup object - PassSetup Setup; - /// color texture pointers - StaticArray colorTextures; - /// depth-stencil texture pointer - texture* depthStencilTexture = nullptr; - /// clear the object - void Clear(); -}; - -} // namespace _priv -} // namespace Oryol - diff --git a/code/Modules/Gfx/private/resourcePools.h b/code/Modules/Gfx/private/resourcePools.h deleted file mode 100644 index e47d36abe..000000000 --- a/code/Modules/Gfx/private/resourcePools.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @file Gfx/Resource/resourcePools.h - @ingroup _priv - @brief Gfx module resource pool classes -*/ -#include "Resource/ResourcePool.h" -#include "Gfx/private/resource.h" - -namespace Oryol { -namespace _priv { - -class pipelinePool : public ResourcePool { }; -class meshPool : public ResourcePool { }; -class shaderPool : public ResourcePool { }; -class texturePool : public ResourcePool { }; -class renderPassPool : public ResourcePool { }; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/sokol/sokolGfxBackend.cc b/code/Modules/Gfx/private/sokol/sokolGfxBackend.cc new file mode 100644 index 000000000..ab84948bf --- /dev/null +++ b/code/Modules/Gfx/private/sokol/sokolGfxBackend.cc @@ -0,0 +1,1038 @@ +//------------------------------------------------------------------------------ +// sokolGfxBackend.cc +//------------------------------------------------------------------------------ +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif +#include "Pre.h" +#include "Core/Core.h" +#include "Core/Assertion.h" +#include "Core/Memory/Memory.h" +#include "sokolGfxBackend.h" + +namespace Oryol { +namespace _priv { + +//------------------------------------------------------------------------------ +static Id makeId(GfxResourceType::Code type, uint32_t sgId) { + // convert a Sokol resource id into a Oryol Id + Id::SlotIndexT slotIndex = sgId & 0xFFFF; + Id::UniqueStampT unique = (sgId >> 16) & 0xFFFF; + Id id(unique, slotIndex, type); + return id; +} + +//------------------------------------------------------------------------------ +static sg_shader makeShaderId(const Id& id) { + // convert an Oryol Id into a sokol sg_shader + o_assert_dbg(id.Type == GfxResourceType::Shader); + uint32_t sgId = (id.UniqueStamp<<16)|id.SlotIndex; + return sg_shader{sgId}; +} + +//------------------------------------------------------------------------------ +static sg_pipeline makePipelineId(const Id& id) { + // convert an Oryol Id into a sokol sg_pipeline + o_assert_dbg(id.Type == GfxResourceType::Pipeline); + uint32_t sgId = (id.UniqueStamp<<16)|id.SlotIndex; + return sg_pipeline{sgId}; +} + +//------------------------------------------------------------------------------ +static sg_buffer makeBufferId(const Id& id) { + // convert an Oryol Id into a sokol sg_buffer + o_assert_dbg(id.Type == GfxResourceType::Buffer); + uint32_t sgId = (id.UniqueStamp<<16)|id.SlotIndex; + return sg_buffer{sgId}; +} + +//------------------------------------------------------------------------------ +static sg_image makeImageId(const Id& id) { + // convert an Oryol Id into a sokol sg_image + o_assert_dbg(id.Type == GfxResourceType::Texture); + uint32_t sgId = (id.UniqueStamp<<16)|id.SlotIndex; + return sg_image{sgId}; +} + +//------------------------------------------------------------------------------ +static sg_pass makePassId(const Id& id) { + // convert an Oryol Id into a sokol sg_pass + o_assert_dbg(id.Type == GfxResourceType::Pass); + uint32_t sgId = (id.UniqueStamp<<16)|id.SlotIndex; + return sg_pass{sgId}; +} + +//------------------------------------------------------------------------------ +static void convertPassAction(const PassAction& src, sg_pass_action& dst) { + o_assert_dbg(GfxConfig::MaxNumColorAttachments <= SG_MAX_COLOR_ATTACHMENTS); + for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { + for (int c = 0; c < 4; c++) { + dst.colors[i].val[c] = src.Color[i][c]; + } + if (src.Flags & (PassAction::ClearC0< 0)) { + dst.content.subimage[f][m].ptr = src.Content.Pointer[f][m]; + dst.content.subimage[f][m].size = src.Content.Size[f][m]; + } + } + } + o_assert_dbg(GfxConfig::MaxInflightFrames <= SG_NUM_INFLIGHT_FRAMES); + #if ORYOL_OPENGL + for (int i = 0; i < GfxConfig::MaxInflightFrames; i++) { + dst.gl_textures[i] = (uint32_t) src.NativeTextures[i]; + } + #elif ORYOL_METAL + for (int i = 0; i < GfxConfig::MaxInflightFrames; i++) { + dst.mtl_textures[i] = (const void*) src.NativeTextures[i]; + } + #elif ORYOL_D3D11 + dst.d3d11_texture = (const void*) src.NativeTextures[0]; + #endif +} + +//------------------------------------------------------------------------------ +sokolGfxBackend::~sokolGfxBackend() { + o_assert(!this->isValid); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::Setup(const GfxDesc& desc) { + o_assert(!this->isValid); + this->displayManager.SetupDisplay(desc); + + // setup sokol-gfx + sg_desc sgDesc = { }; + sgDesc.buffer_pool_size = desc.ResourcePoolSize[GfxResourceType::Buffer]; + sgDesc.image_pool_size = desc.ResourcePoolSize[GfxResourceType::Texture]; + sgDesc.shader_pool_size = desc.ResourcePoolSize[GfxResourceType::Shader]; + sgDesc.pipeline_pool_size = desc.ResourcePoolSize[GfxResourceType::Pipeline]; + sgDesc.pass_pool_size = desc.ResourcePoolSize[GfxResourceType::Pass]; + #if ORYOL_OPENGLES3 + sgDesc.gl_force_gles2 = this->displayManager.useGLES2; + #elif ORYOL_METAL + sgDesc.mtl_device = mtlDisplayMgr::mtlDevice(); + sgDesc.mtl_renderpass_descriptor_cb = mtlDisplayMgr::mtlRenderPassDescriptor; + sgDesc.mtl_drawable_cb = mtlDisplayMgr::mtlDrawable; + sgDesc.mtl_global_uniform_buffer_size = desc.GlobalUniformBufferSize; + #elif ORYOL_D3D11 + sgDesc.d3d11_device = this->displayManager.d3d11Device; + sgDesc.d3d11_device_context = this->displayManager.d3d11DeviceContext; + sgDesc.d3d11_render_target_view_cb = d3d11DisplayMgr::d3d11GetRenderTargetView; + sgDesc.d3d11_depth_stencil_view_cb = d3d11DisplayMgr::d3d11GetDepthStencilView; + #endif + sg_setup(&sgDesc); + + this->registry.Setup(desc.ResourceRegistryCapacity); + this->labelStack.Setup(desc.ResourceLabelStackCapacity); + this->toDestroy.Reserve(64); + this->vsInputs.Reserve(desc.ResourcePoolSize[GfxResourceType::Shader]); + for (int i = 0; i < this->vsInputs.Capacity(); i++) { + this->vsInputs.Add(VertexLayout()); + } + this->isValid = true; +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::Discard() { + o_assert(this->isValid); + this->registry.Discard(); + this->labelStack.Discard(); + sg_shutdown(); + this->displayManager.DiscardDisplay(); + this->isValid = false; +} + +//------------------------------------------------------------------------------ +bool +sokolGfxBackend::IsValid() { + return this->isValid; +} + +//------------------------------------------------------------------------------ +bool +sokolGfxBackend::QuitRequested() { + return this->displayManager.QuitRequested(); +} + +//------------------------------------------------------------------------------ +bool +sokolGfxBackend::QueryFeature(GfxFeature::Code feature) { + o_assert_dbg(this->isValid); + return sg_query_feature(convertFeature(feature)); +} + +//------------------------------------------------------------------------------ +ShaderLang::Code +sokolGfxBackend::QueryShaderLang() { + o_assert_dbg(this->isValid); + ShaderLang::Code slang = ShaderLang::Invalid; + #if ORYOL_OPENGL_CORE_PROFILE + slang = ShaderLang::GLSL330; + #elif ORYOL_OPENGLES2 + slang = ShaderLang::GLSL100; + #elif ORYOL_OPENGLES3 + if (this->displayManager.useGLES2) { + slang = ShaderLang::GLSL100; + } + else { + slang = ShaderLang::GLSLES3; + } + #elif ORYOL_METAL + slang = ShaderLang::Metal; + #elif ORYOL_D3D11 + slang = ShaderLang::HLSL5; + #else + #error("Unknown Platform") + #endif + return slang; +} + +//------------------------------------------------------------------------------ +ResourceState::Code +sokolGfxBackend::QueryResourceState(const Id& id) { + o_assert_dbg(this->isValid); + sg_resource_state sgState = SG_RESOURCESTATE_INVALID; + switch (id.Type) { + case GfxResourceType::Texture: + sgState = sg_query_image_state(makeImageId(id)); + break; + case GfxResourceType::Buffer: + sgState = sg_query_buffer_state(makeBufferId(id)); + break; + case GfxResourceType::Shader: + sgState = sg_query_shader_state(makeShaderId(id)); + break; + case GfxResourceType::Pipeline: + sgState = sg_query_pipeline_state(makePipelineId(id)); + break; + case GfxResourceType::Pass: + sgState = sg_query_pass_state(makePassId(id)); + break; + default: + break; + } + switch (sgState) { + case SG_RESOURCESTATE_INITIAL: return ResourceState::Initial; + case SG_RESOURCESTATE_ALLOC: return ResourceState::Alloc; + case SG_RESOURCESTATE_VALID: return ResourceState::Valid; + case SG_RESOURCESTATE_FAILED: return ResourceState::Failed; + default: return ResourceState::InvalidState; + } +} + +//------------------------------------------------------------------------------ +GfxEvent::HandlerId +sokolGfxBackend::Subscribe(GfxEvent::Handler handler) { + o_assert_dbg(this->isValid); + return this->displayManager.Subscribe(handler); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::Unsubscribe(GfxEvent::HandlerId id) { + o_assert_dbg(this->isValid); + this->displayManager.Unsubscribe(id); +} + +//------------------------------------------------------------------------------ +ResourceLabel +sokolGfxBackend::PushResourceLabel() { + o_assert_dbg(this->isValid); + return this->labelStack.PushLabel(); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::PushResourceLabel(ResourceLabel label) { + o_assert_dbg(this->isValid); + return this->labelStack.PushLabel(label); +} + +//------------------------------------------------------------------------------ +ResourceLabel +sokolGfxBackend::PopResourceLabel() { + o_assert_dbg(this->isValid); + return this->labelStack.PopLabel(); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::CreateBuffer(const BufferDesc& desc) { + o_assert_dbg(this->isValid); + sg_buffer_desc sgDesc = { }; + convertBufferDesc(desc, sgDesc); + return makeId(GfxResourceType::Buffer, sg_make_buffer(&sgDesc).id); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::AllocBuffer() { + o_assert_dbg(this->isValid); + return makeId(GfxResourceType::Buffer, sg_alloc_buffer().id); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::InitBuffer(const Id& id, const BufferDesc& desc) { + o_assert_dbg(this->isValid); + sg_buffer_desc sgDesc = { }; + convertBufferDesc(desc, sgDesc); + sg_init_buffer(makeBufferId(id), &sgDesc); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::FailBuffer(const Id& id) { + o_assert_dbg(this->isValid); + sg_fail_buffer(makeBufferId(id)); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::CreateTexture(const TextureDesc& desc) { + o_assert_dbg(this->isValid); + sg_image_desc sgDesc = { }; + convertTextureDesc(desc, sgDesc); + return makeId(GfxResourceType::Texture, sg_make_image(&sgDesc).id); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::AllocTexture() { + o_assert_dbg(this->isValid); + return makeId(GfxResourceType::Texture, sg_alloc_image().id); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::InitTexture(const Id& id, const TextureDesc& desc) { + o_assert_dbg(this->isValid); + sg_image_desc sgDesc = { }; + convertTextureDesc(desc, sgDesc); + sg_init_image(makeImageId(id), &sgDesc); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::FailTexture(const Id& id) { + o_assert_dbg(this->isValid); + sg_fail_image(makeImageId(id)); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::CreateShader(const ShaderDesc& desc) { + o_assert_dbg(this->isValid); + sg_shader_desc sgDesc = { }; + + // set source- or byte-code, and optional entry function + #if ORYOL_OPENGL + sgDesc.vs.source = desc.Stage[ShaderStage::VS].Source; + sgDesc.fs.source = desc.Stage[ShaderStage::FS].Source; + #elif ORYOL_METAL || ORYOL_D3D11 + sgDesc.vs.byte_code = desc.Stage[ShaderStage::VS].ByteCode; + sgDesc.vs.byte_code_size = desc.Stage[ShaderStage::VS].ByteCodeSize; + sgDesc.fs.byte_code = desc.Stage[ShaderStage::FS].ByteCode; + sgDesc.fs.byte_code_size = desc.Stage[ShaderStage::FS].ByteCodeSize; + #endif + if (desc.Stage[ShaderStage::VS].Entry) { + sgDesc.vs.entry = desc.Stage[ShaderStage::VS].Entry; + } + if (desc.Stage[ShaderStage::FS].Entry) { + sgDesc.fs.entry = desc.Stage[ShaderStage::FS].Entry; + } + + // uniform block declarations + o_assert_dbg(GfxConfig::MaxNumUniformBlocksPerStage <= SG_MAX_SHADERSTAGE_UBS); + int vsUbIndex = 0; + int fsUbIndex = 0; + for (int stageIndex = 0; stageIndex < ShaderStage::Num; stageIndex++) { + for (int ubIndex = 0; ubIndex < GfxConfig::MaxNumUniformBlocksPerStage; ubIndex++) { + auto& src = desc.Stage[stageIndex].UniformBlocks[ubIndex]; + if (src.Size > 0) { + sg_shader_uniform_block_desc* dst = nullptr; + if (stageIndex == ShaderStage::VS) { + dst = &sgDesc.vs.uniform_blocks[vsUbIndex++]; + } + else { + dst = &sgDesc.fs.uniform_blocks[fsUbIndex++]; + } + dst->size = src.Size; + // uniform block members are only defined on OpenGL, Metal + #if ORYOL_OPENGL + // size must be a multiple of 16 (sizeof(vec4)) + o_assert_dbg((src.Size & 15) == 0); + dst->uniforms[0].name = src.Type; + dst->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + dst->uniforms[0].array_count = src.Size / 16; + #endif + } + } + } + + // texture declarations + o_assert_dbg(GfxConfig::MaxNumShaderTextures <= SG_MAX_SHADERSTAGE_IMAGES); + int vsImgIndex = 0; + int fsImgIndex = 0; + for (int stageIndex = 0; stageIndex < ShaderStage::Num; stageIndex++) { + for (int texIndex = 0; texIndex < GfxConfig::MaxNumShaderTextures; texIndex++) { + auto& src = desc.Stage[stageIndex].Textures[texIndex]; + if (src.Type != TextureType::Invalid) { + sg_shader_image_desc* dst = nullptr; + if (stageIndex == ShaderStage::VS) { + dst = &sgDesc.vs.images[vsImgIndex++]; + } + else { + dst = &sgDesc.fs.images[fsImgIndex++]; + } + dst->type = convertTextureType(src.Type); + dst->name = src.Name; + } + } + } + Id shd = makeId(GfxResourceType::Shader, sg_make_shader(&sgDesc).id); + + // keep track of the shader's vertex layout + o_assert_dbg(!desc.Layout.Empty()); + o_assert_dbg(this->vsInputs[shd.SlotIndex].Empty()); + this->vsInputs[shd.SlotIndex] = desc.Layout; + + return shd; +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::CreatePipeline(const PipelineDesc& desc) { + o_assert_dbg(this->isValid); + o_assert_dbg(desc.Shader.IsValid()); + + // lookup the shader vertex shader input layout + const VertexLayout& vsLayout = this->vsInputs[desc.Shader.SlotIndex]; + o_assert_dbg(!vsLayout.Empty()); + + sg_pipeline_desc sgDesc = { }; + sgDesc.shader = makeShaderId(desc.Shader); + sgDesc.primitive_type = convertPrimitiveType(desc.PrimType); + sgDesc.index_type = convertIndexType(desc.IndexType); + convertVertexLayouts(desc, sgDesc, vsLayout); + convertDepthStencilState(desc, sgDesc); + convertBlendState(desc, sgDesc); + convertRasterizerState(desc, sgDesc); + return makeId(GfxResourceType::Pipeline, sg_make_pipeline(&sgDesc).id); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::CreatePass(const PassDesc& desc) { + o_assert_dbg(this->isValid); + o_assert_dbg(GfxConfig::MaxNumColorAttachments <= SG_MAX_COLOR_ATTACHMENTS); + sg_pass_desc sgDesc = { }; + for (int i = 0; i < GfxConfig::MaxNumColorAttachments; i++) { + const auto& src = desc.ColorAttachments[i]; + if (src.Texture.IsValid()) { + auto& dst = sgDesc.color_attachments[i]; + dst.image = makeImageId(src.Texture); + dst.mip_level = src.MipLevel; + dst.layer = src.Layer; + } + } + const auto& src = desc.DepthStencilAttachment; + if (src.Texture.IsValid()) { + auto& dst = sgDesc.depth_stencil_attachment; + dst.image = makeImageId(src.Texture); + dst.mip_level = src.MipLevel; + dst.layer = src.Layer; + } + return makeId(GfxResourceType::Pass, sg_make_pass(&sgDesc).id); +} + +//------------------------------------------------------------------------------ +Id +sokolGfxBackend::LookupResource(const Locator& loc) { + o_assert_dbg(this->isValid); + return this->registry.Lookup(loc); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::AddResource(const Locator& loc, const Id& id) { + o_assert_dbg(this->isValid); + this->registry.Add(loc, id, this->labelStack.PeekLabel()); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::DestroyResources(ResourceLabel label) { + o_assert_dbg(this->isValid); + this->toDestroy = this->registry.Remove(label); + for (const Id& id : this->toDestroy) { + switch (id.Type) { + case GfxResourceType::Buffer: + sg_destroy_buffer(makeBufferId(id)); + break; + case GfxResourceType::Texture: + sg_destroy_image(makeImageId(id)); + break; + case GfxResourceType::Shader: + sg_destroy_shader(makeShaderId(id)); + this->vsInputs[id.SlotIndex].Clear(); + break; + case GfxResourceType::Pipeline: + sg_destroy_pipeline(makePipelineId(id)); + break; + case GfxResourceType::Pass: + sg_destroy_pass(makePassId(id)); + break; + default: + break; + } + } +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::UpdateBuffer(const Id& id, const void* data, int numBytes) { + o_assert_dbg(this->isValid); + sg_update_buffer(makeBufferId(id), data, numBytes); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::UpdateTexture(const Id& id, const ImageContent& srcContent) { + o_assert_dbg(this->isValid); + o_assert_dbg(GfxConfig::MaxNumTextureFaces <= SG_CUBEFACE_NUM); + o_assert_dbg(GfxConfig::MaxNumTextureMipMaps <= SG_MAX_MIPMAPS); + sg_image_content sgContent = { }; + for (int faceIndex = 0; faceIndex < GfxConfig::MaxNumTextureFaces; faceIndex++) { + for (int mipIndex = 0; mipIndex < GfxConfig::MaxNumTextureMipMaps; mipIndex++) { + if (srcContent.Pointer[faceIndex][mipIndex] && (srcContent.Size[faceIndex][mipIndex] > 0)) { + auto& dst = sgContent.subimage[faceIndex][mipIndex]; + dst.size = srcContent.Size[faceIndex][mipIndex]; + dst.ptr = srcContent.Pointer[faceIndex][mipIndex]; + } + else { + break; + } + } + } + sg_update_image(makeImageId(id), &sgContent); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::BeginPass(const Id& passId, const PassAction* action) { + o_assert_dbg(this->isValid); + o_assert_dbg(action); + sg_pass_action sgAction = { }; + convertPassAction(*action, sgAction); + if (passId.IsValid()) { + // offscreen framebuffer + sg_begin_pass(makePassId(passId), &sgAction); + } + else { + // default framebuffer + const DisplayAttrs& attrs = this->displayManager.GetDisplayAttrs(); + sg_begin_default_pass(&sgAction, attrs.Width, attrs.Height); + } +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::EndPass() { + o_assert_dbg(this->isValid); + sg_end_pass(); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ApplyViewPort(int x, int y, int w, int h, bool originTopLeft) { + o_assert_dbg(this->isValid); + sg_apply_viewport(x, y, w, h, originTopLeft); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ApplyScissorRect(int x, int y, int w, int h, bool originTopLeft) { + o_assert_dbg(this->isValid); + sg_apply_scissor_rect(x, y, w, h, originTopLeft); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ApplyPipeline(const Id& pipId) { + o_assert_dbg(this->isValid); + sg_apply_pipeline(makePipelineId(pipId)); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ApplyBindings(const Bindings& bindings) { + o_assert_dbg(this->isValid); + o_assert_dbg(SG_MAX_SHADERSTAGE_BUFFERS >= GfxConfig::MaxNumVertexBuffers); + o_assert_dbg(SG_MAX_SHADERSTAGE_IMAGES >= GfxConfig::MaxNumVertexTextures); + o_assert_dbg(SG_MAX_SHADERSTAGE_IMAGES >= GfxConfig::MaxNumFragmentTextures); + sg_bindings sgBindings = { }; + for (int i = 0; i < GfxConfig::MaxNumVertexBuffers; i++) { + if (bindings.VertexBuffers[i].IsValid()) { + sgBindings.vertex_buffers[i] = makeBufferId(bindings.VertexBuffers[i]); + sgBindings.vertex_buffer_offsets[i] = bindings.VertexBufferOffsets[i]; + } + else { + break; + } + } + if (bindings.IndexBuffer.IsValid()) { + sgBindings.index_buffer = makeBufferId(bindings.IndexBuffer); + sgBindings.index_buffer_offset = bindings.IndexBufferOffset; + } + for (int i = 0; i < GfxConfig::MaxNumVertexTextures; i++) { + if (bindings.VSTexture[i].IsValid()) { + sgBindings.vs_images[i] = makeImageId(bindings.VSTexture[i]); + } + else { + break; + } + } + for (int i = 0; i < GfxConfig::MaxNumFragmentTextures; i++) { + if (bindings.FSTexture[i].IsValid()) { + sgBindings.fs_images[i] = makeImageId(bindings.FSTexture[i]); + } + else { + break; + } + } + sg_apply_bindings(&sgBindings); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ApplyUniforms(ShaderStage::Code stage, int ubIndex, const void* data, int numBytes) { + o_assert_dbg(this->isValid); + sg_shader_stage sgStage = (stage==ShaderStage::VS) ? SG_SHADERSTAGE_VS : SG_SHADERSTAGE_FS; + sg_apply_uniform_block(sgStage, ubIndex, data, numBytes); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::Draw(int baseElement, int numElements, int numInstances) { + o_assert_dbg(this->isValid); + sg_draw(baseElement, numElements, numInstances); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::CommitFrame() { + o_assert_dbg(this->isValid); + sg_commit(); + this->displayManager.Present(); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ResetStateCache() { + o_assert_dbg(this->isValid); + sg_reset_state_cache(); +} + +//------------------------------------------------------------------------------ +void +sokolGfxBackend::ProcessSystemEvents() { + o_assert_dbg(this->isValid); + this->displayManager.ProcessSystemEvents(); +} + +} // namespace _priv +} // namespace Oryol +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + diff --git a/code/Modules/Gfx/private/sokol/sokolGfxBackend.h b/code/Modules/Gfx/private/sokol/sokolGfxBackend.h new file mode 100644 index 000000000..86853e77f --- /dev/null +++ b/code/Modules/Gfx/private/sokol/sokolGfxBackend.h @@ -0,0 +1,124 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + @class Oryol::_priv::sokolGfxBackend + @ingroup _priv + @brief graphics backend implementation on top of sokol_gfx.h +*/ +#include "Gfx/GfxTypes.h" +#include "Gfx/private/displayMgr.h" +#include "Resource/ResourceLabel.h" +#include "Resource/ResourceRegistry.h" +#include "Resource/ResourceLabelStack.h" +#include "Resource/ResourceState.h" +#include "sokol_gfx.h" + +namespace Oryol { +namespace _priv { + +class sokolGfxBackend { +public: + /// destructor + ~sokolGfxBackend(); + + /// setup the Gfx backend + void Setup(const GfxDesc& desc); + /// shutdown the Gfx backend + void Discard(); + /// check if Gfx backend has been setup + bool IsValid(); + /// return true if the Gfx backend wants to quit the application + bool QuitRequested(); + /// check if optional feature is supported + bool QueryFeature(GfxFeature::Code feature); + /// query the supported shader language + ShaderLang::Code QueryShaderLang(); + /// query resource state of a resource + ResourceState::Code QueryResourceState(const Id& id); + + /// subscribe to display events + GfxEvent::HandlerId Subscribe(GfxEvent::Handler handler); + /// unsubscribe from display events + void Unsubscribe(GfxEvent::HandlerId id); + + /// generate new resource label and push on label stack + ResourceLabel PushResourceLabel(); + /// push explicit resource label on label stack + void PushResourceLabel(ResourceLabel label); + /// pop resource label from label stack + ResourceLabel PopResourceLabel(); + + /// create (alloc+init) a buffer resource + Id CreateBuffer(const BufferDesc& desc); + /// create (alloc+init) an texture resource + Id CreateTexture(const TextureDesc& desc); + /// create (alloc+init) a shader resource + Id CreateShader(const ShaderDesc& desc); + /// create (alloc+init) a pipeline resource + Id CreatePipeline(const PipelineDesc& desc); + /// create (alloc+init) a pass resource + Id CreatePass(const PassDesc& desc); + + /// allocate a new buffer id + Id AllocBuffer(); + /// allocate a new texture id + Id AllocTexture(); + /// initialize a buffer + void InitBuffer(const Id& id, const BufferDesc& desc); + /// initialize a texture + void InitTexture(const Id& id, const TextureDesc& desc); + /// set allocated, non-initialized buffer to failed resource state + void FailBuffer(const Id& id); + /// set allocated, non-initialized texture to failed resource state + void FailTexture(const Id& id); + + /// lookup a resource Id by locator + Id LookupResource(const Locator& loc); + /// add a shared resource to the resource registry + void AddResource(const Locator& loc, const Id& id); + /// destroy one or multiple resource(s) by matching label + void DestroyResources(ResourceLabel label); + + /// update dynamic buffer data + void UpdateBuffer(const Id& id, const void* data, int numBytes); + /// update dynamic texture data + void UpdateTexture(const Id& id, const ImageContent& content); + + /// begin rendering pass + void BeginPass(const Id& passId, const PassAction* action); + /// finish rendering pass + void EndPass(); + + /// apply viewport + void ApplyViewPort(int x, int y, int w, int h, bool originTopLeft); + /// apply scissor rect + void ApplyScissorRect(int x, int y, int w, int h, bool originTopLeft); + /// apply pipeline state + void ApplyPipeline(const Id& pipId); + /// apply resource bindings + void ApplyBindings(const Bindings& binding); + /// apply shader uniforms + void ApplyUniforms(ShaderStage::Code stage, int ubIndex, const void* data, int numBytes); + + /// issue a draw call + void Draw(int baseElement, int numElements, int numInstances); + + /// commit current frame + void CommitFrame(); + /// present current frame + void Preset(); + /// reset the internal state cache + void ResetStateCache(); + /// process window system events + void ProcessSystemEvents(); + + bool isValid = false; + displayMgr displayManager; + ResourceRegistry registry; + ResourceLabelStack labelStack; + Array toDestroy; // keeping the array here prevents frequent allocs/frees + Array vsInputs; // to keep track of vertex shader inputs +}; + +} // namespace _priv +} // namespace Oryol diff --git a/code/Modules/Gfx/private/sokol/sokolImpl.cc b/code/Modules/Gfx/private/sokol/sokolImpl.cc new file mode 100644 index 000000000..d00777456 --- /dev/null +++ b/code/Modules/Gfx/private/sokol/sokolImpl.cc @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// sokolImpl.cc +//------------------------------------------------------------------------------ +#include "Core/Assertion.h" +#include "Core/Memory/Memory.h" +#include "Core/Log.h" +#define SOKOL_IMPL +#define SOKOL_ASSERT(c) o_assert_dbg(c) +#define SOKOL_MALLOC(s) Oryol::Memory::Alloc(s) +#define SOKOL_FREE(p) Oryol::Memory::Free(p) +#define SOKOL_LOG(m) Oryol::Log::Info("%s\n",m) +#define SOKOL_UNREACHABLE o_assert_dbg(false) +#if ORYOL_OPENGL +#include "Gfx/private/gl/gl.h" +#endif +#if ORYOL_OPENGLES2 +#define SOKOL_GLES2 +#elif ORYOL_OPENGLES3 +#define SOKOL_GLES3 +#elif ORYOL_OPENGL_CORE_PROFILE +#define SOKOL_GLCORE33 +#elif ORYOL_D3D11 +#define SOKOL_D3D11 +#elif ORYOL_METAL +#error "please include sokolImpl.mm for Metal builds" +#endif +#include "sokol_gfx.h" + diff --git a/code/Modules/Gfx/private/sokol/sokolImpl.mm b/code/Modules/Gfx/private/sokol/sokolImpl.mm new file mode 100644 index 000000000..269657578 --- /dev/null +++ b/code/Modules/Gfx/private/sokol/sokolImpl.mm @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// sokolImpl.mm +//------------------------------------------------------------------------------ +#include "Core/Assertion.h" +#include "Core/Memory/Memory.h" +#include "Core/Log.h" +#define SOKOL_IMPL +#define SOKOL_ASSERT(c) o_assert_dbg(c) +#define SOKOL_MALLOC(s) Oryol::Memory::Alloc(s) +#define SOKOL_FREE(p) Oryol::Memory::Free(p) +#define SOKOL_LOG(m) Oryol::Log::Info("%s\n",m) +#define SOKOL_UNREACHABLE o_assert_dbg(false) +#if !ORYOL_METAL +#error "please include sokolImpl.cc for non-Metal builds" +#endif +#define SOKOL_METAL +#include "sokol_gfx.h" diff --git a/code/Modules/Gfx/private/sokol/sokol_gfx.h b/code/Modules/Gfx/private/sokol/sokol_gfx.h new file mode 100644 index 000000000..d6029c2a4 --- /dev/null +++ b/code/Modules/Gfx/private/sokol/sokol_gfx.h @@ -0,0 +1,9925 @@ +#pragma once +/* + sokol_gfx.h -- simple 3D API wrapper + + Do this: + #define SOKOL_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE33 + #define SOKOL_GLES2 + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_DUMMY_BACKEND + + I.e. for the GL 3.3 Core Profile it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE33 + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + To enable shader compilation support in the D3D11 backend: + #define SOKOL_D3D11_SHADER_COMPILER + + If SOKOL_D3D11_SHADER_COMPILER is enabled, the executable will link against + d3dcompiler.lib (d3dcompiler_47.dll). + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + + If you want to compile without deprecated structs and functions, + define: + + SOKOL_NO_DEPRECATED + + API usage validation macros: + + SOKOL_VALIDATE_BEGIN() - begin a validation block (default:_sg_validate_begin()) + SOKOL_VALIDATE(cond, err) - like assert but for API validation (default: _sg_validate(cond, err)) + SOKOL_VALIDATE_END() - end a validation block, return true if all checks in block passed (default: bool _sg_validate()) + + If you don't want validation errors to be fatal, define SOKOL_VALIDATE_NON_FATAL, + be aware though that this may spam SOKOL_LOG messages. + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + + sokol_gfx DOES NOT: + =================== + - create a window or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided + + For complete code examples using the various backend 3D-APIs, see: + + https://github.com/floooh/sokol-samples + + + STEP BY STEP + ============ + --- to initialize sokol_gfx, after creating a window and a 3D-API + context/device, call: + + sg_setup(const sg_desc*) + + --- create resource objects (at least buffers, shaders and pipelines, + and optionally images and passes): + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + sg_pass sg_make_pass(const sg_pass_desc*) + + --- start rendering to the default frame buffer with: + + sg_begin_default_pass(const sg_pass_action* actions, int width, int height) + + --- or start rendering to an offscreen framebuffer with: + + sg_begin_pass(sg_pass pass, const sg_pass_action* actions) + + --- set the pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects + to use as textures each on the vertex-shader- and fragment-shader-stage + and then call + + sg_apply_bindings(const sg_bindings* bindings) + + to update the resource bindings + + --- optionally update shader uniform data with: + + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) + + --- kick off a draw call with: + + sg_draw(int base_element, int num_elements, int num_instances) + + --- finish the current rendering pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_pass(sg_pass pass) + + --- to set a new viewport rectangle, call + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass + + beginning a pass will reset the viewport to the size of the framebuffer used + in the new pass, + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const void* ptr, int num_bytes) + sg_update_image(sg_image img, const sg_image_content* content) + + Buffers and images to be updated must have been created with + SG_USAGE_DYNAMIC or SG_USAGE_STREAM + + Only one update per frame is allowed for buffer and image resources. + The rationale is to have a simple countermeasure to avoid the CPU + scribbling over data the GPU is currently using, or the CPU having to + wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const void* ptr, int num_bytes) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, data, num_bytes); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + bool sg_query_buffer_overflow(sg_buffer buf) + + --- to check for support of optional features: + + bool sg_query_feature(sg_feature feature) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + BACKEND-SPECIFIC TOPICS: + ======================== + --- the GL backends need to know about the internal structure of uniform + blocks, and the texture sampler-name and -type: + + typedef struct { + float mvp[16]; // model-view-projection matrix + float offset0[2]; // some 2D vectors + float offset1[2]; + float offset2[2]; + } params_t; + + // uniform block structure and texture image definition in sg_shader_desc: + sg_shader_desc desc = { + // uniform block description (size and internal structure) + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .uniforms = { + [0] = { .name="mvp", .type=SG_UNIFORMTYPE_MAT4 }, + [1] = { .name="offset0", .type=SG_UNIFORMTYPE_VEC2 }, + ... + } + }, + // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type + .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } + ... + }; + + --- the Metal and D3D11 backends only need to know the size of uniform blocks, + not their internal member structure, and they only need to know + the type of a texture sampler, not its name: + + sg_shader_desc desc = { + .vs.uniform_blocks[0].size = sizeof(params_t), + .fs.images[0].type = SG_IMAGETYPE_ARRAY, + ... + }; + + --- when creating a pipeline object, GLES2/WebGL need to know the vertex + attribute names as used in the vertex shader when describing vertex + layouts: + + sg_pipeline_desc desc = { + .layout = { + .attrs = { + [0] = { .name="position", .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .name="color1", .format=SG_VERTEXFORMAT_FLOAT4 } + } + } + }; + + --- on D3D11 you need to provide a semantic name and semantic index in the + vertex attribute definition instead (see the D3D11 documentation on + D3D11_INPUT_ELEMENT_DESC for details): + + sg_pipeline_desc desc = { + .layout = { + .attrs = { + [0] = { .sem_name="POSITION", .sem_index=0, .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .sem_name="COLOR", .sem_index=1, .format=SG_VERTEXFORMAT_FLOAT4 } + } + } + }; + + --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute + name or semantic name, since vertex attributes can be bound by their slot index + (this is mandatory in Metal, and optional in GL): + + sg_pipeline_desc desc = { + .layout = { + .attrs = { + [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } + } + } + }; + + WORKING WITH CONTEXTS + ===================== + sokol-gfx allows to switch between different rendering contexts and + associate resource objects with contexts. This is useful to + create GL applications that render into multiple windows. + + A rendering context keeps track of all resources created while + the context is active. When the context is destroyed, all resources + "belonging to the context" are destroyed as well. + + A default context will be created and activated implicitly in + sg_setup(), and destroyed in sg_shutdown(). So for a typical application + which *doesn't* use multiple contexts, nothing changes, and calling + the context functions isn't necessary. + + Three functions have been added to work with contexts: + + --- sg_context sg_setup_context(): + This must be called once after a GL context has been created and + made active. + + --- void sg_activate_context(sg_context ctx) + This must be called after making a different GL context active. + Apart from 3D-API-specific actions, the call to sg_activate_context() + will internally call sg_reset_state_cache(). + + --- void sg_discard_context(sg_context ctx) + This must be called right before a GL context is destroyed and + will destroy all resources associated with the context (that + have been created while the context was active) The GL context must be + active at the time sg_discard_context(sg_context ctx) is called. + + Also note that resources (buffers, images, shaders and pipelines) must + only be used or destroyed while the same GL context is active that + was also active while the resource was created (an exception is + resource sharing on GL, such resources can be used while + another context is active, but must still be destroyed under + the same context that was active during creation). + + For more information, check out the multiwindow-glfw sample: + + https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + TODO: + ==== + - talk about asynchronous resource creation + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#include +#include + +#ifndef SOKOL_API_DECL + #define SOKOL_API_DECL extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#endif + +/* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: textures and render targets + sg_shader: vertex- and fragment-shaders, uniform blocks + sg_pipeline: associated shader and vertex-layouts, and render states + sg_pass: a bundle of render targets and actions on them + sg_context: a 'context handle' for switching between 3D-API contexts + + Instead of pointers, resource creation functions return a 32-bit + number which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'unique counter' in the upper bits. The index allows fast + pool lookups, and combined with the unique-mask it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a struct so that the compiler + can complain when the wrong resource type is used. +*/ +typedef struct sg_buffer { uint32_t id; } sg_buffer; +typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_shader { uint32_t id; } sg_shader; +typedef struct sg_pipeline { uint32_t id; } sg_pipeline; +typedef struct sg_pass { uint32_t id; } sg_pass; +typedef struct sg_context { uint32_t id; } sg_context; + +/* + various compile-time constants + + FIXME: it may make sense to convert some of those into defines so + that the user code can override them. +*/ +enum { + SG_INVALID_ID = 0, + SG_NUM_SHADER_STAGES = 2, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 4, + SG_MAX_SHADERSTAGE_BUFFERS = 4, + SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_UBS = 4, + SG_MAX_UB_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, + SG_MAX_MIPMAPS = 16, + SG_MAX_TEXTUREARRAY_LAYERS = 128 +}; + +/* + sg_feature + + These are optional features, use the function + sg_query_feature() to check whether the feature is supported. +*/ +typedef enum sg_feature { + SG_FEATURE_INSTANCING, + SG_FEATURE_TEXTURE_COMPRESSION_DXT, + SG_FEATURE_TEXTURE_COMPRESSION_PVRTC, + SG_FEATURE_TEXTURE_COMPRESSION_ATC, + SG_FEATURE_TEXTURE_COMPRESSION_ETC2, + SG_FEATURE_TEXTURE_FLOAT, + SG_FEATURE_TEXTURE_HALF_FLOAT, + SG_FEATURE_ORIGIN_BOTTOM_LEFT, + SG_FEATURE_ORIGIN_TOP_LEFT, + SG_FEATURE_MSAA_RENDER_TARGETS, + SG_FEATURE_PACKED_VERTEX_FORMAT_10_2, + SG_FEATURE_MULTIPLE_RENDER_TARGET, + SG_FEATURE_IMAGETYPE_3D, + SG_FEATURE_IMAGETYPE_ARRAY, + + SG_NUM_FEATURES +} sg_feature; + +/* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. +*/ +typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sg_resource_state; + +/* + sg_usage + + A resource usage hint describing the update strategy of + buffers and images. This is used in the sg_buffer_desc.usage + and sg_image_desc.usage members when creating buffers + and images: + + SG_USAGE_IMMUTABLE: the resource will never be updated with + new data, instead the data content of the + resource must be provided on creation + SG_USAGE_DYNAMIC: the resource will be updated infrequently + with new data (this could range from "once + after creation", to "quite often but not + every frame") + SG_USAGE_STREAM: the resource will be updated each frame + with new content + + The rendering backends use this hint to prevent that the + CPU needs to wait for the GPU when attempting to update + a resource that might be currently accessed by the GPU. + + Resource content is updated with the function sg_update_buffer() for + buffer objects, and sg_update_image() for image objects. Only + one update is allowed per frame and resource object. The + application must update all data required for rendering (this + means that the update data can be smaller than the resource size, + if only a part of the overall resource size is used for rendering, + you only need to make sure that the data that *is* used is valid. + + The default usage is SG_USAGE_IMMUTABLE. +*/ +typedef enum sg_usage { + _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ + SG_USAGE_IMMUTABLE, + SG_USAGE_DYNAMIC, + SG_USAGE_STREAM, + _SG_USAGE_NUM, + _SG_USAGE_FORCE_U32 = 0x7FFFFFFF +} sg_usage; + +/* + sg_buffer_type + + This indicates whether a buffer contains vertex- or index-data, + used in the sg_buffer_desc.type member when creating a buffer. + + The default value is SG_BUFFERTYPE_VERTEXBUFFER. +*/ +typedef enum sg_buffer_type { + _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_BUFFERTYPE_VERTEXBUFFER, + SG_BUFFERTYPE_INDEXBUFFER, + _SG_BUFFERTYPE_NUM, + _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_buffer_type; + +/* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. +*/ +typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_index_type; + +/* + sg_image_type + + Indicates the basic image type (2D-texture, cubemap, 3D-texture + or 2D-array-texture). 3D- and array-textures are not supported + on the GLES2/WebGL backend. The image type is used in the + sg_image_desc.type member when creating an image. + + The default image type when creating an image is SG_IMAGETYPE_2D. +*/ +typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_type; + +/* + sg_cube_face + + The cubemap faces. Use these as indices in the sg_image_desc.content + array. +*/ +typedef enum sg_cube_face { + SG_CUBEFACE_POS_X, + SG_CUBEFACE_NEG_X, + SG_CUBEFACE_POS_Y, + SG_CUBEFACE_NEG_Y, + SG_CUBEFACE_POS_Z, + SG_CUBEFACE_NEG_Z, + SG_CUBEFACE_NUM, + _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF +} sg_cube_face; + +/* + sg_shader_stage + + There are 2 shader stages: vertex- and fragment-shader-stage. + Each shader stage consists of: + + - one slot for a shader function (provided as source- or byte-code) + - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by + the shader function +*/ +typedef enum sg_shader_stage { + SG_SHADERSTAGE_VS, + SG_SHADERSTAGE_FS, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF +} sg_shader_stage; + +/* + sg_pixel_format + + This is a common subset of useful and widely supported pixel formats. The + pixel format enum is mainly used when creating an image object in the + sg_image_desc.pixel_format member. + + The default pixel format when creating an image is SG_PIXELFORMAT_RGBA8. +*/ +typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ + SG_PIXELFORMAT_NONE, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_RGB8, + SG_PIXELFORMAT_RGBA4, + SG_PIXELFORMAT_R5G6B5, + SG_PIXELFORMAT_R5G5B5A1, + SG_PIXELFORMAT_R10G10B10A2, + SG_PIXELFORMAT_RGBA32F, + SG_PIXELFORMAT_RGBA16F, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_L8, + SG_PIXELFORMAT_DXT1, + SG_PIXELFORMAT_DXT3, + SG_PIXELFORMAT_DXT5, + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTHSTENCIL, + SG_PIXELFORMAT_PVRTC2_RGB, + SG_PIXELFORMAT_PVRTC4_RGB, + SG_PIXELFORMAT_PVRTC2_RGBA, + SG_PIXELFORMAT_PVRTC4_RGBA, + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_SRGB8, + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_pixel_format; + +/* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. +*/ +typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_primitive_type; + +/* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_image_desc.min_filter and sg_image_desc.mag_filter + members when creating an image object. + + The default filter mode is SG_FILTER_NEAREST. +*/ +typedef enum sg_filter { + _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + SG_FILTER_NEAREST_MIPMAP_NEAREST, + SG_FILTER_NEAREST_MIPMAP_LINEAR, + SG_FILTER_LINEAR_MIPMAP_NEAREST, + SG_FILTER_LINEAR_MIPMAP_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF +} sg_filter; + +/* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. +*/ +typedef enum sg_wrap { + _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF +} sg_wrap; + +/* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of vertex data when creating a pipeline object. +*/ +typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_format; + +/* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. +*/ +typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_step; + +/* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. +*/ +typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + SG_UNIFORMTYPE_FLOAT2, + SG_UNIFORMTYPE_FLOAT3, + SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_MAT4, + _SG_UNIFORMTYPE_NUM, + _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_type; + +/* + sg_cull_mode + + The face-culling mode, this is used in the + sg_pipeline_desc.rasterizer.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE +*/ +typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF +} sg_cull_mode; + +/* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.rasterizer.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) +*/ +typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF +} sg_face_winding; + +/* + sg_compare_func + + The compare-function for depth- and stencil-ref tests. + This is used when creating pipeline objects in the members: + + sg_pipeline_desc + .depth_stencil + .depth_compare_func + .stencil_front.compare_func + .stencil_back.compare_func + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. +*/ +typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF +} sg_compare_func; + +/* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the members: + + sg_pipeline_desc + .depth_stencil + .stencil_front + .fail_op + .depth_fail_op + .pass_op + .stencil_back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. +*/ +typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF +} sg_stencil_op; + +/* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and SG_BLENDFACTOR_ZERO for destination factors. +*/ +typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF +} sg_blend_factor; + +/* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following members when + creating a pipeline object: + + sg_pipeline_desc + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. +*/ +typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF +} sg_blend_op; + +/* + sg_color_mask + + Selects the color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.blend.color_write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) +*/ +typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ + SG_COLORMASK_NONE = (0x10), /* special value for 'all channels disabled */ + SG_COLORMASK_R = (1<<0), + SG_COLORMASK_G = (1<<1), + SG_COLORMASK_B = (1<<2), + SG_COLORMASK_A = (1<<3), + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF +} sg_color_mask; + +/* + sg_action + + Defines what action should be performed at the start of a render pass: + + SG_ACTION_CLEAR: clear the render target image + SG_ACTION_LOAD: load the previous content of the render target image + SG_ACTION_DONTCARE: leave the render target image content undefined + + This is used in the sg_pass_action structure. + + The default action for all pass attachments is SG_ACTION_CLEAR, with the + clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is in its _SG_ACTION_DEFAULT, the value fields will be ignored). +*/ +typedef enum sg_action { + _SG_ACTION_DEFAULT, + SG_ACTION_CLEAR, + SG_ACTION_LOAD, + SG_ACTION_DONTCARE, + _SG_ACTION_NUM, + _SG_ACTION_FORCE_U32 = 0x7FFFFFFF +} sg_action; + +/* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start of a rendering pass in the functions sg_begin_pass() + and sg_begin_default_pass(). + + A separate action and clear values can be defined for each + color attachment, and for the depth-stencil attachment. + + The default clear values are defined by the macros: + + - SG_DEFAULT_CLEAR_RED: 0.5f + - SG_DEFAULT_CLEAR_GREEN: 0.5f + - SG_DEFAULT_CLEAR_BLUE: 0.5f + - SG_DEFAULT_CLEAR_ALPHA: 1.0f + - SG_DEFAULT_CLEAR_DEPTH: 1.0f + - SG_DEFAULT_CLEAR_STENCIL: 0 +*/ +typedef struct sg_color_attachment_action { + sg_action action; + float val[4]; +} sg_color_attachment_action; + +typedef struct sg_depth_attachment_action { + sg_action action; + float val; +} sg_depth_attachment_action; + +typedef struct sg_stencil_attachment_action { + sg_action action; + uint8_t val; +} sg_stencil_attachment_action; + +typedef struct sg_pass_action { + uint32_t _start_canary; + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; + uint32_t _end_canary; +} sg_pass_action; + +/* + sg_bindings + + The sg_bindings structure defines the resource binding slots + of the sokol_gfx render pipeline, used as argument to the + sg_apply_bindings() function. + + A resource binding struct contains: + + - 1..N vertex buffers + - 0..N vertex buffer offsets + - 0..1 index buffers + - 0..1 index buffer offsets + - 0..N vertex shader stage images + - 0..N fragment shader stage images + + The max number of vertex buffer and shader stage images + are defined by the SG_MAX_SHADERSTAGE_BUFFERS and + SG_MAX_SHADERSTAGE_IMAGES configuration constants. + + The optional buffer offsets can be used to group different chunks + of vertex- and/or index-data into the same buffer objects. +*/ +typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + uint32_t _end_canary; +} sg_bindings; + +/* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the + sg_make_buffer() call. + + The default configuration is: + + .size: 0 (this *must* be set to a valid size in bytes) + .type: SG_BUFFERTYPE_VERTEXBUFFER + .usage: SG_USAGE_IMMUTABLE + .content 0 + .label 0 (optional string label for trace hooks) + + The dbg_label will be ignored by sokol_gfx.h, it is only useful + when hooking into sg_make_buffer() or sg_init_buffer() via + the sg_install_trace_hook + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other members except the .content member, and + these must match the creation parameters of the native buffers you + provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API + buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when + calling sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. +*/ +typedef struct sg_buffer_desc { + uint32_t _start_canary; + int size; + sg_buffer_type type; + sg_usage usage; + const void* content; + const char* label; + /* GL specific */ + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* Metal specific */ + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_buffer; + uint32_t _end_canary; +} sg_buffer_desc; + +/* + sg_subimage_content + + Pointer to and size of a subimage-surface data, this is + used to describe the initial content of immutable-usage images, + or for updating a dynamic- or stream-usage images. + + For 3D- or array-textures, one sg_subimage_content item + describes an entire mipmap level consisting of all array- or + 3D-slices of the mipmap level. It is only possible to update + an entire mipmap level, not parts of it. +*/ +typedef struct sg_subimage_content { + const void* ptr; /* pointer to subimage data */ + int size; /* size in bytes of pointed-to subimage data */ +} sg_subimage_content; + +/* + sg_image_content + + Defines the content of an image through a 2D array + of sg_subimage_content structs. The first array dimension + is the cubemap face, and the second array dimension the + mipmap level. +*/ +typedef struct sg_image_content { + sg_subimage_content subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; +} sg_image_content; + +/* + sg_image_desc + + Creation parameters for sg_image objects, used in the + sg_make_image() call. + + The default configuration is: + + .type: SG_IMAGETYPE_2D + .render_target: false + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .depth/.layers: 1 + .num_mipmaps: 1 + .usage: SG_USAGE_IMMUTABLE + .pixel_format: SG_PIXELFORMAT_RGBA8 + .sample_count: 1 (only used in render_targets) + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .max_anisotropy 1 (must be 1..16) + .min_lod 0.0f + .max_lod FLT_MAX + .content an sg_image_content struct to define the initial content + .label 0 (optional string label for trace hooks) + + SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on + WebGL/GLES2, use sg_query_feature(SG_FEATURE_IMAGETYPE_ARRAY) and + sg_query_feature(SG_FEATURE_IMAGETYPE_3D) at runtime to check + if array- and 3D-textures are supported. + + Images with usage SG_USAGE_IMMUTABLE must be fully initialized by + providing a valid .content member which points to + initialization data. + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal + or D3D11 textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + + The same rules apply as for injecting native buffers + (see sg_buffer_desc documentation for more details). +*/ +typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + bool render_target; + int width; + int height; + union { + int depth; + int layers; + }; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + uint32_t max_anisotropy; + float min_lod; + float max_lod; + sg_image_content content; + const char* label; + /* GL specific */ + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + /* Metal specific */ + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_texture; + uint32_t _end_canary; +} sg_image_desc; + +/* + sg_shader_desc + + The structure sg_shader_desc describes the shaders, uniform blocks + and texture images on the vertex- and fragment-shader stage. + + TODO: source code vs byte code, 3D backend API specifics. +*/ +typedef struct sg_shader_uniform_desc { + const char* name; + sg_uniform_type type; + int array_count; +} sg_shader_uniform_desc; + +typedef struct sg_shader_uniform_block_desc { + int size; + sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; +} sg_shader_uniform_block_desc; + +typedef struct sg_shader_image_desc { + const char* name; + sg_image_type type; +} sg_shader_image_desc; + +typedef struct sg_shader_stage_desc { + const char* source; + const uint8_t* byte_code; + int byte_code_size; + const char* entry; + sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; +} sg_shader_stage_desc; + +typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_stage_desc vs; + sg_shader_stage_desc fs; + const char* label; + uint32_t _end_canary; +} sg_shader_desc; + +/* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters + for an sg_pipeline object, used as argument to the + sg_make_pipeline() function: + + - the complete vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - depth-stencil state + - alpha-blending state + - rasterizer state + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol will then compute the offsets and strides + from the vertex component formats (.layout.attrs[].offset). Please note + that ALL vertex attribute offsets must be 0 in order for the the + automatic offset computation to kick in. + + The default configuration is as follows: + + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .name 0 (GLES2 requires an attribute name here) + .sem_name 0 (D3D11 requires a semantic name here) + .sem_index 0 (D3D11 requires a semantic index here) + .shader: 0 (must be intilized with a valid sg_shader id!) + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .depth_stencil: + .stencil_front, .stencil_back: + .fail_op: SG_STENCILOP_KEEP + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .compare_func SG_COMPAREFUNC_ALWAYS + .depth_compare_func: SG_COMPAREFUNC_ALWAYS + .depth_write_enabled: false + .stencil_enabled: false + .stencil_read_mask: 0 + .stencil_write_mask: 0 + .stencil_ref: 0 + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .color_write_mask: SG_COLORMASK_RGBA + .color_attachment_count 1 + .color_format SG_PIXELFORMAT_RGBA8 + .depth_format SG_PIXELFORMAT_DEPTHSTENCIL + .blend_color: { 0.0f, 0.0f, 0.0f, 0.0f } + .rasterizer: + .alpha_to_coverage_enabled: false + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: 1 + .depth_bias: 0.0f + .depth_bias_slope_scale: 0.0f + .depth_bias_clamp: 0.0f + .label 0 (optional string label for trace hooks) +*/ +typedef struct sg_buffer_layout_desc { + int stride; + sg_vertex_step step_func; + int step_rate; +} sg_buffer_layout_desc; + +typedef struct sg_vertex_attr_desc { + const char* name; + const char* sem_name; + int sem_index; + int buffer_index; + int offset; + sg_vertex_format format; +} sg_vertex_attr_desc; + +typedef struct sg_layout_desc { + sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_layout_desc; + +typedef struct sg_stencil_state { + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; + sg_compare_func compare_func; +} sg_stencil_state; + +typedef struct sg_depth_stencil_state { + sg_stencil_state stencil_front; + sg_stencil_state stencil_back; + sg_compare_func depth_compare_func; + bool depth_write_enabled; + bool stencil_enabled; + uint8_t stencil_read_mask; + uint8_t stencil_write_mask; + uint8_t stencil_ref; +} sg_depth_stencil_state; + +typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; + uint8_t color_write_mask; + int color_attachment_count; + sg_pixel_format color_format; + sg_pixel_format depth_format; + float blend_color[4]; +} sg_blend_state; + +typedef struct sg_rasterizer_state { + bool alpha_to_coverage_enabled; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + float depth_bias; + float depth_bias_slope_scale; + float depth_bias_clamp; +} sg_rasterizer_state; + +typedef struct sg_pipeline_desc { + uint32_t _start_canary; + sg_layout_desc layout; + sg_shader shader; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_depth_stencil_state depth_stencil; + sg_blend_state blend; + sg_rasterizer_state rasterizer; + const char* label; + uint32_t _end_canary; +} sg_pipeline_desc; + +/* + sg_pass_desc + + Creation parameters for an sg_pass object, used as argument + to the sg_make_pass() function. + + A pass object contains 1..4 color-attachments and none, or one, + depth-stencil-attachment. Each attachment consists of + an image, and two additional indices describing + which subimage the pass will render: one mipmap index, and + if the image is a cubemap, array-texture or 3D-texture, the + face-index, array-layer or depth-slice. + + Pass images must fulfill the following requirements: + + All images must have: + - been created as render target (sg_image_desc.render_target = true) + - the same size + - the same sample count + + In addition, all color-attachment images must have the same + pixel format. +*/ +typedef struct sg_attachment_desc { + sg_image image; + int mip_level; + union { + int face; + int layer; + int slice; + }; +} sg_attachment_desc; + +typedef struct sg_pass_desc { + uint32_t _start_canary; + sg_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; + sg_attachment_desc depth_stencil_attachment; + const char* label; + uint32_t _end_canary; +} sg_pass_desc; + +/* + sg_trace_hooks + + Installable callback function to keep track of the sokol_gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks functions with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. +*/ +typedef struct sg_trace_hooks { + void* user_data; + void (*query_feature)(sg_feature feature, bool result, void* user_data); + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_pass)(sg_pass pass, void* user_data); + void (*update_buffer)(sg_buffer buf, const void* data_ptr, int data_size, void* user_data); + void (*update_image)(sg_image img, const sg_image_content* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const void* data_ptr, int data_size, int result, void* user_data); + void (*query_buffer_overflow)(sg_buffer buf, bool result, void* user_data); + void (*query_buffer_state)(sg_buffer buf, sg_resource_state result, void* user_data); + void (*query_image_state)(sg_image img, sg_resource_state result, void* user_data); + void (*query_shader_state)(sg_shader shd, sg_resource_state result, void* user_data); + void (*query_pipeline_state)(sg_pipeline pip, sg_resource_state result, void* user_data); + void (*query_pass_state)(sg_pass pass, sg_resource_state result, void* user_data); + void (*begin_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); + void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const void* data, int num_bytes, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_pass)(sg_pass result, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_pass)(sg_pass pass_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); + void (*err_buffer_pool_exhausted)(void* user_data); + void (*err_image_pool_exhausted)(void* user_data); + void (*err_shader_pool_exhausted)(void* user_data); + void (*err_pipeline_pool_exhausted)(void* user_data); + void (*err_pass_pool_exhausted)(void* user_data); + void (*err_context_mismatch)(void* user_data); + void (*err_pass_invalid)(void* user_data); + void (*err_draw_invalid)(void* user_data); + void (*err_bindings_invalid)(void* user_data); +} sg_trace_hooks; + +/* + sg_desc + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + The default configuration is: + + .buffer_pool_size: 128 + .image_pool_size: 128 + .shader_pool_size: 32 + .pipeline_pool_size: 64 + .pass_pool_size: 16 + .context_pool_size: 16 + + GL specific: + .gl_force_gles2 + if this is true the GL backend will act in "GLES2 fallback mode" even + when compiled with SOKOL_GLES3, this is useful to fall back + to traditional WebGL if a browser doesn't support a WebGL2 context + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged (const void*) to sokol_gfx, which will use a unretained + bridged cast (__bridged id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object for the + duration of the sokol_gfx call! + + .mtl_device + a pointer to the MTLDevice object + .mtl_renderpass_descriptor_cb + a C callback function to obtain the MTLRenderPassDescriptor for the + current frame when rendering to the default framebuffer, will be called + in sg_begin_default_pass() + .mtl_drawable_cb + a C callback function to obtain a MTLDrawable for the current + frame when rendering to the default framebuffer, will be called in + sg_end_pass() of the default pass + .mtl_global_uniform_buffer_size + the size of the global uniform buffer in bytes, this must be big + enough to hold all uniform block updates for a single frame, + the default value is 4 MByte (4 * 1024 * 1024) + .mtl_sampler_cache_size + the number of slots in the sampler cache, the Metal backend + will share texture samplers with the same state in this + cache, the default value is 64 + + D3D11 specific: + .d3d11_device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .d3d11_device_context + a pointer to the ID3D11DeviceContext object + .d3d11_render_target_view_cb + a C callback function to obtain a pointer to the current + ID3D11RenderTargetView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .d3d11_depth_stencil_view_cb + a C callback function to obtain a pointer to the current + ID3D11DepthStencilView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer +*/ +typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int pass_pool_size; + int context_pool_size; + /* GL specific */ + bool gl_force_gles2; + /* Metal-specific */ + const void* mtl_device; + const void* (*mtl_renderpass_descriptor_cb)(void); + const void* (*mtl_drawable_cb)(void); + int mtl_global_uniform_buffer_size; + int mtl_sampler_cache_size; + /* D3D11-specific */ + const void* d3d11_device; + const void* d3d11_device_context; + const void* (*d3d11_render_target_view_cb)(void); + const void* (*d3d11_depth_stencil_view_cb)(void); + uint32_t _end_canary; +} sg_desc; + +/* setup and misc functions */ +SOKOL_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_API_DECL void sg_shutdown(void); +SOKOL_API_DECL bool sg_isvalid(void); +SOKOL_API_DECL bool sg_query_feature(sg_feature feature); +SOKOL_API_DECL void sg_reset_state_cache(void); +SOKOL_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); +SOKOL_API_DECL void sg_push_debug_group(const char* name); +SOKOL_API_DECL void sg_pop_debug_group(void); + +/* resource creation, destruction and updating */ +SOKOL_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); +SOKOL_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); +SOKOL_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); +SOKOL_API_DECL void sg_destroy_buffer(sg_buffer buf); +SOKOL_API_DECL void sg_destroy_image(sg_image img); +SOKOL_API_DECL void sg_destroy_shader(sg_shader shd); +SOKOL_API_DECL void sg_destroy_pipeline(sg_pipeline pip); +SOKOL_API_DECL void sg_destroy_pass(sg_pass pass); +SOKOL_API_DECL void sg_update_buffer(sg_buffer buf, const void* data_ptr, int data_size); +SOKOL_API_DECL void sg_update_image(sg_image img, const sg_image_content* data); +SOKOL_API_DECL int sg_append_buffer(sg_buffer buf, const void* data_ptr, int data_size); +SOKOL_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); + +/* get resource state (initial, alloc, valid, failed) */ +SOKOL_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); +SOKOL_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); +SOKOL_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); +SOKOL_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); + +/* rendering functions */ +SOKOL_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); +SOKOL_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); +SOKOL_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); +SOKOL_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); +SOKOL_API_DECL void sg_apply_pipeline(sg_pipeline pip); +SOKOL_API_DECL void sg_apply_bindings(const sg_bindings* bindings); +SOKOL_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes); +SOKOL_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_API_DECL void sg_end_pass(void); +SOKOL_API_DECL void sg_commit(void); + +/* separate resource allocation and initialization (for async setup) */ +SOKOL_API_DECL sg_buffer sg_alloc_buffer(void); +SOKOL_API_DECL sg_image sg_alloc_image(void); +SOKOL_API_DECL sg_shader sg_alloc_shader(void); +SOKOL_API_DECL sg_pipeline sg_alloc_pipeline(void); +SOKOL_API_DECL sg_pass sg_alloc_pass(void); +SOKOL_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); +SOKOL_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); +SOKOL_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); +SOKOL_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); +SOKOL_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); +SOKOL_API_DECL void sg_fail_buffer(sg_buffer buf_id); +SOKOL_API_DECL void sg_fail_image(sg_image img_id); +SOKOL_API_DECL void sg_fail_shader(sg_shader shd_id); +SOKOL_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); +SOKOL_API_DECL void sg_fail_pass(sg_pass pass_id); + +/* rendering contexts (optional) */ +SOKOL_API_DECL sg_context sg_setup_context(void); +SOKOL_API_DECL void sg_activate_context(sg_context ctx_id); +SOKOL_API_DECL void sg_discard_context(sg_context ctx_id); + +/* deprecated structs and functions */ +#ifndef SOKOL_NO_DEPRECATED +typedef struct sg_draw_state { + uint32_t _start_canary; + sg_pipeline pipeline; + sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + uint32_t _end_canary; +} sg_draw_state; +SOKOL_API_DECL void sg_apply_draw_state(const sg_draw_state* ds); +SOKOL_API_DECL void sg_apply_uniform_block(sg_shader_stage stage, int ub_index, const void* data, int num_bytes); +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/*--- IMPLEMENTATION ---------------------------------------------------------*/ +#ifdef SOKOL_IMPL +#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL or SOKOL_DUMMY_BACKEND" +#endif +#include /* memset */ + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_VALIDATE_BEGIN + #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() +#endif +#ifndef SOKOL_VALIDATE + #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) +#endif +#ifndef SOKOL_VALIDATE_END + #define SOKOL_VALIDATE_END() _sg_validate_end() +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef SOKOL_MALLOC + #include + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #else + #define SOKOL_LOG(s) + #endif +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +/* default clear values */ +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV + #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 + #endif + #ifndef GL_UNSIGNED_INT_24_8 + #define GL_UNSIGNED_INT_24_8 0x84FA + #endif + #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + #endif + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 + #endif + #ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #endif + #ifndef GL_COMPRESSED_SRGB8_ETC2 + #define GL_COMPRESSED_SRGB8_ETC2 0x9275 + #endif + #ifndef GL_DEPTH24_STENCIL8 + #define GL_DEPTH24_STENCIL8 0x88F0 + #endif + #ifndef GL_HALF_FLOAT + #define GL_HALF_FLOAT 0x140B + #endif + #ifndef GL_DEPTH_STENCIL + #define GL_DEPTH_STENCIL 0x84F9 + #endif + #ifndef GL_LUMINANCE + #define GL_LUMINANCE 0x1909 + #endif + #ifdef SOKOL_GLES2 + # ifdef GL_ANGLE_instanced_arrays + # define SOKOL_INSTANCING_ENABLED + # define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) + # define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) + # define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) + # elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) + # define SOKOL_INSTANCING_ENABLED + # define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) + # define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) + # define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) + # else + # define SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" + # define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) + # define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) + # define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && SOKOL_GLES2_INSTANCING_ERROR) + # endif + #else + # define SOKOL_INSTANCING_ENABLED + #endif + #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } + +#elif defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef CINTERFACE + #define CINTERFACE + #endif + #ifndef COBJMACROS + #define COBJMACROS + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #pragma comment (lib, "WindowsApp.lib") + #else + #pragma comment (lib, "user32.lib") + #pragma comment (lib, "dxgi.lib") + #pragma comment (lib, "d3d11.lib") + #pragma comment (lib, "dxguid.lib") + #endif + #if defined(SOKOL_D3D11_SHADER_COMPILER) + #include + #if !(defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #pragma comment (lib, "d3dcompiler.lib") + #endif + #endif +#elif defined(SOKOL_METAL) + #if !__has_feature(objc_arc) + #error "Please enable ARC when using the Metal backend" + #endif + #include + #import +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#pragma warning(disable:4115) /* named type definition in parentheses */ +#pragma warning(disable:4505) /* unreferenced local function has been removed */ +#endif + +/*=== PRIVATE DECLS ==========================================================*/ + +/* resource pool slots */ +typedef struct { + uint32_t id; + uint32_t ctx_id; + sg_resource_state state; +} _sg_slot_t; + +/* constants */ +enum { + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_PASS_POOL_SIZE = 16, + _SG_DEFAULT_CONTEXT_POOL_SIZE = 16 +}; + +/* helper macros */ +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) ((ab)?a:b) +#define _sg_clamp(v,v0,v1) ((vv1)?(v1):(v))) +#define _sg_fequal(val,cmp,delta) (((val-cmp)> -delta)&&((val-cmp) _sg_mtl_device; +static id _sg_mtl_cmd_queue; +static id _sg_mtl_cmd_buffer; +static id _sg_mtl_uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +static id _sg_mtl_cmd_encoder; +static dispatch_semaphore_t _sg_mtl_sem; + +#endif /* SOKOL_METAL */ + +/*=== RESOURCE POOL DECLARATIONS =============================================*/ + +/* this *MUST* remain 0 */ +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +typedef struct { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t pass_pool; + _sg_pool_t context_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_pass_t* passes; + _sg_context_t* contexts; +} _sg_pools_t; + +/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ +typedef enum { + /* special case 'validation was successful' */ + _SG_VALIDATE_SUCCESS, + + /* buffer creation */ + _SG_VALIDATE_BUFFERDESC_CANARY, + _SG_VALIDATE_BUFFERDESC_SIZE, + _SG_VALIDATE_BUFFERDESC_CONTENT, + _SG_VALIDATE_BUFFERDESC_NO_CONTENT, + + /* image creation */ + _SG_VALIDATE_IMAGEDESC_CANARY, + _SG_VALIDATE_IMAGEDESC_WIDTH, + _SG_VALIDATE_IMAGEDESC_HEIGHT, + _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, + _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, + _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, + _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT, + _SG_VALIDATE_IMAGEDESC_CONTENT, + _SG_VALIDATE_IMAGEDESC_NO_CONTENT, + + /* shader creation */ + _SG_VALIDATE_SHADERDESC_CANARY, + _SG_VALIDATE_SHADERDESC_SOURCE, + _SG_VALIDATE_SHADERDESC_BYTECODE, + _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, + _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, + _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, + _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, + _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, + _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, + _SG_VALIDATE_SHADERDESC_IMG_NAME, + + /* pipeline creation */ + _SG_VALIDATE_PIPELINEDESC_CANARY, + _SG_VALIDATE_PIPELINEDESC_SHADER, + _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, + _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, + _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, + _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, + + /* pass creation */ + _SG_VALIDATE_PASSDESC_CANARY, + _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_IMAGE, + _SG_VALIDATE_PASSDESC_MIPLEVEL, + _SG_VALIDATE_PASSDESC_FACE, + _SG_VALIDATE_PASSDESC_LAYER, + _SG_VALIDATE_PASSDESC_SLICE, + _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, + _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS, + _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_IMAGE_SIZES, + _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, + + /* sg_begin_pass validation */ + _SG_VALIDATE_BEGINPASS_PASS, + _SG_VALIDATE_BEGINPASS_IMAGE, + + /* sg_apply_pipeline validation */ + _SG_VALIDATE_APIP_PIPELINE_VALID_ID, + _SG_VALIDATE_APIP_PIPELINE_EXISTS, + _SG_VALIDATE_APIP_PIPELINE_VALID, + _SG_VALIDATE_APIP_SHADER_EXISTS, + _SG_VALIDATE_APIP_SHADER_VALID, + _SG_VALIDATE_APIP_ATT_COUNT, + _SG_VALIDATE_APIP_COLOR_FORMAT, + _SG_VALIDATE_APIP_DEPTH_FORMAT, + _SG_VALIDATE_APIP_SAMPLE_COUNT, + + /* sg_apply_bindings validation */ + _SG_VALIDATE_ABND_PIPELINE, + _SG_VALIDATE_ABND_PIPELINE_EXISTS, + _SG_VALIDATE_ABND_PIPELINE_VALID, + _SG_VALIDATE_ABND_VBS, + _SG_VALIDATE_ABND_VB_EXISTS, + _SG_VALIDATE_ABND_VB_TYPE, + _SG_VALIDATE_ABND_VB_OVERFLOW, + _SG_VALIDATE_ABND_NO_IB, + _SG_VALIDATE_ABND_IB, + _SG_VALIDATE_ABND_IB_EXISTS, + _SG_VALIDATE_ABND_IB_TYPE, + _SG_VALIDATE_ABND_IB_OVERFLOW, + _SG_VALIDATE_ABND_VS_IMGS, + _SG_VALIDATE_ABND_VS_IMG_EXISTS, + _SG_VALIDATE_ABND_VS_IMG_TYPES, + _SG_VALIDATE_ABND_FS_IMGS, + _SG_VALIDATE_ABND_FS_IMG_EXISTS, + _SG_VALIDATE_ABND_FS_IMG_TYPES, + + /* sg_apply_uniforms validation */ + _SG_VALIDATE_AUB_NO_PIPELINE, + _SG_VALIDATE_AUB_NO_UB_AT_SLOT, + _SG_VALIDATE_AUB_SIZE, + + /* sg_update_buffer validation */ + _SG_VALIDATE_UPDATEBUF_USAGE, + _SG_VALIDATE_UPDATEBUF_SIZE, + _SG_VALIDATE_UPDATEBUF_ONCE, + _SG_VALIDATE_UPDATEBUF_APPEND, + + /* sg_append_buffer validation */ + _SG_VALIDATE_APPENDBUF_USAGE, + _SG_VALIDATE_APPENDBUF_SIZE, + _SG_VALIDATE_APPENDBUF_UPDATE, + + /* sg_update_image validation */ + _SG_VALIDATE_UPDIMG_USAGE, + _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, + _SG_VALIDATE_UPDIMG_SIZE, + _SG_VALIDATE_UPDIMG_COMPRESSED, + _SG_VALIDATE_UPDIMG_ONCE +} _sg_validate_error_t; + +/*=== GENERIC BACKEND STATE ==================================================*/ + +/* FIXME: merge all of the above in here */ +typedef struct { + bool valid; + uint32_t frame_index; + sg_context active_context; + sg_pass cur_pass; + sg_pipeline cur_pipeline; + bool pass_valid; + bool bindings_valid; + bool next_draw_valid; + #if defined(SOKOL_DEBUG) + _sg_validate_error_t validate_error; + #endif + _sg_pools_t pools; + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sg_gl_backend_t gl; + #elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; + #elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; + #endif + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; + #endif +} _sg_state_t; +static _sg_state_t _sg; + +/*-- helper functions --------------------------------------------------------*/ + +/* return byte size of a vertex format */ +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_INVALID: return 0; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +/* return the byte size of a shader uniform */ +_SOKOL_PRIVATE int _sg_uniform_size(sg_uniform_type type, int count) { + switch (type) { + case SG_UNIFORMTYPE_INVALID: return 0; + case SG_UNIFORMTYPE_FLOAT: return 4 * count; + case SG_UNIFORMTYPE_FLOAT2: return 8 * count; + case SG_UNIFORMTYPE_FLOAT3: return 12 * count; /* FIXME: std140??? */ + case SG_UNIFORMTYPE_FLOAT4: return 16 * count; + case SG_UNIFORMTYPE_MAT4: return 64 * count; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +/* return true if pixel format is a compressed format */ +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DXT1: + case SG_PIXELFORMAT_DXT3: + case SG_PIXELFORMAT_DXT5: + case SG_PIXELFORMAT_PVRTC2_RGB: + case SG_PIXELFORMAT_PVRTC4_RGB: + case SG_PIXELFORMAT_PVRTC2_RGBA: + case SG_PIXELFORMAT_PVRTC4_RGBA: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + return true; + default: + return false; + } +} + +/* return true if pixel format is a valid render target format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_R10G10B10A2: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGBA16F: + return true; + default: + return false; + } +} + +/* return true if pixel format is a valid depth format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: + case SG_PIXELFORMAT_DEPTHSTENCIL: + return true; + default: + return false; + } +} + +/* return true if pixel format is a depth-stencil format */ +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + /* FIXME: more depth stencil formats? */ + return (SG_PIXELFORMAT_DEPTHSTENCIL == fmt); +} + +/* return the bytes-per-pixel for a pixel format */ +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA32F: + return 16; + case SG_PIXELFORMAT_RGBA16F: + return 8; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_R10G10B10A2: + case SG_PIXELFORMAT_R32F: + return 4; + case SG_PIXELFORMAT_RGB8: + return 3; + case SG_PIXELFORMAT_R5G5B5A1: + case SG_PIXELFORMAT_R5G6B5: + case SG_PIXELFORMAT_RGBA4: + case SG_PIXELFORMAT_R16F: + return 2; + case SG_PIXELFORMAT_L8: + return 1; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +/* return row pitch for an image */ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width) { + int pitch; + switch (fmt) { + case SG_PIXELFORMAT_DXT1: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + pitch = ((width + 3) / 4) * 8; + pitch = pitch < 8 ? 8 : pitch; + break; + case SG_PIXELFORMAT_DXT3: + case SG_PIXELFORMAT_DXT5: + pitch = ((width + 3) / 4) * 16; + pitch = pitch < 16 ? 16 : pitch; + break; + case SG_PIXELFORMAT_PVRTC4_RGB: + case SG_PIXELFORMAT_PVRTC4_RGBA: + { + const int block_size = 4*4; + const int bpp = 4; + int width_blocks = width / 4; + width_blocks = width_blocks < 2 ? 2 : width_blocks; + pitch = width_blocks * ((block_size * bpp) / 8); + } + break; + case SG_PIXELFORMAT_PVRTC2_RGB: + case SG_PIXELFORMAT_PVRTC2_RGBA: + { + const int block_size = 8*4; + const int bpp = 2; + int width_blocks = width / 4; + width_blocks = width_blocks < 2 ? 2 : width_blocks; + pitch = width_blocks * ((block_size * bpp) / 8); + } + break; + default: + pitch = width * _sg_pixelformat_bytesize(fmt); + break; + } + return pitch; +} + +/* return pitch of a 2D subimage / texture slice */ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height) { + int num_rows = 0; + switch (fmt) { + case SG_PIXELFORMAT_DXT1: + case SG_PIXELFORMAT_DXT3: + case SG_PIXELFORMAT_DXT5: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + case SG_PIXELFORMAT_PVRTC2_RGB: + case SG_PIXELFORMAT_PVRTC2_RGBA: + case SG_PIXELFORMAT_PVRTC4_RGB: + case SG_PIXELFORMAT_PVRTC4_RGBA: + num_rows = ((height + 3) / 4); + break; + default: + num_rows = height; + break; + } + if (num_rows < 1) { + num_rows = 1; + } + return num_rows * _sg_row_pitch(fmt, width); +} + +/* resolve pass action defaults into a new pass action struct */ +_SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { + SOKOL_ASSERT(from && to); + *to = *from; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (to->colors[i].action == _SG_ACTION_DEFAULT) { + to->colors[i].action = SG_ACTION_CLEAR; + to->colors[i].val[0] = SG_DEFAULT_CLEAR_RED; + to->colors[i].val[1] = SG_DEFAULT_CLEAR_GREEN; + to->colors[i].val[2] = SG_DEFAULT_CLEAR_BLUE; + to->colors[i].val[3] = SG_DEFAULT_CLEAR_ALPHA; + } + } + if (to->depth.action == _SG_ACTION_DEFAULT) { + to->depth.action = SG_ACTION_CLEAR; + to->depth.val = SG_DEFAULT_CLEAR_DEPTH; + } + if (to->stencil.action == _SG_ACTION_DEFAULT) { + to->stencil.action = SG_ACTION_CLEAR; + to->stencil.val = SG_DEFAULT_CLEAR_STENCIL; + } +} + +/*== DUMMY BACKEND IMPL ======================================================*/ +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); +} + +_SOKOL_PRIVATE void _sg_discard_backend(void) { + /* empty */ +} + +_SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { + _SOKOL_UNUSED(f); + return true; +} + +_SOKOL_PRIVATE void _sg_reset_state_cache(void) { + /* empty*/ +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + buf->size = desc->size; + buf->append_pos = 0; + buf->append_overflow = false; + buf->type = _sg_def(desc->type, SG_BUFFERTYPE_VERTEXBUFFER); + buf->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + buf->update_frame_index = 0; + buf->append_frame_index = 0; + buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + buf->active_slot = 0; + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + img->type = _sg_def(desc->type, SG_IMAGETYPE_2D); + img->render_target = desc->render_target; + img->width = desc->width; + img->height = desc->height; + img->depth = _sg_def(desc->depth, 1); + img->num_mipmaps = _sg_def(desc->num_mipmaps, 1); + img->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + img->pixel_format = _sg_def(desc->pixel_format, SG_PIXELFORMAT_RGBA8); + img->sample_count = _sg_def(desc->sample_count, 1); + img->min_filter = _sg_def(desc->min_filter, SG_FILTER_NEAREST); + img->mag_filter = _sg_def(desc->mag_filter, SG_FILTER_NEAREST); + img->wrap_u = _sg_def(desc->wrap_u, SG_WRAP_REPEAT); + img->wrap_v = _sg_def(desc->wrap_v, SG_WRAP_REPEAT); + img->wrap_w = _sg_def(desc->wrap_w, SG_WRAP_REPEAT); + img->max_anisotropy = _sg_def(desc->max_anisotropy, 1); + img->upd_frame_index = 0; + img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; + img->active_slot = 0; + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + /* uniform block sizes and image types */ + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; + ub->size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].type = img_desc->type; + stage->num_images++; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + pip->shader = shd; + pip->shader_id = desc->shader; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); + pip->vertex_layout_valid[a_desc->buffer_index] = true; + } + pip->color_attachment_count = _sg_def(desc->blend.color_attachment_count, 1); + pip->color_format = _sg_def(desc->blend.color_format, SG_PIXELFORMAT_RGBA8); + pip->depth_format = _sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL); + pip->sample_count = _sg_def(desc->rasterizer.sample_count, 1); + pip->depth_bias = desc->rasterizer.depth_bias; + pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; + pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; + pip->index_type = _sg_def(desc->index_type, SG_INDEXTYPE_NONE); + for (int i = 0; i < 4; i++) { + pip->blend_color[i] = desc->blend.blend_color[i]; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + /* copy image pointers and desc attributes */ + const sg_attachment_desc* att_desc; + _sg_attachment_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + SOKOL_ASSERT(0 == pass->color_atts[i].image); + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + pass->num_color_atts++; + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); + att = &pass->color_atts[i]; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + SOKOL_ASSERT(0 == pass->ds_att.image); + att_desc = &desc->depth_stencil_attachment; + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); + att = &pass->ds_att; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[ds_img_index]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + _SOKOL_UNUSED(pass); + _SOKOL_UNUSED(action); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); +} + +_SOKOL_PRIVATE void _sg_end_pass(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_commit(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(vbs && vb_offsets); + SOKOL_ASSERT(vs_imgs); + SOKOL_ASSERT(fs_imgs); + _SOKOL_UNUSED(pip); + _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); + _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); + _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); +} + +_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + SOKOL_ASSERT(data && (num_bytes > 0)); + SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(num_bytes); +} + +_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); +} + +_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { + SOKOL_ASSERT(buf && data && (data_size > 0)); + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(data_size); + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } +} + +_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { + SOKOL_ASSERT(buf && data && (data_size > 0)); + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(data_size); + if (new_frame) { + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->active_slot >= img->num_slots) { + img->active_slot = 0; + } +} + +/*== GL BACKEND ==============================================================*/ +#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + +/*-- type translation --------------------------------------------------------*/ +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + #if !defined(SOKOL_GLES2) + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + #endif + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; + case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case SG_USAGE_STREAM: return GL_STREAM_DRAW; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_UINT10_N2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return GL_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_R32F: + return GL_FLOAT; + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_R16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGB8: + case SG_PIXELFORMAT_L8: + return GL_UNSIGNED_BYTE; + case SG_PIXELFORMAT_R10G10B10A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_PIXELFORMAT_R5G5B5A1: + return GL_UNSIGNED_SHORT_5_5_5_1; + case SG_PIXELFORMAT_R5G6B5: + return GL_UNSIGNED_SHORT_5_6_5; + case SG_PIXELFORMAT_RGBA4: + return GL_UNSIGNED_SHORT_4_4_4_4; + case SG_PIXELFORMAT_DEPTH: + /* FIXME */ + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_DEPTHSTENCIL: + /* FIXME */ + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_NONE: + return 0; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_R5G5B5A1: + case SG_PIXELFORMAT_RGBA4: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_R10G10B10A2: + return GL_RGBA; + case SG_PIXELFORMAT_RGB8: + case SG_PIXELFORMAT_R5G6B5: + return GL_RGB; + case SG_PIXELFORMAT_L8: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_R16F: + #if defined(SOKOL_GLES2) + return GL_LUMINANCE; + #else + if (_sg.gl.gles2) { + return GL_LUMINANCE; + } + else { + return GL_RED; + } + #endif + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTHSTENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_PVRTC2_RGB: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC4_RGB: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC2_RGBA: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC4_RGBA: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { + #if defined(SOKOL_GLES2) + return _sg_gl_teximage_format(fmt); + #else + if (_sg.gl.gles2) { + return _sg_gl_teximage_format(fmt); + } + else { + switch (fmt) { + case SG_PIXELFORMAT_NONE: + return 0; + case SG_PIXELFORMAT_RGBA8: + return GL_RGBA8; + case SG_PIXELFORMAT_RGB8: + return GL_RGB8; + case SG_PIXELFORMAT_RGBA4: + return GL_RGBA4; + case SG_PIXELFORMAT_R5G6B5: + #if defined(SOKOL_GLES3) + return GL_RGB565; + #else + return GL_RGB5; + #endif + case SG_PIXELFORMAT_R5G5B5A1: + return GL_RGB5_A1; + case SG_PIXELFORMAT_R10G10B10A2: + return GL_RGB10_A2; + case SG_PIXELFORMAT_RGBA32F: + return GL_RGBA32F; + case SG_PIXELFORMAT_RGBA16F: + return GL_RGBA16F; + case SG_PIXELFORMAT_R32F: + return GL_R32F; + case SG_PIXELFORMAT_R16F: + return GL_R16F; + case SG_PIXELFORMAT_L8: + return GL_R8; + case SG_PIXELFORMAT_DEPTH: + /* FIXME */ + return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTHSTENCIL: + return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_PVRTC2_RGB: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC4_RGB: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC2_RGBA: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC4_RGBA: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_SRGB8: + return GL_COMPRESSED_SRGB8_ETC2; + default: + SOKOL_UNREACHABLE; return 0; + } + } + #endif +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTHSTENCIL: return GL_DEPTH24_STENCIL8; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE void _sg_gl_init_attr(_sg_gl_attr_t* attr) { + attr->vb_index = -1; + attr->divisor = -1; +} + +_SOKOL_PRIVATE void _sg_gl_init_stencil_state(sg_stencil_state* s) { + SOKOL_ASSERT(s); + s->fail_op = SG_STENCILOP_KEEP; + s->depth_fail_op = SG_STENCILOP_KEEP; + s->pass_op = SG_STENCILOP_KEEP; + s->compare_func = SG_COMPAREFUNC_ALWAYS; +} + +_SOKOL_PRIVATE void _sg_gl_init_depth_stencil_state(sg_depth_stencil_state* s) { + SOKOL_ASSERT(s); + _sg_gl_init_stencil_state(&s->stencil_front); + _sg_gl_init_stencil_state(&s->stencil_back); + s->depth_compare_func = SG_COMPAREFUNC_ALWAYS; +} + +_SOKOL_PRIVATE void _sg_gl_init_blend_state(sg_blend_state* s) { + SOKOL_ASSERT(s); + s->src_factor_rgb = SG_BLENDFACTOR_ONE; + s->dst_factor_rgb = SG_BLENDFACTOR_ZERO; + s->op_rgb = SG_BLENDOP_ADD; + s->src_factor_alpha = SG_BLENDFACTOR_ONE; + s->dst_factor_alpha = SG_BLENDFACTOR_ZERO; + s->op_alpha = SG_BLENDOP_ADD; + s->color_write_mask = SG_COLORMASK_RGBA; +} + +_SOKOL_PRIVATE void _sg_gl_init_rasterizer_state(sg_rasterizer_state* s) { + SOKOL_ASSERT(s); + s->cull_mode = SG_CULLMODE_NONE; + s->face_winding = SG_FACEWINDING_CW; + s->sample_count = 1; +} + +/*-- state cache implementation ----------------------------------------------*/ +_SOKOL_PRIVATE void _sg_gl_bind_buffer(GLenum target, GLuint buffer, _sg_state_cache_t* cache) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (cache->cur_gl_vb != buffer) { + cache->cur_gl_vb = buffer; + glBindBuffer(target, buffer); + } + } + else { + if (cache->cur_gl_ib != buffer) { + cache->cur_gl_ib = buffer; + glBindBuffer(target, buffer); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(_sg_state_cache_t* cache) { + SOKOL_ASSERT(cache); + _SG_GL_CHECK_ERROR(); + memset(cache, 0, sizeof(_sg_state_cache_t)); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_gl_init_attr(&cache->attrs[i].gl_attr); + glDisableVertexAttribArray(i); + _SG_GL_CHECK_ERROR(); + } + cache->cur_primitive_type = GL_TRIANGLES; + + /* depth-stencil state */ + _sg_gl_init_depth_stencil_state(&cache->ds); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + + /* blend state */ + _sg_gl_init_blend_state(&cache->blend); + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* rasterizer state */ + _sg_gl_init_rasterizer_state(&cache->rast); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + #if defined(SOKOL_GLCORE33) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + #endif +} + +/*-- main GL backend state and functions -------------------------------------*/ + +_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { + /* assumes that _sg.gl is already zero-initialized */ + _sg.gl.valid = true; + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sg.gl.gles2 = desc->gl_force_gles2; + #else + _SOKOL_UNUSED(desc); + _sg.gl.gles2 = false; + #endif + + /* clear initial GL error state */ + #if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); + #endif + + /* initialize feature flags */ + _sg.gl.features[SG_FEATURE_ORIGIN_BOTTOM_LEFT] = true; + #if defined(SOKOL_GLCORE33) + _sg.gl.features[SG_FEATURE_INSTANCING] = true; + _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = true; + _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = true; + _sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS] = true; + _sg.gl.features[SG_FEATURE_PACKED_VERTEX_FORMAT_10_2] = true; + _sg.gl.features[SG_FEATURE_MULTIPLE_RENDER_TARGET] = true; + _sg.gl.features[SG_FEATURE_IMAGETYPE_3D] = true; + _sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY] = true; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, i); + if (strstr(ext, "_texture_compression_s3tc")) { + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = true; + continue; + } + else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + continue; + } + } + #elif defined(SOKOL_GLES3) + const char* ext = (const char*) glGetString(GL_EXTENSIONS); + if (!_sg.gl.gles2) { + _sg.gl.features[SG_FEATURE_INSTANCING] = true; + _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = true; + _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = true; + _sg.gl.features[SG_FEATURE_IMAGETYPE_3D] = true; + _sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY] = true; + _sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS] = true; + _sg.gl.features[SG_FEATURE_PACKED_VERTEX_FORMAT_10_2] = true; + _sg.gl.features[SG_FEATURE_MULTIPLE_RENDER_TARGET] = true; + } + else { + _sg.gl.features[SG_FEATURE_INSTANCING] = strstr(ext, "_instanced_arrays"); + _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = strstr(ext, "_texture_float"); + _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = strstr(ext, "_texture_half_float"); + } + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = + strstr(ext, "_texture_compression_s3tc") || + strstr(ext, "_compressed_texture_s3tc") || + strstr(ext, "texture_compression_dxt1"); + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC] = + strstr(ext, "_texture_compression_pvrtc") || + strstr(ext, "_compressed_texture_pvrtc"); + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ATC] = + strstr(ext, "_compressed_texture_atc"); + _sg.gl.ext_anisotropic = + strstr(ext, "_texture_filter_anisotropic"); + #elif defined(SOKOL_GLES2) + const char* ext = (const char*) glGetString(GL_EXTENSIONS); + _sg.gl.features[SG_FEATURE_INSTANCING] = + strstr(ext, "_instanced_arrays"); + _sg.gl.features[SG_FEATURE_TEXTURE_FLOAT] = + strstr(ext, "_texture_float"); + _sg.gl.features[SG_FEATURE_TEXTURE_HALF_FLOAT] = + strstr(ext, "_texture_half_float"); + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT] = + strstr(ext, "_texture_compression_s3tc") || + strstr(ext, "_compressed_texture_s3tc") || + strstr(ext, "texture_compression_dxt1"); + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC] = + strstr(ext, "_texture_compression_pvrtc") || + strstr(ext, "_compressed_texture_pvrtc"); + _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ATC] = + strstr(ext, "_compressed_texture_atc"); + _sg.gl.ext_anisotropic = + strstr(ext, "_texture_filter_anisotropic"); + #endif + _sg.gl.max_anisotropy = 1; + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &_sg.gl.max_anisotropy); + } +} + +_SOKOL_PRIVATE void _sg_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + _sg.gl.valid = false; +} + +_SOKOL_PRIVATE void _sg_reset_state_cache(void) { + if (_sg.gl.cur_context) { + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _SG_GL_CHECK_ERROR(); + glBindVertexArray(_sg.gl.cur_context->vao); + _SG_GL_CHECK_ERROR(); + } + #endif + _sg_gl_reset_state_cache(&_sg.gl.cache); + } +} + +_SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { + SOKOL_ASSERT((f>=0) && (fdefault_framebuffer); + _SG_GL_CHECK_ERROR(); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); + _SG_GL_CHECK_ERROR(); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + SOKOL_ASSERT(0 == ctx->vao); + glGenVertexArrays(1, &ctx->vao); + glBindVertexArray(ctx->vao); + _SG_GL_CHECK_ERROR(); + } + #endif + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (ctx->vao) { + glDeleteVertexArrays(1, &ctx->vao); + } + _SG_GL_CHECK_ERROR(); + } + #endif +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + buf->size = desc->size; + buf->append_pos = 0; + buf->append_overflow = false; + buf->type = _sg_def(desc->type, SG_BUFFERTYPE_VERTEXBUFFER); + buf->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + buf->update_frame_index = 0; + buf->append_frame_index = 0; + buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + buf->active_slot = 0; + buf->ext_buffers = (0 != desc->gl_buffers[0]); + GLenum gl_target = _sg_gl_buffer_target(buf->type); + GLenum gl_usage = _sg_gl_usage(buf->usage); + for (int slot = 0; slot < buf->num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->ext_buffers) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } + else { + glGenBuffers(1, &gl_buf); + _sg_gl_bind_buffer(gl_target, gl_buf, &_sg.gl.cache); + glBufferData(gl_target, buf->size, 0, gl_usage); + if (buf->usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->content); + glBufferSubData(gl_target, 0, buf->size, desc->content); + } + } + buf->gl_buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SG_GL_CHECK_ERROR(); + if (!buf->ext_buffers) { + for (int slot = 0; slot < buf->num_slots; slot++) { + if (buf->gl_buf[slot]) { + glDeleteBuffers(1, &buf->gl_buf[slot]); + } + } + _SG_GL_CHECK_ERROR(); + } +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DXT1: + case SG_PIXELFORMAT_DXT3: + case SG_PIXELFORMAT_DXT5: + return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_DXT]; + case SG_PIXELFORMAT_PVRTC2_RGB: + case SG_PIXELFORMAT_PVRTC4_RGB: + case SG_PIXELFORMAT_PVRTC2_RGBA: + case SG_PIXELFORMAT_PVRTC4_RGBA: + return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_PVRTC]; + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_SRGB8: + return _sg.gl.features[SG_FEATURE_TEXTURE_COMPRESSION_ETC2]; + default: + return true; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + img->type = _sg_def(desc->type, SG_IMAGETYPE_2D); + img->render_target = desc->render_target; + img->width = desc->width; + img->height = desc->height; + img->depth = _sg_def(desc->depth, 1); + img->num_mipmaps = _sg_def(desc->num_mipmaps, 1); + img->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + img->pixel_format = _sg_def(desc->pixel_format, SG_PIXELFORMAT_RGBA8); + img->sample_count = _sg_def(desc->sample_count, 1); + img->min_filter = _sg_def(desc->min_filter, SG_FILTER_NEAREST); + img->mag_filter = _sg_def(desc->mag_filter, SG_FILTER_NEAREST); + img->wrap_u = _sg_def(desc->wrap_u, SG_WRAP_REPEAT); + img->wrap_v = _sg_def(desc->wrap_v, SG_WRAP_REPEAT); + img->wrap_w = _sg_def(desc->wrap_w, SG_WRAP_REPEAT); + img->max_anisotropy = _sg_def(desc->max_anisotropy, 1); + img->upd_frame_index = 0; + + /* check if texture format is support */ + if (!_sg_gl_supported_texture_format(img->pixel_format)) { + SOKOL_LOG("compressed texture format not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + /* check for optional texture types */ + if ((img->type == SG_IMAGETYPE_3D) && !_sg.gl.features[SG_FEATURE_IMAGETYPE_3D]) { + SOKOL_LOG("3D textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + if ((img->type == SG_IMAGETYPE_ARRAY) && !_sg.gl.features[SG_FEATURE_IMAGETYPE_ARRAY]) { + SOKOL_LOG("array textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* create 1 or 2 GL textures, depending on requested update strategy */ + img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + img->active_slot = 0; + img->ext_textures = (0 != desc->gl_textures[0]); + + #if !defined(SOKOL_GLES2) + bool msaa = false; + if (!_sg.gl.gles2) { + msaa = (img->sample_count > 1) && (_sg.gl.features[SG_FEATURE_MSAA_RENDER_TARGETS]); + } + #endif + + if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + /* special case depth-stencil-buffer? */ + SOKOL_ASSERT((img->usage == SG_USAGE_IMMUTABLE) && (img->num_slots == 1)); + SOKOL_ASSERT(!img->ext_textures); /* cannot provide external texture for depth images */ + glGenRenderbuffers(1, &img->gl_depth_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl_depth_render_buffer); + GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->pixel_format); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && msaa) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_depth_format, img->width, img->height); + } + else + #endif + { + glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->width, img->height); + } + } + else { + /* regular color texture */ + img->gl_target = _sg_gl_texture_target(img->type); + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->pixel_format); + + /* if this is a MSAA render target, need to create a separate render buffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && img->render_target && msaa) { + glGenRenderbuffers(1, &img->gl_msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl_msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->sample_count, gl_internal_format, img->width, img->height); + } + #endif + + if (img->ext_textures) { + /* inject externally GL textures */ + for (int slot = 0; slot < img->num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl_tex[slot] = desc->gl_textures[slot]; + } + } + else { + /* create our own GL texture(s) */ + const GLenum gl_format = _sg_gl_teximage_format(img->pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->pixel_format); + for (int slot = 0; slot < img->num_slots; slot++) { + glGenTextures(1, &img->gl_tex[slot]); + glActiveTexture(GL_TEXTURE0); + glBindTexture(img->gl_target, img->gl_tex[slot]); + GLenum gl_min_filter = _sg_gl_filter(img->min_filter); + GLenum gl_mag_filter = _sg_gl_filter(img->mag_filter); + glTexParameteri(img->gl_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(img->gl_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); + if (_sg.gl.ext_anisotropic && (img->max_anisotropy > 1)) { + GLint max_aniso = (GLint) img->max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glTexParameteri(img->gl_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + if (img->type == SG_IMAGETYPE_CUBE) { + glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else { + glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_S, _sg_gl_wrap(img->wrap_u)); + glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_T, _sg_gl_wrap(img->wrap_v)); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && (img->type == SG_IMAGETYPE_3D)) { + glTexParameteri(img->gl_target, GL_TEXTURE_WRAP_R, _sg_gl_wrap(img->wrap_w)); + } + #endif + } + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(_sg_def_flt(desc->max_lod, 1000.0f), 0.0f, 1000.0f); + glTexParameterf(img->gl_target, GL_TEXTURE_MIN_LOD, min_lod); + glTexParameterf(img->gl_target, GL_TEXTURE_MAX_LOD, max_lod); + } + #endif + const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl_target; + if (SG_IMAGETYPE_CUBE == img->type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = desc->content.subimage[face_index][mip_index].ptr; + const int data_size = desc->content.subimage[face_index][mip_index].size; + int mip_width = img->width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { + if (is_compressed) { + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); + glTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } + #if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { + int mip_depth = img->depth; + if (SG_IMAGETYPE_3D == img->type) { + mip_depth >>= mip_index; + } + if (mip_depth == 0) { + mip_depth = 1; + } + if (is_compressed) { + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->pixel_format); + glTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } + } + #endif + } + } + } + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SG_GL_CHECK_ERROR(); + if (!img->ext_textures) { + for (int slot = 0; slot < img->num_slots; slot++) { + if (img->gl_tex[slot]) { + glDeleteTextures(1, &img->gl_tex[slot]); + } + } + } + if (img->gl_depth_render_buffer) { + glDeleteRenderbuffers(1, &img->gl_depth_render_buffer); + } + if (img->gl_msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl_msaa_render_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + /* compilation failed, log error and delete shader */ + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC(log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteShader(gl_shd); + gl_shd = 0; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl_prog); + _SG_GL_CHECK_ERROR(); + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); + if (!(gl_vs && gl_fs)) { + return SG_RESOURCESTATE_FAILED; + } + GLuint gl_prog = glCreateProgram(); + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC(log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + shd->gl_prog = gl_prog; + + /* resolve uniforms */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; + ub->size = ub_desc->size; + SOKOL_ASSERT(ub->num_uniforms == 0); + int cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + _sg_uniform_t* u = &ub->uniforms[u_index]; + u->type = u_desc->type; + u->count = (uint8_t) _sg_def(u_desc->array_count, 1); + u->offset = (uint16_t) cur_uniform_offset; + cur_uniform_offset += _sg_uniform_size(u->type, u->count); + if (u_desc->name) { + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); + } + else { + u->gl_loc = u_index; + } + ub->num_uniforms++; + } + SOKOL_ASSERT(ub_desc->size == cur_uniform_offset); + stage->num_uniform_blocks++; + } + } + + /* resolve image locations */ + _SG_GL_CHECK_ERROR(); + int gl_tex_slot = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + _sg_shader_image_t* img = &stage->images[img_index]; + img->type = img_desc->type; + img->gl_loc = img_index; + if (img_desc->name) { + img->gl_loc = glGetUniformLocation(gl_prog, img_desc->name); + } + if (img->gl_loc != -1) { + img->gl_tex_slot = gl_tex_slot++; + } + else { + img->gl_tex_slot = -1; + } + stage->num_images++; + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl_prog) { + glDeleteProgram(shd->gl_prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_load_stencil(const sg_stencil_state* src, sg_stencil_state* dst) { + dst->fail_op = _sg_def(src->fail_op, SG_STENCILOP_KEEP); + dst->depth_fail_op = _sg_def(src->depth_fail_op, SG_STENCILOP_KEEP); + dst->pass_op = _sg_def(src->pass_op, SG_STENCILOP_KEEP); + dst->compare_func = _sg_def(src->compare_func, SG_COMPAREFUNC_ALWAYS); +} + +_SOKOL_PRIVATE void _sg_gl_load_depth_stencil(const sg_depth_stencil_state* src, sg_depth_stencil_state* dst) { + _sg_gl_load_stencil(&src->stencil_front, &dst->stencil_front); + _sg_gl_load_stencil(&src->stencil_back, &dst->stencil_back); + dst->depth_compare_func = _sg_def(src->depth_compare_func, SG_COMPAREFUNC_ALWAYS); + dst->depth_write_enabled = src->depth_write_enabled; + dst->stencil_enabled = src->stencil_enabled; + dst->stencil_read_mask = src->stencil_read_mask; + dst->stencil_write_mask = src->stencil_write_mask; + dst->stencil_ref = src->stencil_ref; +} + +_SOKOL_PRIVATE void _sg_gl_load_blend(const sg_blend_state* src, sg_blend_state* dst) { + dst->enabled = src->enabled; + dst->src_factor_rgb = _sg_def(src->src_factor_rgb, SG_BLENDFACTOR_ONE); + dst->dst_factor_rgb = _sg_def(src->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + dst->op_rgb = _sg_def(src->op_rgb, SG_BLENDOP_ADD); + dst->src_factor_alpha = _sg_def(src->src_factor_alpha, SG_BLENDFACTOR_ONE); + dst->dst_factor_alpha = _sg_def(src->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + dst->op_alpha = _sg_def(src->op_alpha, SG_BLENDOP_ADD); + if (src->color_write_mask == SG_COLORMASK_NONE) { + dst->color_write_mask = 0; + } + else { + dst->color_write_mask = (uint8_t) _sg_def((sg_color_mask)src->color_write_mask, SG_COLORMASK_RGBA); + } + for (int i = 0; i < 4; i++) { + dst->blend_color[i] = src->blend_color[i]; + } +} + +_SOKOL_PRIVATE void _sg_gl_load_rasterizer(const sg_rasterizer_state* src, sg_rasterizer_state* dst) { + dst->alpha_to_coverage_enabled = src->alpha_to_coverage_enabled; + dst->cull_mode = _sg_def(src->cull_mode, SG_CULLMODE_NONE); + dst->face_winding = _sg_def(src->face_winding, SG_FACEWINDING_CW); + dst->sample_count = _sg_def(src->sample_count, 1); + dst->depth_bias = src->depth_bias; + dst->depth_bias_slope_scale = src->depth_bias_slope_scale; + dst->depth_bias_clamp = src->depth_bias_clamp; +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(!pip->shader && pip->shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->gl_prog); + pip->shader = shd; + pip->shader_id = desc->shader; + pip->primitive_type = _sg_def(desc->primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + pip->index_type = _sg_def(desc->index_type, SG_INDEXTYPE_NONE); + pip->color_attachment_count = _sg_def(desc->blend.color_attachment_count, 1); + pip->color_format = _sg_def(desc->blend.color_format, SG_PIXELFORMAT_RGBA8); + pip->depth_format = _sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL); + pip->sample_count = _sg_def(desc->rasterizer.sample_count, 1); + _sg_gl_load_depth_stencil(&desc->depth_stencil, &pip->depth_stencil); + _sg_gl_load_blend(&desc->blend, &pip->blend); + _sg_gl_load_rasterizer(&desc->rasterizer, &pip->rast); + + /* resolve vertex attributes */ + int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + auto_offset[layout_index] = 0; + } + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl_attrs[attr_index].vb_index = -1; + /* to use computed offsets, *all* attr offsets must be 0 */ + if (desc->layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = _sg_def(l_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); + const int step_rate = _sg_def(l_desc->step_rate, 1); + GLint attr_loc = attr_index; + if (a_desc->name) { + attr_loc = glGetAttribLocation(pip->shader->gl_prog, a_desc->name); + } + SOKOL_ASSERT(attr_loc < SG_MAX_VERTEX_ATTRIBUTES); + if (attr_loc != -1) { + _sg_gl_attr_t* gl_attr = &pip->gl_attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_desc->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } + else { + gl_attr->divisor = (int8_t) step_rate; + } + gl_attr->stride = (uint8_t) l_desc->stride; + gl_attr->offset = use_auto_offset ? auto_offset[a_desc->buffer_index] : a_desc->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); + gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); + pip->vertex_layout_valid[a_desc->buffer_index] = true; + } + else { + SOKOL_LOG("Vertex attribute not found in shader: "); + SOKOL_LOG(a_desc->name); + } + auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + } + /* fill computed vertex strides that haven't been explicitely provided */ + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + _sg_gl_attr_t* gl_attr = &pip->gl_attrs[attr_index]; + if ((gl_attr->vb_index != -1) && (0 == gl_attr->stride)) { + gl_attr->stride = (uint8_t) auto_offset[gl_attr->vb_index]; + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + /* empty */ +} + +/* + _sg_create_pass + + att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, + first entries are the color attachment images (or nullptr), last entry + is the depth-stencil image (or nullptr). +*/ +_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && att_images && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _SG_GL_CHECK_ERROR(); + + /* copy image pointers and desc attributes */ + const sg_attachment_desc* att_desc; + _sg_attachment_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + SOKOL_ASSERT(0 == pass->color_atts[i].image); + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + pass->num_color_atts++; + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); + att = &pass->color_atts[i]; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + SOKOL_ASSERT(0 == pass->ds_att.image); + att_desc = &desc->depth_stencil_attachment; + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); + att = &pass->ds_att; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[ds_img_index]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + + /* store current framebuffer binding (restored at end of function) */ + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + /* create a framebuffer object */ + glGenFramebuffers(1, &pass->gl_fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); + + /* attach msaa render buffer or textures */ + const bool is_msaa = (0 != att_images[0]->gl_msaa_render_buffer); + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->color_atts[i].image; + if (att_img) { + const GLuint gl_render_buffer = att_img->gl_msaa_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_RENDERBUFFER, gl_render_buffer); + } + } + } + else { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->color_atts[i].image; + const int mip_level = pass->color_atts[i].mip_level; + const int slice = pass->color_atts[i].slice; + if (att_img) { + const GLuint gl_tex = att_img->gl_tex[0]; + SOKOL_ASSERT(gl_tex); + const GLenum gl_att = GL_COLOR_ATTACHMENT0 + i; + switch (att_img->type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + /* 3D- or array-texture */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); + } + #endif + break; + } + } + } + } + + /* attach depth-stencil buffer to framebuffer */ + if (pass->ds_att.image) { + const GLuint gl_render_buffer = pass->ds_att.image->gl_depth_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + if (_sg_is_depth_stencil_format(pass->ds_att.image->pixel_format)) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + } + } + + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed!\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* create MSAA resolve framebuffers if necessary */ + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + att = &pass->color_atts[i]; + if (att->image) { + SOKOL_ASSERT(0 == att->gl_msaa_resolve_buffer); + glGenFramebuffers(1, &att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_FRAMEBUFFER, att->gl_msaa_resolve_buffer); + const GLuint gl_tex = att->image->gl_tex[0]; + SOKOL_ASSERT(gl_tex); + switch (att->image->type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_tex, att->mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + _sg_gl_cubeface_target(att->slice), gl_tex, att->mip_level); + break; + default: + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, att->mip_level, att->slice); + } + #endif + break; + } + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); + return SG_RESOURCESTATE_FAILED; + } + } + } + } + + /* restore original framebuffer binding */ + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SG_GL_CHECK_ERROR(); + if (0 != pass->gl_fb) { + glDeleteFramebuffers(1, &pass->gl_fb); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->color_atts[i].gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->color_atts[i].gl_msaa_resolve_buffer); + } + } + if (pass->ds_att.gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->ds_att.gl_msaa_resolve_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +/*-- GL backend rendering functions ------------------------------------------*/ +_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + /* FIXME: what if a texture used as render target is still bound, should we + unbind all currently bound textures in begin pass? */ + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + _sg.gl.in_pass = true; + _sg.gl.cur_pass = pass; /* can be 0 */ + if (pass) { + _sg.gl.cur_pass_id.id = pass->slot.id; + } + else { + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + } + _sg.gl.cur_pass_width = w; + _sg.gl.cur_pass_height = h; + if (pass) { + /* offscreen pass */ + SOKOL_ASSERT(pass->gl_fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl_fb); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + int num_attrs = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->color_atts[num_attrs].image) { + num_attrs++; + } + else { + break; + } + } + glDrawBuffers(num_attrs, att); + } + #endif + } + else { + /* default pass */ + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + } + glViewport(0, 0, w, h); + glScissor(0, 0, w, h); + bool need_pip_cache_flush = false; + if (_sg.gl.cache.blend.color_write_mask != SG_COLORMASK_RGBA) { + need_pip_cache_flush = true; + _sg.gl.cache.blend.color_write_mask = SG_COLORMASK_RGBA; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + if (!_sg.gl.cache.ds.depth_write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.depth_write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.ds.depth_compare_func != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.depth_compare_func = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + if (_sg.gl.cache.ds.stencil_write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.ds.stencil_write_mask = 0xFF; + glStencilMask(0xFF); + } + if (need_pip_cache_flush) { + /* we messed with the state cache directly, need to clear cached + pipeline to force re-evaluation in next sg_apply_pipeline() */ + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } + bool use_mrt_clear = (0 != pass); + #if defined(SOKOL_GLES2) + use_mrt_clear = false; + #else + if (_sg.gl.gles2) { + use_mrt_clear = false; + } + #endif + if (!use_mrt_clear) { + GLbitfield clear_mask = 0; + if (action->colors[0].action == SG_ACTION_CLEAR) { + clear_mask |= GL_COLOR_BUFFER_BIT; + const float* c = action->colors[0].val; + glClearColor(c[0], c[1], c[2], c[3]); + } + if (action->depth.action == SG_ACTION_CLEAR) { + clear_mask |= GL_DEPTH_BUFFER_BIT; + #ifdef SOKOL_GLCORE33 + glClearDepth(action->depth.val); + #else + glClearDepthf(action->depth.val); + #endif + } + if (action->stencil.action == SG_ACTION_CLEAR) { + clear_mask |= GL_STENCIL_BUFFER_BIT; + glClearStencil(action->stencil.val); + } + if (0 != clear_mask) { + glClear(clear_mask); + } + } + #if !defined SOKOL_GLES2 + else { + SOKOL_ASSERT(pass); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->color_atts[i].image) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, action->colors[i].val); + } + } + else { + break; + } + } + if (pass->ds_att.image) { + if ((action->depth.action == SG_ACTION_CLEAR) && (action->stencil.action == SG_ACTION_CLEAR)) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.val, action->stencil.val); + } + else if (action->depth.action == SG_ACTION_CLEAR) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.val); + } + else if (action->stencil.action == SG_ACTION_CLEAR) { + GLuint val = action->stencil.val; + glClearBufferuiv(GL_STENCIL, 0, &val); + } + } + } + #endif + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_end_pass(void) { + SOKOL_ASSERT(_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + + /* if this was an offscreen pass, and MSAA rendering was used, need + to resolve into the pass images */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && _sg.gl.cur_pass) { + /* check if the pass object is still valid */ + const _sg_pass_t* pass = _sg.gl.cur_pass; + SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); + bool is_msaa = (0 != _sg.gl.cur_pass->color_atts[0].gl_msaa_resolve_buffer); + if (is_msaa) { + SOKOL_ASSERT(pass->gl_fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl_fb); + SOKOL_ASSERT(pass->color_atts[0].image); + const int w = pass->color_atts[0].image->width; + const int h = pass->color_atts[0].image->height; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const _sg_attachment_t* att = &pass->color_atts[att_index]; + if (att->image) { + SOKOL_ASSERT(att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, att->gl_msaa_resolve_buffer); + glReadBuffer(GL_COLOR_ATTACHMENT0 + att_index); + const GLenum gl_att = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &gl_att); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else { + break; + } + } + } + } + #endif + _sg.gl.cur_pass = 0; + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + _sg.gl.cur_pass_width = 0; + _sg.gl.cur_pass_height = 0; + + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + _sg.gl.in_pass = false; + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader); + _SG_GL_CHECK_ERROR(); + if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.gl.cache.cur_pipeline = pip; + _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->index_type); + + /* update depth-stencil state */ + const sg_depth_stencil_state* new_ds = &pip->depth_stencil; + sg_depth_stencil_state* cache_ds = &_sg.gl.cache.ds; + if (new_ds->depth_compare_func != cache_ds->depth_compare_func) { + cache_ds->depth_compare_func = new_ds->depth_compare_func; + glDepthFunc(_sg_gl_compare_func(new_ds->depth_compare_func)); + } + if (new_ds->depth_write_enabled != cache_ds->depth_write_enabled) { + cache_ds->depth_write_enabled = new_ds->depth_write_enabled; + glDepthMask(new_ds->depth_write_enabled); + } + if (new_ds->stencil_enabled != cache_ds->stencil_enabled) { + cache_ds->stencil_enabled = new_ds->stencil_enabled; + if (new_ds->stencil_enabled) glEnable(GL_STENCIL_TEST); + else glDisable(GL_STENCIL_TEST); + } + if (new_ds->stencil_write_mask != cache_ds->stencil_write_mask) { + cache_ds->stencil_write_mask = new_ds->stencil_write_mask; + glStencilMask(new_ds->stencil_write_mask); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_state* new_ss = (i==0)? &new_ds->stencil_front : &new_ds->stencil_back; + sg_stencil_state* cache_ss = (i==0)? &cache_ds->stencil_front : &cache_ds->stencil_back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((new_ss->compare_func != cache_ss->compare_func) || + (new_ds->stencil_read_mask != cache_ds->stencil_read_mask) || + (new_ds->stencil_ref != cache_ds->stencil_ref)) + { + cache_ss->compare_func = new_ss->compare_func; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(new_ss->compare_func), + new_ds->stencil_ref, + new_ds->stencil_read_mask); + } + if ((new_ss->fail_op != cache_ss->fail_op) || + (new_ss->depth_fail_op != cache_ss->depth_fail_op) || + (new_ss->pass_op != cache_ss->pass_op)) + { + cache_ss->fail_op = new_ss->fail_op; + cache_ss->depth_fail_op = new_ss->depth_fail_op; + cache_ss->pass_op = new_ss->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(new_ss->fail_op), + _sg_gl_stencil_op(new_ss->depth_fail_op), + _sg_gl_stencil_op(new_ss->pass_op)); + } + } + cache_ds->stencil_read_mask = new_ds->stencil_read_mask; + cache_ds->stencil_ref = new_ds->stencil_ref; + + /* update blend state */ + const sg_blend_state* new_b = &pip->blend; + sg_blend_state* cache_b = &_sg.gl.cache.blend; + if (new_b->enabled != cache_b->enabled) { + cache_b->enabled = new_b->enabled; + if (new_b->enabled) glEnable(GL_BLEND); + else glDisable(GL_BLEND); + } + if ((new_b->src_factor_rgb != cache_b->src_factor_rgb) || + (new_b->dst_factor_rgb != cache_b->dst_factor_rgb) || + (new_b->src_factor_alpha != cache_b->src_factor_alpha) || + (new_b->dst_factor_alpha != cache_b->dst_factor_alpha)) + { + cache_b->src_factor_rgb = new_b->src_factor_rgb; + cache_b->dst_factor_rgb = new_b->dst_factor_rgb; + cache_b->src_factor_alpha = new_b->src_factor_alpha; + cache_b->dst_factor_alpha = new_b->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(new_b->src_factor_rgb), + _sg_gl_blend_factor(new_b->dst_factor_rgb), + _sg_gl_blend_factor(new_b->src_factor_alpha), + _sg_gl_blend_factor(new_b->dst_factor_alpha)); + } + if ((new_b->op_rgb != cache_b->op_rgb) || (new_b->op_alpha != cache_b->op_alpha)) { + cache_b->op_rgb = new_b->op_rgb; + cache_b->op_alpha = new_b->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(new_b->op_rgb), _sg_gl_blend_op(new_b->op_alpha)); + } + if (new_b->color_write_mask != cache_b->color_write_mask) { + cache_b->color_write_mask = new_b->color_write_mask; + glColorMask((new_b->color_write_mask & SG_COLORMASK_R) != 0, + (new_b->color_write_mask & SG_COLORMASK_G) != 0, + (new_b->color_write_mask & SG_COLORMASK_B) != 0, + (new_b->color_write_mask & SG_COLORMASK_A) != 0); + } + if (!_sg_fequal(new_b->blend_color[0], cache_b->blend_color[0], 0.0001f) || + !_sg_fequal(new_b->blend_color[1], cache_b->blend_color[1], 0.0001f) || + !_sg_fequal(new_b->blend_color[2], cache_b->blend_color[2], 0.0001f) || + !_sg_fequal(new_b->blend_color[3], cache_b->blend_color[3], 0.0001f)) + { + const float* bc = new_b->blend_color; + for (int i=0; i<4; i++) { + cache_b->blend_color[i] = bc[i]; + } + glBlendColor(bc[0], bc[1], bc[2], bc[3]); + } + + /* update rasterizer state */ + const sg_rasterizer_state* new_r = &pip->rast; + sg_rasterizer_state* cache_r = &_sg.gl.cache.rast; + if (new_r->cull_mode != cache_r->cull_mode) { + cache_r->cull_mode = new_r->cull_mode; + if (SG_CULLMODE_NONE == new_r->cull_mode) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == new_r->cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + } + } + if (new_r->face_winding != cache_r->face_winding) { + cache_r->face_winding = new_r->face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == new_r->face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + } + if (new_r->alpha_to_coverage_enabled != cache_r->alpha_to_coverage_enabled) { + cache_r->alpha_to_coverage_enabled = new_r->alpha_to_coverage_enabled; + if (new_r->alpha_to_coverage_enabled) glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + else glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + #ifdef SOKOL_GLCORE33 + if (new_r->sample_count != cache_r->sample_count) { + cache_r->sample_count = new_r->sample_count; + if (new_r->sample_count > 1) glEnable(GL_MULTISAMPLE); + else glDisable(GL_MULTISAMPLE); + } + #endif + if (!_sg_fequal(new_r->depth_bias, cache_r->depth_bias, 0.000001f) || + !_sg_fequal(new_r->depth_bias_slope_scale, cache_r->depth_bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_r->depth_bias = new_r->depth_bias; + cache_r->depth_bias_slope_scale = new_r->depth_bias_slope_scale; + glPolygonOffset(new_r->depth_bias_slope_scale, new_r->depth_bias); + bool po_enabled = true; + if (_sg_fequal(new_r->depth_bias, 0.0f, 0.000001f) && + _sg_fequal(new_r->depth_bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) glEnable(GL_POLYGON_OFFSET_FILL); + else glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + /* bind shader program */ + glUseProgram(pip->shader->gl_prog); + } +} + +_SOKOL_PRIVATE void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(num_fs_imgs); + _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(num_vbs); + _SG_GL_CHECK_ERROR(); + + /* bind textures */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; + SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); + for (int img_index = 0; img_index < stage->num_images; img_index++) { + const _sg_shader_image_t* shd_img = &stage->images[img_index]; + if (shd_img->gl_loc != -1) { + _sg_image_t* img = imgs[img_index]; + const GLuint gl_tex = img->gl_tex[img->active_slot]; + SOKOL_ASSERT(img && img->gl_target); + SOKOL_ASSERT((shd_img->gl_tex_slot != -1) && gl_tex); + glUniform1i(shd_img->gl_loc, shd_img->gl_tex_slot); + glActiveTexture(GL_TEXTURE0+shd_img->gl_tex_slot); + glBindTexture(img->gl_target, gl_tex); + } + } + } + _SG_GL_CHECK_ERROR(); + + /* index buffer (can be 0) */ + const GLuint gl_ib = ib ? ib->gl_buf[ib->active_slot] : 0; + _sg_gl_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib, &_sg.gl.cache); + _sg.gl.cache.cur_ib_offset = ib_offset; + + /* vertex attributes */ + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + _sg_gl_attr_t* attr = &pip->gl_attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + /* attribute is enabled */ + SOKOL_ASSERT(attr->vb_index < num_vbs); + _sg_buffer_t* vb = vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl_buf[vb->active_slot]; + vb_offset = vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_bind_buffer(GL_ARRAY_BUFFER, gl_vb, &_sg.gl.cache); + glVertexAttribPointer(attr_index, attr->size, attr->type, + attr->normalized, attr->stride, + (const GLvoid*)(GLintptr)vb_offset); + #ifdef SOKOL_INSTANCING_ENABLED + if (_sg.gl.features[SG_FEATURE_INSTANCING]) { + glVertexAttribDivisor(attr_index, attr->divisor); + } + #endif + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + else { + /* attribute is disabled */ + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + _SOKOL_UNUSED(num_bytes); + SOKOL_ASSERT(data && (num_bytes > 0)); + SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->shader_id.id); + _sg_shader_stage_t* stage = &_sg.gl.cache.cur_pipeline->shader->stage[stage_index]; + SOKOL_ASSERT(ub_index < stage->num_uniform_blocks); + _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->size == num_bytes); + for (int u_index = 0; u_index < ub->num_uniforms; u_index++) { + _sg_uniform_t* u = &ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + GLfloat* ptr = (GLfloat*) (((uint8_t*)data) + u->offset); + switch (u->type) { + case SG_UNIFORMTYPE_INVALID: + break; + case SG_UNIFORMTYPE_FLOAT: + glUniform1fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + glUniform4fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_MAT4: + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, ptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { + const GLenum i_type = _sg.gl.cache.cur_index_type; + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + if (0 != i_type) { + /* indexed rendering */ + const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; + const int ib_offset = _sg.gl.cache.cur_ib_offset; + const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); + if (num_instances == 1) { + glDrawElements(p_type, num_elements, i_type, indices); + } + else { + if (_sg.gl.features[SG_FEATURE_INSTANCING]) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } + } + } + else { + /* non-indexed rendering */ + if (num_instances == 1) { + glDrawArrays(p_type, base_element, num_elements); + } + else { + if (_sg.gl.features[SG_FEATURE_INSTANCING]) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } + } + } +} + +_SOKOL_PRIVATE void _sg_commit(void) { + SOKOL_ASSERT(!_sg.gl.in_pass); +} + +_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { + SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); + /* only one update per buffer per frame allowed */ + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->type); + SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl_buf[buf->active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_bind_buffer(gl_tgt, gl_buf, &_sg.gl.cache); + glBufferSubData(gl_tgt, 0, data_size, data_ptr); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { + SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); + if (new_frame) { + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->type); + SOKOL_ASSERT(buf->active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl_buf[buf->active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_bind_buffer(gl_tgt, gl_buf, &_sg.gl.cache); + glBufferSubData(gl_tgt, buf->append_pos, data_size, data_ptr); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { + SOKOL_ASSERT(img && data); + /* only one update per image per frame allowed */ + if (++img->active_slot >= img->num_slots) { + img->active_slot = 0; + } + SOKOL_ASSERT(img->active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl_tex[img->active_slot]); + glBindTexture(img->gl_target, img->gl_tex[img->active_slot]); + const GLenum gl_img_format = _sg_gl_teximage_format(img->pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->pixel_format); + const int num_faces = img->type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + GLenum gl_img_target = img->gl_target; + if (SG_IMAGETYPE_CUBE == img->type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; + int mip_width = img->width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->type) || (SG_IMAGETYPE_CUBE == img->type)) { + glTexSubImage2D(gl_img_target, mip_index, + 0, 0, + mip_width, mip_height, + gl_img_format, gl_img_type, + data_ptr); + } + #if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->type) || (SG_IMAGETYPE_ARRAY == img->type))) { + int mip_depth = img->depth >> mip_index; + if (mip_depth == 0) { + mip_depth = 1; + } + glTexSubImage3D(gl_img_target, mip_index, + 0, 0, 0, + mip_width, mip_height, mip_depth, + gl_img_format, gl_img_type, + data_ptr); + + } + #endif + } + } +} + +/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_D3D11) + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return D3D11_USAGE_IMMUTABLE; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_USAGE_DYNAMIC; + default: + SOKOL_UNREACHABLE; + return (D3D11_USAGE) 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return 0; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_CPU_ACCESS_WRITE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_texture_format(sg_pixel_format fmt) { + /* + NOTE: the following pixel formats are only supported on D3D11.1 + (we're running on D3D11.0): + DXGI_FORMAT_B4G4R4A4_UNORM + DXGI_FORMAT_B5G6R5_UNORM + DXGI_FORMAT_B5G5R5A1_UNORM + */ + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_R10G10B10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_L8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_DXT1: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_DXT3: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_DXT5: return DXGI_FORMAT_BC3_UNORM; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rendertarget_color_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_L8: return DXGI_FORMAT_R8_UNORM; + default: return DXGI_FORMAT_UNKNOWN; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_rendertarget_depth_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D16_UNORM; + case SG_PIXELFORMAT_DEPTHSTENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; + default: return DXGI_FORMAT_UNKNOWN; + } +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { + if (max_anisotropy > 1) { + return D3D11_FILTER_ANISOTROPIC; + } + else if (mag_f == SG_FILTER_NEAREST) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + else if (mag_f == SG_FILTER_LINEAR) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + /* invalid value for mag filter */ + SOKOL_UNREACHABLE; + return D3D11_FILTER_MIN_MAG_MIP_POINT; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + /* FIXME: signed 10-10-10-2 vertex format not supported on d3d11 (only unsigned) */ + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { + /* assume _sg.d3d11 already is zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->d3d11_device); + SOKOL_ASSERT(desc->d3d11_device_context); + SOKOL_ASSERT(desc->d3d11_render_target_view_cb); + SOKOL_ASSERT(desc->d3d11_depth_stencil_view_cb); + SOKOL_ASSERT(desc->d3d11_render_target_view_cb != desc->d3d11_depth_stencil_view_cb); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->d3d11_device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->d3d11_device_context; + _sg.d3d11.rtv_cb = desc->d3d11_render_target_view_cb; + _sg.d3d11.dsv_cb = desc->d3d11_depth_stencil_view_cb; +} + +_SOKOL_PRIVATE void _sg_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { + switch (f) { + case SG_FEATURE_INSTANCING: + case SG_FEATURE_TEXTURE_COMPRESSION_DXT: + case SG_FEATURE_TEXTURE_FLOAT: + case SG_FEATURE_TEXTURE_HALF_FLOAT: + case SG_FEATURE_ORIGIN_TOP_LEFT: + case SG_FEATURE_MSAA_RENDER_TARGETS: + case SG_FEATURE_MULTIPLE_RENDER_TARGET: + case SG_FEATURE_IMAGETYPE_3D: + case SG_FEATURE_IMAGETYPE_ARRAY: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ + ID3D11DeviceContext_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.zero_rtvs, NULL); + ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, NULL); + ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, NULL, 0); + ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, NULL, NULL, 0xFFFFFFFF); + ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, _sg.d3d11.zero_vbs, _sg.d3d11.zero_vb_strides, _sg.d3d11.zero_vb_offsets); + ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, NULL, DXGI_FORMAT_UNKNOWN, 0); + ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, NULL); + ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); + ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); + ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); + ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); + ID3D11DeviceContext_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); + ID3D11DeviceContext_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); + ID3D11DeviceContext_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); + ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); +} + +_SOKOL_PRIVATE void _sg_reset_state_cache(void) { + /* just clear the d3d11 device context state */ + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_reset_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11_buf); + buf->size = desc->size; + buf->append_pos = 0; + buf->append_overflow = false; + buf->type = _sg_def(desc->type, SG_BUFFERTYPE_VERTEXBUFFER); + buf->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + buf->update_frame_index = 0; + buf->append_frame_index = 0; + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11_buf = (ID3D11Buffer*) desc->d3d11_buffer; + ID3D11Buffer_AddRef(buf->d3d11_buf); + } + else { + D3D11_BUFFER_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.ByteWidth = buf->size; + d3d11_desc.Usage = _sg_d3d11_usage(buf->usage); + d3d11_desc.BindFlags = buf->type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; + d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->usage); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + D3D11_SUBRESOURCE_DATA init_data; + memset(&init_data, 0, sizeof(init_data)); + if (buf->usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->content); + init_data.pSysMem = desc->content; + init_data_ptr = &init_data; + } + HRESULT hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11_buf); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11_buf) { + ID3D11Buffer_Release(buf->d3d11_buf); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_content* content) { + const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; + int subres_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; + const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; + const sg_subimage_content* subimg_content = &(content->subimage[face_index][mip_index]); + const int slice_size = subimg_content->size / num_slices; + const int slice_offset = slice_size * slice_index; + const uint8_t* ptr = (const uint8_t*) subimg_content->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = _sg_row_pitch(img->pixel_format, mip_width); + if (img->type == SG_IMAGETYPE_3D) { + /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ + subres_data->SysMemSlicePitch = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); + } + else { + subres_data->SysMemSlicePitch = 0; + } + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(!img->d3d11_tex2d && !img->d3d11_tex3d && !img->d3d11_texds && !img->d3d11_texmsaa); + SOKOL_ASSERT(!img->d3d11_srv && !img->d3d11_smp); + HRESULT hr; + + img->type = _sg_def(desc->type, SG_IMAGETYPE_2D); + img->render_target = desc->render_target; + img->width = desc->width; + img->height = desc->height; + img->depth = _sg_def(desc->depth, 1); + img->num_mipmaps = _sg_def(desc->num_mipmaps, 1); + img->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + img->pixel_format = _sg_def(desc->pixel_format, SG_PIXELFORMAT_RGBA8); + img->sample_count = _sg_def(desc->sample_count, 1); + img->min_filter = _sg_def(desc->min_filter, SG_FILTER_NEAREST); + img->mag_filter = _sg_def(desc->mag_filter, SG_FILTER_NEAREST); + img->wrap_u = _sg_def(desc->wrap_u, SG_WRAP_REPEAT); + img->wrap_v = _sg_def(desc->wrap_v, SG_WRAP_REPEAT); + img->wrap_w = _sg_def(desc->wrap_w, SG_WRAP_REPEAT); + img->max_anisotropy = _sg_def(desc->max_anisotropy, 1); + img->upd_frame_index = 0; + const bool injected = (0 != desc->d3d11_texture); + + /* special case depth-stencil buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + /* create only a depth-texture */ + SOKOL_ASSERT(!injected); + img->d3d11_format = _sg_d3d11_rendertarget_depth_format(img->pixel_format); + if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + D3D11_TEXTURE2D_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.Width = img->width; + d3d11_desc.Height = img->height; + d3d11_desc.MipLevels = 1; + d3d11_desc.ArraySize = 1; + d3d11_desc.Format = img->d3d11_format; + d3d11_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + d3d11_desc.SampleDesc.Count = img->sample_count; + d3d11_desc.SampleDesc.Quality = (img->sample_count > 1) ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11_texds); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texds); + } + else { + /* create (or inject) color texture */ + + /* prepare initial content pointers */ + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { + _sg_d3d11_fill_subres_data(img, &desc->content); + init_data = _sg.d3d11.subres_data; + } + if (img->type != SG_IMAGETYPE_3D) { + /* 2D-, cube- or array-texture */ + /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = img->width; + d3d11_tex_desc.Height = img->height; + d3d11_tex_desc.MipLevels = img->num_mipmaps; + switch (img->type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = img->depth; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; + } + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + if (img->render_target) { + img->d3d11_format = _sg_d3d11_rendertarget_color_format(img->pixel_format); + d3d11_tex_desc.Format = img->d3d11_format; + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (img->sample_count == 1) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + img->d3d11_format = _sg_d3d11_texture_format(img->pixel_format); + d3d11_tex_desc.Format = img->d3d11_format; + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); + } + if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + d3d11_tex_desc.SampleDesc.Count = 1; + d3d11_tex_desc.SampleDesc.Quality = 0; + d3d11_tex_desc.MiscFlags = (img->type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + if (injected) { + img->d3d11_tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + ID3D11Texture2D_AddRef(img->d3d11_tex2d); + } + else { + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex2d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex2d); + } + + /* also need to create a separate MSAA render target texture? */ + if (img->sample_count > 1) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.SampleDesc.Count = img->sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; + hr = ID3D11Device_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11_texmsaa); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_texmsaa); + } + + /* shader-resource-view */ + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = d3d11_tex_desc.Format; + switch (img->type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = img->num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = img->num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = img->num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = img->depth; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex2d, &d3d11_srv_desc, &img->d3d11_srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); + } + else { + /* 3D texture */ + D3D11_TEXTURE3D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = img->width; + d3d11_tex_desc.Height = img->height; + d3d11_tex_desc.Depth = img->depth; + d3d11_tex_desc.MipLevels = img->num_mipmaps; + if (img->render_target) { + img->d3d11_format = _sg_d3d11_rendertarget_color_format(img->pixel_format); + d3d11_tex_desc.Format = img->d3d11_format; + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + img->d3d11_format = _sg_d3d11_texture_format(img->pixel_format); + d3d11_tex_desc.Format = img->d3d11_format; + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->usage); + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->usage); + } + if (img->d3d11_format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + if (injected) { + img->d3d11_tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + ID3D11Texture3D_AddRef(img->d3d11_tex3d); + } + else { + hr = ID3D11Device_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11_tex3d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_tex3d); + } + + /* shader resource view for 3d texture */ + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = d3d11_tex_desc.Format; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = img->num_mipmaps; + hr = ID3D11Device_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11_tex3d, &d3d11_srv_desc, &img->d3d11_srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_srv); + } + + /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ + D3D11_SAMPLER_DESC d3d11_smp_desc; + memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); + d3d11_smp_desc.Filter = _sg_d3d11_filter(img->min_filter, img->mag_filter, img->max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->wrap_w); + d3d11_smp_desc.MaxAnisotropy = img->max_anisotropy; + d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = _sg_def_flt(desc->max_lod, D3D11_FLOAT32_MAX); + hr = ID3D11Device_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11_smp); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11_smp); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11_tex2d) { + ID3D11Texture2D_Release(img->d3d11_tex2d); + } + if (img->d3d11_tex3d) { + ID3D11Texture3D_Release(img->d3d11_tex3d); + } + if (img->d3d11_texds) { + ID3D11Texture2D_Release(img->d3d11_texds); + } + if (img->d3d11_texmsaa) { + ID3D11Texture2D_Release(img->d3d11_texmsaa); + } + if (img->d3d11_srv) { + ID3D11ShaderResourceView_Release(img->d3d11_srv); + } + if (img->d3d11_smp) { + ID3D11SamplerState_Release(img->d3d11_smp); + } +} + +#if defined(SOKOL_D3D11_SHADER_COMPILER) +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc, const char* target) { + ID3DBlob* output = NULL; + ID3DBlob* errors = NULL; + D3DCompile( + stage_desc->source, /* pSrcData */ + strlen(stage_desc->source), /* SrcDataSize */ + NULL, /* pSourceName */ + NULL, /* pDefines */ + NULL, /* pInclude */ + stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ + target, /* pTarget (vs_5_0 or ps_5_0) */ + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ + 0, /* Flags2 */ + &output, /* ppCode */ + &errors); /* ppErrorMsgs */ + if (errors) { + SOKOL_LOG((LPCSTR)ID3D10Blob_GetBufferPointer(errors)); + ID3D10Blob_Release(errors); errors = NULL; + } + return output; +} +#endif + +#define _sg_d3d11_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) + +_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11_vs && !shd->d3d11_fs && !shd->d3d11_vs_blob); + HRESULT hr; + sg_resource_state result = SG_RESOURCESTATE_FAILED; + + /* shader stage uniform blocks and image slots */ + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; + ub->size = ub_desc->size; + + /* create a D3D constant buffer */ + SOKOL_ASSERT(!stage->d3d11_cbs[ub_index]); + D3D11_BUFFER_DESC cb_desc; + memset(&cb_desc, 0, sizeof(cb_desc)); + cb_desc.ByteWidth = _sg_d3d11_roundup(ub->size, 16); + cb_desc.Usage = D3D11_USAGE_DEFAULT; + cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + hr = ID3D11Device_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &stage->d3d11_cbs[ub_index]); + SOKOL_ASSERT(SUCCEEDED(hr) && stage->d3d11_cbs[ub_index]); + + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].type = img_desc->type; + stage->num_images++; + } + } + + const void* vs_ptr = 0, *fs_ptr = 0; + SIZE_T vs_length = 0, fs_length = 0; + #if defined(SOKOL_D3D11_SHADER_COMPILER) + ID3DBlob* vs_blob = 0, *fs_blob = 0; + #endif + if (desc->vs.byte_code && desc->fs.byte_code) { + /* create from byte code */ + vs_ptr = desc->vs.byte_code; + fs_ptr = desc->fs.byte_code; + vs_length = desc->vs.byte_code_size; + fs_length = desc->fs.byte_code_size; + } + else { + /* compile shader code */ + #if defined(SOKOL_D3D11_SHADER_COMPILER) + vs_blob = _sg_d3d11_compile_shader(&desc->vs, "vs_5_0"); + fs_blob = _sg_d3d11_compile_shader(&desc->fs, "ps_5_0"); + if (vs_blob && fs_blob) { + vs_ptr = ID3D10Blob_GetBufferPointer(vs_blob); + vs_length = ID3D10Blob_GetBufferSize(vs_blob); + fs_ptr = ID3D10Blob_GetBufferPointer(fs_blob); + fs_length = ID3D10Blob_GetBufferSize(fs_blob); + } + #endif + } + if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { + /* create the D3D vertex- and pixel-shader objects */ + hr = ID3D11Device_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11_vs); + SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_vs); + hr = ID3D11Device_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11_fs); + SOKOL_ASSERT(SUCCEEDED(hr) && shd->d3d11_fs); + + /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ + shd->d3d11_vs_blob_length = (int)vs_length; + shd->d3d11_vs_blob = SOKOL_MALLOC((int)vs_length); + SOKOL_ASSERT(shd->d3d11_vs_blob); + memcpy(shd->d3d11_vs_blob, vs_ptr, vs_length); + + result = SG_RESOURCESTATE_VALID; + } + #if defined(SOKOL_D3D11_SHADER_COMPILER) + if (vs_blob) { + ID3D10Blob_Release(vs_blob); vs_blob = 0; + } + if (fs_blob) { + ID3D10Blob_Release(fs_blob); fs_blob = 0; + } + #endif + return result; +} + +_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11_vs) { + ID3D11VertexShader_Release(shd->d3d11_vs); + } + if (shd->d3d11_fs) { + ID3D11PixelShader_Release(shd->d3d11_fs); + } + if (shd->d3d11_vs_blob) { + SOKOL_FREE(shd->d3d11_vs_blob); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + for (int ub_index = 0; ub_index < stage->num_uniform_blocks; ub_index++) { + if (stage->d3d11_cbs[ub_index]) { + ID3D11Buffer_Release(stage->d3d11_cbs[ub_index]); + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(shd->d3d11_vs_blob && shd->d3d11_vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11_il && !pip->d3d11_rs && !pip->d3d11_dss && !pip->d3d11_bs); + HRESULT hr; + + pip->shader = shd; + pip->shader_id = desc->shader; + pip->index_type = _sg_def(desc->index_type, SG_INDEXTYPE_NONE); + pip->color_attachment_count = _sg_def(desc->blend.color_attachment_count, 1); + pip->color_format = _sg_def(desc->blend.color_format, SG_PIXELFORMAT_RGBA8); + pip->depth_format = _sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL); + pip->sample_count = _sg_def(desc->rasterizer.sample_count, 1); + pip->d3d11_index_format = _sg_d3d11_index_format(pip->index_type); + pip->d3d11_topology = _sg_d3d11_primitive_topology(_sg_def(desc->primitive_type, SG_PRIMITIVETYPE_TRIANGLES)); + for (int i = 0; i < 4; i++) { + pip->blend_color[i] = desc->blend.blend_color[i]; + } + pip->d3d11_stencil_ref = desc->depth_stencil.stencil_ref; + + /* create input layout object */ + int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + auto_offset[layout_index] = 0; + } + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + /* to use computed offsets, all attr offsets must be 0 */ + if (desc->layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; + memset(d3d11_comps, 0, sizeof(d3d11_comps)); + int attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = _sg_def(l_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); + const int step_rate = _sg_def(l_desc->step_rate, 1); + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = a_desc->sem_name; + d3d11_comp->SemanticIndex = a_desc->sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); + d3d11_comp->InputSlot = a_desc->buffer_index; + d3d11_comp->AlignedByteOffset = use_auto_offset ? auto_offset[a_desc->buffer_index] : a_desc->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = step_rate; + } + auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + pip->vertex_layout_valid[a_desc->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + const int stride = l_desc->stride ? l_desc->stride : auto_offset[layout_index]; + SOKOL_ASSERT(stride > 0); + pip->d3d11_vb_strides[layout_index] = stride; + } + else { + pip->d3d11_vb_strides[layout_index] = 0; + } + } + hr = ID3D11Device_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, /* pInputElementDesc */ + attr_index, /* NumElements */ + shd->d3d11_vs_blob, /* pShaderByteCodeWithInputSignature */ + shd->d3d11_vs_blob_length, /* BytecodeLength */ + &pip->d3d11_il); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_il); + + /* create rasterizer state */ + D3D11_RASTERIZER_DESC rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = _sg_d3d11_cull_mode(_sg_def(desc->rasterizer.cull_mode, SG_CULLMODE_NONE)); + rs_desc.FrontCounterClockwise = _sg_def(desc->rasterizer.face_winding, SG_FACEWINDING_CW) == SG_FACEWINDING_CCW; + rs_desc.DepthBias = (INT) desc->rasterizer.depth_bias; + rs_desc.DepthBiasClamp = desc->rasterizer.depth_bias_clamp; + rs_desc.SlopeScaledDepthBias = desc->rasterizer.depth_bias_slope_scale; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = _sg_def(desc->rasterizer.sample_count, 1) > 1; + rs_desc.AntialiasedLineEnable = FALSE; + hr = ID3D11Device_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11_rs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_rs); + + /* create depth-stencil state */ + D3D11_DEPTH_STENCIL_DESC dss_desc; + memset(&dss_desc, 0, sizeof(dss_desc)); + dss_desc.DepthEnable = TRUE; + dss_desc.DepthWriteMask = desc->depth_stencil.depth_write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dss_desc.DepthFunc = _sg_d3d11_compare_func(_sg_def(desc->depth_stencil.depth_compare_func, SG_COMPAREFUNC_ALWAYS)); + dss_desc.StencilEnable = desc->depth_stencil.stencil_enabled; + dss_desc.StencilReadMask = desc->depth_stencil.stencil_read_mask; + dss_desc.StencilWriteMask = desc->depth_stencil.stencil_write_mask; + const sg_stencil_state* sf = &desc->depth_stencil.stencil_front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(_sg_def(sf->fail_op, SG_STENCILOP_KEEP)); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(_sg_def(sf->depth_fail_op, SG_STENCILOP_KEEP)); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(_sg_def(sf->pass_op, SG_STENCILOP_KEEP)); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(_sg_def(sf->compare_func, SG_COMPAREFUNC_ALWAYS)); + const sg_stencil_state* sb = &desc->depth_stencil.stencil_back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(_sg_def(sb->fail_op, SG_STENCILOP_KEEP)); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(_sg_def(sb->depth_fail_op, SG_STENCILOP_KEEP)); + dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(_sg_def(sb->pass_op, SG_STENCILOP_KEEP)); + dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(_sg_def(sb->compare_func, SG_COMPAREFUNC_ALWAYS)); + hr = ID3D11Device_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11_dss); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_dss); + + /* create blend state */ + D3D11_BLEND_DESC bs_desc; + memset(&bs_desc, 0, sizeof(bs_desc)); + bs_desc.AlphaToCoverageEnable = desc->rasterizer.alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = FALSE; + bs_desc.RenderTarget[0].BlendEnable = desc->blend.enabled; + bs_desc.RenderTarget[0].SrcBlend = _sg_d3d11_blend_factor(_sg_def(desc->blend.src_factor_rgb, SG_BLENDFACTOR_ONE)); + bs_desc.RenderTarget[0].DestBlend = _sg_d3d11_blend_factor(_sg_def(desc->blend.dst_factor_rgb, SG_BLENDFACTOR_ZERO)); + bs_desc.RenderTarget[0].BlendOp = _sg_d3d11_blend_op(_sg_def(desc->blend.op_rgb, SG_BLENDOP_ADD)); + bs_desc.RenderTarget[0].SrcBlendAlpha = _sg_d3d11_blend_factor(_sg_def(desc->blend.src_factor_alpha, SG_BLENDFACTOR_ONE)); + bs_desc.RenderTarget[0].DestBlendAlpha = _sg_d3d11_blend_factor(_sg_def(desc->blend.dst_factor_alpha, SG_BLENDFACTOR_ZERO)); + bs_desc.RenderTarget[0].BlendOpAlpha = _sg_d3d11_blend_op(_sg_def(desc->blend.op_alpha, SG_BLENDOP_ADD)); + bs_desc.RenderTarget[0].RenderTargetWriteMask = _sg_d3d11_color_write_mask(_sg_def((sg_color_mask)desc->blend.color_write_mask, SG_COLORMASK_RGBA)); + hr = ID3D11Device_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11_bs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11_bs); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->d3d11_il) { + ID3D11InputLayout_Release(pip->d3d11_il); + } + if (pip->d3d11_rs) { + ID3D11RasterizerState_Release(pip->d3d11_rs); + } + if (pip->d3d11_dss) { + ID3D11DepthStencilState_Release(pip->d3d11_dss); + } + if (pip->d3d11_bs) { + ID3D11BlendState_Release(pip->d3d11_bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(_sg.d3d11.dev); + + const sg_attachment_desc* att_desc; + _sg_attachment_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + SOKOL_ASSERT(0 == pass->color_atts[i].image); + SOKOL_ASSERT(pass->d3d11_rtvs[i] == 0); + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + pass->num_color_atts++; + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); + att = &pass->color_atts[i]; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + + /* create D3D11 render-target-view */ + ID3D11Resource* d3d11_res = 0; + const bool is_msaa = att->image->sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = att->image->d3d11_format; + switch (att->image->type) { + case SG_IMAGETYPE_2D: + if (is_msaa) { + d3d11_res = (ID3D11Resource*) att->image->d3d11_texmsaa; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = att->mip_level; + } + break; + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + if (is_msaa) { + d3d11_res = (ID3D11Resource*) att->image->d3d11_texmsaa; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY; + d3d11_rtv_desc.Texture2DMSArray.FirstArraySlice = att->slice; + d3d11_rtv_desc.Texture2DMSArray.ArraySize = 1; + } + else { + d3d11_res = (ID3D11Resource*) att->image->d3d11_tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + break; + case SG_IMAGETYPE_3D: + SOKOL_ASSERT(!is_msaa); + d3d11_res = (ID3D11Resource*) att->image->d3d11_tex3d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + break; + default: + SOKOL_UNREACHABLE; break; + } + SOKOL_ASSERT(d3d11_res); + HRESULT hr = ID3D11Device_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11_rtvs[i]); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_rtvs[i]); + } + } + + /* optional depth-stencil image */ + SOKOL_ASSERT(0 == pass->ds_att.image); + SOKOL_ASSERT(pass->d3d11_dsv == 0); + att_desc = &desc->depth_stencil_attachment; + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); + att = &pass->ds_att; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[ds_img_index]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + + /* create D3D11 depth-stencil-view */ + D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; + memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); + d3d11_dsv_desc.Format = att->image->d3d11_format; + const bool is_msaa = att->image->sample_count > 1; + if (is_msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + } + ID3D11Resource* d3d11_res = (ID3D11Resource*) att->image->d3d11_texds; + SOKOL_ASSERT(d3d11_res); + HRESULT hr = ID3D11Device_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11_dsv); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11_dsv); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->d3d11_rtvs[i]) { + ID3D11RenderTargetView_Release(pass->d3d11_rtvs[i]); + } + } + if (pass->d3d11_dsv) { + ID3D11DepthStencilView_Release(pass->d3d11_dsv); + } +} + +_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.d3d11.in_pass); + _sg.d3d11.in_pass = true; + _sg.d3d11.cur_width = w; + _sg.d3d11.cur_height = h; + if (pass) { + _sg.d3d11.cur_pass = pass; + _sg.d3d11.cur_pass_id.id = pass->slot.id; + _sg.d3d11.num_rtvs = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = pass->d3d11_rtvs[i]; + if (_sg.d3d11.cur_rtvs[i]) { + _sg.d3d11.num_rtvs++; + } + } + _sg.d3d11.cur_dsv = pass->d3d11_dsv; + } + else { + /* render to default frame buffer */ + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.num_rtvs = 1; + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); + for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); + SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); + } + /* apply the render-target- and depth-stencil-views */ + ID3D11DeviceContext_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); + + /* set viewport and scissor rect to cover whole screen */ + D3D11_VIEWPORT vp; + memset(&vp, 0, sizeof(vp)); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MaxDepth = 1.0f; + ID3D11DeviceContext_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = w; + rect.bottom = h; + ID3D11DeviceContext_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + /* perform clear action */ + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + ID3D11DeviceContext_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], action->colors[i].val); + } + } + UINT ds_flags = 0; + if (action->depth.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { + ID3D11DeviceContext_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.val, action->stencil.val); + } +} + +/* D3D11CalcSubresource only exists for C++ */ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_end_pass(void) { + SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); + _sg.d3d11.in_pass = false; + + /* need to resolve MSAA render target into texture? */ + if (_sg.d3d11.cur_pass) { + SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + _sg_attachment_t* att = &_sg.d3d11.cur_pass->color_atts[i]; + SOKOL_ASSERT(att->image && (att->image->slot.id == att->image_id.id)); + if (att->image->sample_count > 1) { + SOKOL_ASSERT(att->image->d3d11_tex2d && att->image->d3d11_texmsaa && !att->image->d3d11_tex3d); + SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att->image->d3d11_format); + const _sg_image_t* img = att->image; + UINT subres = _sg_d3d11_calcsubresource(att->mip_level, att->slice, img->num_mipmaps); + ID3D11DeviceContext_ResolveSubresource(_sg.d3d11.ctx, + (ID3D11Resource*) img->d3d11_tex2d, /* pDstResource */ + subres, /* DstSubresource */ + (ID3D11Resource*) img->d3d11_texmsaa, /* pSrcResource */ + subres, /* SrcSubresource */ + img->d3d11_format); + } + } + } + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + _sg.d3d11.cur_dsv = 0; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + ID3D11DeviceContext_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); + ID3D11DeviceContext_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + SOKOL_ASSERT(pip->d3d11_rs && pip->d3d11_bs && pip->d3d11_dss && pip->d3d11_il); + + _sg.d3d11.cur_pipeline = pip; + _sg.d3d11.cur_pipeline_id.id = pip->slot.id; + _sg.d3d11.use_indexed_draw = (pip->d3d11_index_format != DXGI_FORMAT_UNKNOWN); + + ID3D11DeviceContext_RSSetState(_sg.d3d11.ctx, pip->d3d11_rs); + ID3D11DeviceContext_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11_dss, pip->d3d11_stencil_ref); + ID3D11DeviceContext_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11_bs, pip->blend_color, 0xFFFFFFFF); + ID3D11DeviceContext_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11_topology); + ID3D11DeviceContext_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11_il); + ID3D11DeviceContext_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_vs, NULL, 0); + ID3D11DeviceContext_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_VS].d3d11_cbs); + ID3D11DeviceContext_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11_fs, NULL, 0); + ID3D11DeviceContext_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->stage[SG_SHADERSTAGE_FS].d3d11_cbs); +} + +_SOKOL_PRIVATE void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + + /* gather all the D3D11 resources into arrays */ + ID3D11Buffer* d3d11_ib = ib ? ib->d3d11_buf : 0; + ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + int i; + for (i = 0; i < num_vbs; i++) { + SOKOL_ASSERT(vbs[i]->d3d11_buf); + d3d11_vbs[i] = vbs[i]->d3d11_buf; + d3d11_vb_offsets[i] = vb_offsets[i]; + } + for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + d3d11_vbs[i] = 0; + d3d11_vb_offsets[i] = 0; + } + for (i = 0; i < num_vs_imgs; i++) { + SOKOL_ASSERT(vs_imgs[i]->d3d11_srv); + SOKOL_ASSERT(vs_imgs[i]->d3d11_smp); + d3d11_vs_srvs[i] = vs_imgs[i]->d3d11_srv; + d3d11_vs_smps[i] = vs_imgs[i]->d3d11_smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_vs_srvs[i] = 0; + d3d11_vs_smps[i] = 0; + } + for (i = 0; i < num_fs_imgs; i++) { + SOKOL_ASSERT(fs_imgs[i]->d3d11_srv); + SOKOL_ASSERT(fs_imgs[i]->d3d11_smp); + d3d11_fs_srvs[i] = fs_imgs[i]->d3d11_srv; + d3d11_fs_smps[i] = fs_imgs[i]->d3d11_smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_fs_srvs[i] = 0; + d3d11_fs_smps[i] = 0; + } + + ID3D11DeviceContext_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11_vb_strides, d3d11_vb_offsets); + ID3D11DeviceContext_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11_index_format, ib_offset); + ID3D11DeviceContext_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); + ID3D11DeviceContext_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); + ID3D11DeviceContext_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); + ID3D11DeviceContext_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); +} + +_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + _SOKOL_UNUSED(num_bytes); + SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); + SOKOL_ASSERT(data && (num_bytes > 0)); + SOKOL_ASSERT((stage_index >= 0) && (stage_index < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(num_bytes == _sg.d3d11.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->stage[stage_index].d3d11_cbs[ub_index]; + SOKOL_ASSERT(cb); + ID3D11DeviceContext_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data, 0, 0); +} + +_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.d3d11.in_pass); + if (_sg.d3d11.use_indexed_draw) { + if (1 == num_instances) { + ID3D11DeviceContext_DrawIndexed(_sg.d3d11.ctx, num_elements, base_element, 0); + } + else { + ID3D11DeviceContext_DrawIndexedInstanced(_sg.d3d11.ctx, num_elements, num_instances, base_element, 0, 0); + } + } + else { + if (1 == num_instances) { + ID3D11DeviceContext_Draw(_sg.d3d11.ctx, num_elements, base_element); + } + else { + ID3D11DeviceContext_DrawInstanced(_sg.d3d11.ctx, num_elements, num_instances, base_element, 0); + } + } +} + +_SOKOL_PRIVATE void _sg_commit(void) { + SOKOL_ASSERT(!_sg.d3d11.in_pass); +} + +_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size) { + SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11_buf); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + memcpy(d3d11_msr.pData, data_ptr, data_size); + ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); +} + +_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data_ptr, int data_size, bool new_frame) { + SOKOL_ASSERT(buf && data_ptr && (data_size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11_buf); + D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0, map_type, 0, &d3d11_msr); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->append_pos; + memcpy(dst_ptr, data_ptr, data_size); + ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11_buf, 0); +} + +_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11_tex2d || img->d3d11_tex3d); + ID3D11Resource* d3d11_res = 0; + if (img->d3d11_tex3d) { + d3d11_res = (ID3D11Resource*) img->d3d11_tex3d; + } + else { + d3d11_res = (ID3D11Resource*) img->d3d11_tex2d; + } + SOKOL_ASSERT(d3d11_res); + const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth:1; + int subres_index = 0; + HRESULT hr; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + const int mip_width = ((img->width>>mip_index)>0) ? img->width>>mip_index : 1; + const int mip_height = ((img->height>>mip_index)>0) ? img->height>>mip_index : 1; + const int src_pitch = _sg_row_pitch(img->pixel_format, mip_width); + const sg_subimage_content* subimg_content = &(data->subimage[face_index][mip_index]); + const int slice_size = subimg_content->size / num_slices; + const int slice_offset = slice_size * slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)subimg_content->ptr) + slice_offset; + hr = ID3D11DeviceContext_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + SOKOL_ASSERT(SUCCEEDED(hr)); + /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + if (src_pitch == (int)d3d11_msr.RowPitch) { + memcpy(d3d11_msr.pData, slice_ptr, slice_size); + } + else { + SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_ptr, src_ptr, src_pitch); + src_ptr += src_pitch; + dst_ptr += d3d11_msr.RowPitch; + } + } + ID3D11DeviceContext_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); + } + } + } +} + +/*== METAL BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_METAL) + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: return MTLLoadActionClear; + case SG_ACTION_LOAD: return MTLLoadActionLoad; + case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return MTLResourceStorageModeShared; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + return MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; + #else + return MTLCPUCacheModeWriteCombined; + #endif + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_texture_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_R10G10B10A2: return MTLPixelFormatRGB10A2Unorm; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_L8: return MTLPixelFormatR8Unorm; + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + case SG_PIXELFORMAT_DXT1: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_DXT3: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_DXT5: return MTLPixelFormatBC3_RGBA; + #else + case SG_PIXELFORMAT_PVRTC2_RGB: return MTLPixelFormatPVRTC_RGB_2BPP; + case SG_PIXELFORMAT_PVRTC4_RGB: return MTLPixelFormatPVRTC_RGB_4BPP; + case SG_PIXELFORMAT_PVRTC2_RGBA: return MTLPixelFormatPVRTC_RGBA_2BPP; + case SG_PIXELFORMAT_PVRTC4_RGBA: return MTLPixelFormatPVRTC_RGBA_4BPP; + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_SRGB8: return MTLPixelFormatETC2_RGB8_sRGB; + #endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_color_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatBGRA8Unorm; /* not a bug */ + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_R10G10B10A2: return MTLPixelFormatRGB10A2Unorm; + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_depth_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: + return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTHSTENCIL: + /* NOTE: Depth24_Stencil8 isn't universally supported! */ + return MTLPixelFormatDepth32Float_Stencil8; + default: + return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_rendertarget_stencil_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTHSTENCIL: + return MTLPixelFormatDepth32Float_Stencil8; + default: + return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE NSUInteger _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_PVRTC2_RGB: + case SG_PIXELFORMAT_PVRTC2_RGBA: + case SG_PIXELFORMAT_PVRTC4_RGB: + case SG_PIXELFORMAT_PVRTC4_RGBA: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterNotMipmapped; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +/*-- a pool for all Metal resource objects, with deferred release queue -------*/ + +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * _sg_def(desc->buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE) + + 5 * _sg_def(desc->image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE) + + 4 * _sg_def(desc->shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE) + + 2 * _sg_def(desc->pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE) + + _sg_def(desc->pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE) + ); + _sg_mtl_idpool = [NSMutableArray arrayWithCapacity:_sg.mtl.idpool.num_slots]; + NSNull* null = [NSNull null]; + for (uint32_t i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg_mtl_idpool addObject:null]; + } + SOKOL_ASSERT([_sg_mtl_idpool count] == _sg.mtl.idpool.num_slots); + /* a queue of currently free slot indices */ + _sg.mtl.idpool.free_queue_top = 0; + _sg.mtl.idpool.free_queue = (uint32_t*)SOKOL_MALLOC(_sg.mtl.idpool.num_slots * sizeof(uint32_t)); + /* pool slot 0 is reserved! */ + for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = (uint32_t)i; + } + /* a circular queue which holds release items (frame index + when a resource is to be released, and the resource's + pool index + */ + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC(_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + for (uint32_t i = 0; i < _sg.mtl.idpool.num_slots; i++) { + _sg.mtl.idpool.release_queue[i].frame_index = 0; + _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _sg_mtl_idpool = nil; +} + +/* get a new free resource pool slot */ +_SOKOL_PRIVATE uint32_t _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const uint32_t slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +/* put a free resource pool slot back into the free-queue */ +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(uint32_t slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ +_SOKOL_PRIVATE uint32_t _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + const uint32_t slot_index = _sg_mtl_alloc_pool_slot(); + SOKOL_ASSERT([NSNull null] == _sg_mtl_idpool[slot_index]); + _sg_mtl_idpool[slot_index] = res; + return slot_index; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, uint32_t slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg_mtl_idpool[slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_front = 0; + } + /* release queue full? */ + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +/* run garbage-collection pass on all resources in the release-queue */ +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + /* don't need to check further, release-items past this are too young */ + break; + } + /* safe to release this resource */ + const uint32_t slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT(_sg_mtl_idpool[slot_index] != [NSNull null]); + _sg_mtl_idpool[slot_index] = [NSNull null]; + /* put the now free pool index back on the free queue */ + _sg_mtl_free_pool_slot(slot_index); + /* reset the release queue slot and advance the back index */ + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +/*-- a very simple sampler cache ----------------------------------------------- + + since there's only a small number of different samplers, sampler objects + will never be deleted (except on shutdown), and searching an identical + sampler is a simple linear search +*/ +/* initialize the sampler cache */ +_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { + _sg.mtl.sampler_cache.capacity = _sg_def(desc->mtl_sampler_cache_size, _SG_MTL_DEFAULT_SAMPLER_CACHE_CAPACITY); + _sg.mtl.sampler_cache.num_items = 0; + const int size = _sg.mtl.sampler_cache.capacity * sizeof(_sg_mtl_sampler_cache_item_t); + _sg.mtl.sampler_cache.items = (_sg_mtl_sampler_cache_item_t*)SOKOL_MALLOC(size); + memset(_sg.mtl.sampler_cache.items, 0, size); +} + +/* destroy the sampler cache, and release all sampler objects */ +_SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { + SOKOL_ASSERT(_sg.mtl.sampler_cache.items); + SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); + for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { + _sg_mtl_release_resource(frame_index, _sg.mtl.sampler_cache.items[i].mtl_sampler_state); + } + SOKOL_FREE(_sg.mtl.sampler_cache.items); _sg.mtl.sampler_cache.items = 0; + _sg.mtl.sampler_cache.num_items = 0; + _sg.mtl.sampler_cache.capacity = 0; +} + +/* + create and add an MTLSamplerStateObject and return its resource pool index, + reuse identical sampler state if one exists +*/ +_SOKOL_PRIVATE uint32_t _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + SOKOL_ASSERT(_sg.mtl.sampler_cache.items); + /* sampler state cache is full */ + const sg_filter min_filter = _sg_def(img_desc->min_filter, SG_FILTER_NEAREST); + const sg_filter mag_filter = _sg_def(img_desc->mag_filter, SG_FILTER_NEAREST); + const sg_wrap wrap_u = _sg_def(img_desc->wrap_u, SG_WRAP_REPEAT); + const sg_wrap wrap_v = _sg_def(img_desc->wrap_v, SG_WRAP_REPEAT); + const sg_wrap wrap_w = _sg_def(img_desc->wrap_w, SG_WRAP_REPEAT); + const uint32_t max_anisotropy = _sg_def(img_desc->max_anisotropy, 1); + /* convert floats to valid int for proper comparison */ + const int min_lod = (int)(img_desc->min_lod * 1000.0f); + const int max_lod = (int)(_sg_def_flt(img_desc->max_lod, 1000.0f) * 1000.0f); + /* first try to find identical sampler, number of samplers will be small, so linear search is ok */ + for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { + _sg_mtl_sampler_cache_item_t* item = &_sg.mtl.sampler_cache.items[i]; + if ((min_filter == item->min_filter) && + (mag_filter == item->mag_filter) && + (wrap_u == item->wrap_u) && + (wrap_v == item->wrap_v) && + (wrap_w == item->wrap_w) && + (max_anisotropy == item->max_anisotropy) && + (min_lod == item->min_lod) && + (max_lod == item->max_lod)) + { + return item->mtl_sampler_state; + } + } + /* fallthrough: need to create a new MTLSamplerState object */ + SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items < _sg.mtl.sampler_cache.capacity); + _sg_mtl_sampler_cache_item_t* new_item = &_sg.mtl.sampler_cache.items[_sg.mtl.sampler_cache.num_items++]; + new_item->min_filter = min_filter; + new_item->mag_filter = mag_filter; + new_item->wrap_u = wrap_u; + new_item->wrap_v = wrap_v; + new_item->wrap_w = wrap_w; + new_item->min_lod = min_lod; + new_item->max_lod = max_lod; + new_item->max_anisotropy = max_anisotropy; + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(wrap_v); + if (SG_IMAGETYPE_3D == img_desc->type) { + mtl_desc.rAddressMode = _sg_mtl_address_mode(wrap_w); + } + mtl_desc.minFilter = _sg_mtl_minmag_filter(min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(mag_filter); + mtl_desc.mipFilter = _sg_mtl_mip_filter(min_filter); + mtl_desc.lodMinClamp = img_desc->min_lod; + mtl_desc.lodMaxClamp = _sg_def_flt(img_desc->max_lod, FLT_MAX); + mtl_desc.maxAnisotropy = max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; + new_item->mtl_sampler_state = _sg_mtl_add_resource(mtl_sampler); + return new_item->mtl_sampler_state; +} + +/*-- a simple state cache for the resource bindings --------------------------*/ +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); +} + +/*-- main Metal backend state and functions ----------------------------------*/ +_SOKOL_PRIVATE void _sg_setup_backend(const sg_desc* desc) { + /* assume already zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->mtl_device); + SOKOL_ASSERT(desc->mtl_renderpass_descriptor_cb); + SOKOL_ASSERT(desc->mtl_drawable_cb); + _sg_mtl_init_pool(desc); + _sg_mtl_init_sampler_cache(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.renderpass_descriptor_cb = desc->mtl_renderpass_descriptor_cb; + _sg.mtl.drawable_cb = desc->mtl_drawable_cb; + _sg.mtl.frame_index = 1; + _sg.mtl.ub_size = _sg_def(desc->mtl_global_uniform_buffer_size, _SG_MTL_DEFAULT_UB_SIZE); + _sg_mtl_sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg_mtl_device = (__bridge id) desc->mtl_device; + _sg_mtl_cmd_queue = [_sg_mtl_device newCommandQueue]; + MTLResourceOptions res_opts = MTLResourceCPUCacheModeWriteCombined; + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + res_opts |= MTLResourceStorageModeManaged; + #endif + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg_mtl_uniform_buffers[i] = [_sg_mtl_device + newBufferWithLength:_sg.mtl.ub_size + options:res_opts + ]; + } +} + +_SOKOL_PRIVATE void _sg_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + /* wait for the last frame to finish */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); + } + _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); + _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + _sg_mtl_cmd_encoder = nil; + _sg_mtl_cmd_buffer = nil; + _sg_mtl_cmd_queue = nil; + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg_mtl_uniform_buffers[i] = nil; + } + _sg_mtl_device = nil; +} + +_SOKOL_PRIVATE bool _sg_query_feature(sg_feature f) { + switch (f) { + case SG_FEATURE_INSTANCING: + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + case SG_FEATURE_TEXTURE_COMPRESSION_DXT: + #else + case SG_FEATURE_TEXTURE_COMPRESSION_PVRTC: + case SG_FEATURE_TEXTURE_COMPRESSION_ETC2: + #endif + case SG_FEATURE_TEXTURE_FLOAT: + case SG_FEATURE_ORIGIN_TOP_LEFT: + case SG_FEATURE_MSAA_RENDER_TARGETS: + case SG_FEATURE_PACKED_VERTEX_FORMAT_10_2: + case SG_FEATURE_MULTIPLE_RENDER_TARGET: + case SG_FEATURE_IMAGETYPE_3D: + case SG_FEATURE_IMAGETYPE_ARRAY: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE void _sg_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE void _sg_activate_context(_sg_context_t* ctx) { + _sg_reset_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + buf->size = desc->size; + buf->append_pos = 0; + buf->append_overflow = false; + buf->type = _sg_def(desc->type, SG_BUFFERTYPE_VERTEXBUFFER); + buf->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + buf->update_frame_index = 0; + buf->append_frame_index = 0; + buf->num_slots = (buf->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + buf->active_slot = 0; + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->usage); + for (int slot = 0; slot < buf->num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } + else { + if (buf->usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->content); + mtl_buf = [_sg_mtl_device newBufferWithBytes:desc->content length:buf->size options:mtl_options]; + } + else { + mtl_buf = [_sg_mtl_device newBufferWithLength:buf->size options:mtl_options]; + } + } + buf->mtl_buf[slot] = _sg_mtl_add_resource(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + for (int slot = 0; slot < buf->num_slots; slot++) { + /* it's valid to call release resource with '0' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl_buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_content(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_content* content) { + const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->type == SG_IMAGETYPE_ARRAY) ? img->depth : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->num_mipmaps; mip_index++) { + SOKOL_ASSERT(content->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(content->subimage[face_index][mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)content->subimage[face_index][mip_index].ptr; + const int mip_width = _sg_max(img->width >> mip_index, 1); + const int mip_height = _sg_max(img->height >> mip_index, 1); + /* special case PVRTC formats: bytePerRow must be 0 */ + int bytes_per_row = 0; + int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); + if (!_sg_mtl_is_pvrtc(img->pixel_format)) { + bytes_per_row = _sg_row_pitch(img->pixel_format, mip_width); + } + MTLRegion region; + if (img->type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_max(img->depth >> mip_index, 1); + region = MTLRegionMake3D(0, 0, 0, mip_width, mip_height, mip_depth); + /* FIXME: apparently the minimal bytes_per_image size for 3D texture + is 4 KByte... somehow need to handle this */ + } + else { + region = MTLRegionMake2D(0, 0, mip_width, mip_height); + } + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int mtl_slice_index = (img->type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)content->subimage[face_index][mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:mip_index + slice:mtl_slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:bytes_per_row + bytesPerImage:bytes_per_slice]; + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + img->type = _sg_def(desc->type, SG_IMAGETYPE_2D); + img->render_target = desc->render_target; + img->width = desc->width; + img->height = desc->height; + img->depth = _sg_def(desc->depth, 1); + img->num_mipmaps = _sg_def(desc->num_mipmaps, 1); + img->usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + img->pixel_format = _sg_def(desc->pixel_format, SG_PIXELFORMAT_RGBA8); + img->sample_count = _sg_def(desc->sample_count, 1); + img->min_filter = _sg_def(desc->min_filter, SG_FILTER_NEAREST); + img->mag_filter = _sg_def(desc->mag_filter, SG_FILTER_NEAREST); + img->wrap_u = _sg_def(desc->wrap_u, SG_WRAP_REPEAT); + img->wrap_v = _sg_def(desc->wrap_v, SG_WRAP_REPEAT); + img->wrap_w = _sg_def(desc->wrap_w, SG_WRAP_REPEAT); + img->max_anisotropy = _sg_def(desc->max_anisotropy, 1); + img->upd_frame_index = 0; + img->num_slots = (img->usage == SG_USAGE_IMMUTABLE) ? 1 :SG_NUM_INFLIGHT_FRAMES; + img->active_slot = 0; + const bool injected = (0 != desc->mtl_textures[0]); + + /* first initialize all Metal resource pool slots to 'empty' */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl_tex[i] = _sg_mtl_add_resource(nil); + } + img->mtl_sampler_state = _sg_mtl_add_resource(nil); + img->mtl_depth_tex = _sg_mtl_add_resource(nil); + img->mtl_msaa_tex = _sg_mtl_add_resource(nil); + + /* initialize a Metal texture descriptor with common attributes */ + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + mtl_desc.textureType = _sg_mtl_texture_type(img->type); + if (img->render_target) { + if (_sg_is_valid_rendertarget_color_format(img->pixel_format)) { + mtl_desc.pixelFormat = _sg_mtl_rendertarget_color_format(img->pixel_format); + } + else { + mtl_desc.pixelFormat = _sg_mtl_rendertarget_depth_format(img->pixel_format); + } + } + else { + mtl_desc.pixelFormat = _sg_mtl_texture_format(img->pixel_format); + } + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + SOKOL_LOG("Unsupported texture pixel format!\n"); + return SG_RESOURCESTATE_FAILED; + } + mtl_desc.width = img->width; + mtl_desc.height = img->height; + if (SG_IMAGETYPE_3D == img->type) { + mtl_desc.depth = img->depth; + } + else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = img->num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->type) { + mtl_desc.arrayLength = img->depth; + } + else { + mtl_desc.arrayLength = 1; + } + if (img->render_target) { + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; + mtl_desc.storageMode = MTLStorageModePrivate; + mtl_desc.usage |= MTLTextureUsageRenderTarget; + } + + /* special case depth-stencil-buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->pixel_format)) { + /* create only a depth texture */ + SOKOL_ASSERT(img->render_target); + SOKOL_ASSERT(img->type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->num_mipmaps == 1); + SOKOL_ASSERT(!injected); + if (img->sample_count > 1) { + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.sampleCount = img->sample_count; + } + id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; + SOKOL_ASSERT(nil != tex); + img->mtl_depth_tex = _sg_mtl_add_resource(tex); + } + else { + /* create the color texture(s) */ + for (int slot = 0; slot < img->num_slots; slot++) { + id tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + tex = (__bridge id) desc->mtl_textures[slot]; + } + else { + tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; + if ((img->usage == SG_USAGE_IMMUTABLE) && !img->render_target) { + _sg_mtl_copy_image_content(img, tex, &desc->content); + } + } + img->mtl_tex[slot] = _sg_mtl_add_resource(tex); + } + + /* if MSAA color render target, create an additional MSAA render-surface texture */ + if (img->render_target && (img->sample_count > 1)) { + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.depth = 1; + mtl_desc.arrayLength = 1; + mtl_desc.mipmapLevelCount = 1; + mtl_desc.sampleCount = img->sample_count; + id tex = [_sg_mtl_device newTextureWithDescriptor:mtl_desc]; + img->mtl_msaa_tex = _sg_mtl_add_resource(tex); + } + + /* create (possibly shared) sampler state */ + img->mtl_sampler_state = _sg_mtl_create_sampler(_sg_mtl_device, desc); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + /* it's valid to call release resource with a 'null resource' */ + for (int slot = 0; slot < img->num_slots; slot++) { + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_tex[slot]); + } + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_depth_tex); + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl_msaa_tex); + /* NOTE: sampler state objects are shared and not released until shutdown */ +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg_mtl_device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const uint8_t* ptr, int num_bytes) { + NSError* err = NULL; + dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id lib = [_sg_mtl_device newLibraryWithData:lib_data error:&err]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + /* uniform block sizes and image types */ + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &shd->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + _sg_uniform_block_t* ub = &stage->uniform_blocks[ub_index]; + ub->size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].type = img_desc->type; + stage->num_images++; + } + } + + /* create metal libray objects and lookup entry functions */ + id vs_lib; + id fs_lib; + id vs_func; + id fs_func; + const char* vs_entry = _sg_def(desc->vs.entry, "_main"); + const char* fs_entry = _sg_def(desc->fs.entry, "_main"); + if (desc->vs.byte_code && desc->fs.byte_code) { + /* separate byte code provided */ + vs_lib = _sg_mtl_library_from_bytecode(desc->vs.byte_code, desc->vs.byte_code_size); + fs_lib = _sg_mtl_library_from_bytecode(desc->fs.byte_code, desc->fs.byte_code_size); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else if (desc->vs.source && desc->fs.source) { + /* separate sources provided */ + vs_lib = _sg_mtl_compile_library(desc->vs.source); + fs_lib = _sg_mtl_compile_library(desc->fs.source); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else { + return SG_RESOURCESTATE_FAILED; + } + if (nil == vs_func) { + SOKOL_LOG("vertex shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + if (nil == fs_func) { + SOKOL_LOG("fragment shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ + shd->stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + shd->stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + shd->stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + shd->stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->stage[SG_SHADERSTAGE_FS].mtl_lib); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + + pip->shader = shd; + pip->shader_id = desc->shader; + pip->color_attachment_count = _sg_def(desc->blend.color_attachment_count, 1); + pip->color_format = _sg_def(desc->blend.color_format, SG_PIXELFORMAT_RGBA8); + pip->depth_format = _sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL); + pip->sample_count = _sg_def(desc->rasterizer.sample_count, 1); + pip->depth_bias = desc->rasterizer.depth_bias; + pip->depth_bias_slope_scale = desc->rasterizer.depth_bias_slope_scale; + pip->depth_bias_clamp = desc->rasterizer.depth_bias_clamp; + sg_primitive_type prim_type = _sg_def(desc->primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + pip->mtl_prim_type = _sg_mtl_primitive_type(prim_type); + pip->index_type = _sg_def(desc->index_type, SG_INDEXTYPE_NONE); + pip->mtl_index_size = _sg_mtl_index_size(pip->index_type); + if (SG_INDEXTYPE_NONE != pip->index_type) { + pip->mtl_index_type = _sg_mtl_index_type(pip->index_type); + } + pip->mtl_cull_mode = _sg_mtl_cull_mode(_sg_def(desc->rasterizer.cull_mode, SG_CULLMODE_NONE)); + pip->mtl_winding = _sg_mtl_winding(_sg_def(desc->rasterizer.face_winding, SG_FACEWINDING_CW)); + pip->mtl_stencil_ref = desc->depth_stencil.stencil_ref; + for (int i = 0; i < 4; i++) { + pip->blend_color[i] = desc->blend.blend_color[i]; + } + + /* create vertex-descriptor */ + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + auto_offset[layout_index] = 0; + } + /* to use computed offsets, *all* attr offsets must be 0 */ + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + if (desc->layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + break; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT((a_desc->buffer_index >= 0) && (a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS)); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); + vtx_desc.attributes[attr_index].offset = use_auto_offset ? auto_offset[a_desc->buffer_index] : a_desc->offset; + vtx_desc.attributes[attr_index].bufferIndex = a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS; + auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + pip->vertex_layout_valid[a_desc->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + const int mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; + const int stride = l_desc->stride ? l_desc->stride : auto_offset[layout_index]; + SOKOL_ASSERT(stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(_sg_def(l_desc->step_func, SG_VERTEXSTEP_PER_VERTEX)); + vtx_desc.layouts[mtl_vb_slot].stepRate = _sg_def(l_desc->step_rate, 1); + } + } + + /* render-pipeline descriptor */ + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_VS].mtl_func]; + SOKOL_ASSERT(shd->stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_idpool[shd->stage[SG_SHADERSTAGE_FS].mtl_func]; + rp_desc.sampleCount = _sg_def(desc->rasterizer.sample_count, 1); + rp_desc.alphaToCoverageEnabled = desc->rasterizer.alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_rendertarget_depth_format(_sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL)); + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_rendertarget_stencil_format(_sg_def(desc->blend.depth_format, SG_PIXELFORMAT_DEPTHSTENCIL)); + /* FIXME: this only works on macOS 10.13! + for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + */ + const int att_count = _sg_def(desc->blend.color_attachment_count, 1); + for (int i = 0; i < att_count; i++) { + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_rendertarget_color_format(_sg_def(desc->blend.color_format, SG_PIXELFORMAT_RGBA8)); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask((sg_color_mask)_sg_def(desc->blend.color_write_mask, SG_COLORMASK_RGBA)); + rp_desc.colorAttachments[i].blendingEnabled = desc->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(_sg_def(desc->blend.op_alpha, SG_BLENDOP_ADD)); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(_sg_def(desc->blend.op_rgb, SG_BLENDOP_ADD)); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(_sg_def(desc->blend.dst_factor_alpha, SG_BLENDFACTOR_ZERO)); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(_sg_def(desc->blend.dst_factor_rgb, SG_BLENDFACTOR_ZERO)); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(_sg_def(desc->blend.src_factor_alpha, SG_BLENDFACTOR_ONE)); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(_sg_def(desc->blend.src_factor_rgb, SG_BLENDFACTOR_ONE)); + } + NSError* err = NULL; + id mtl_rps = [_sg_mtl_device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + SOKOL_LOG([err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + + /* depth-stencil-state */ + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(_sg_def(desc->depth_stencil.depth_compare_func, SG_COMPAREFUNC_ALWAYS)); + ds_desc.depthWriteEnabled = desc->depth_stencil.depth_write_enabled; + if (desc->depth_stencil.stencil_enabled) { + const sg_stencil_state* sb = &desc->depth_stencil.stencil_back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(_sg_def(sb->fail_op, SG_STENCILOP_KEEP)); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(_sg_def(sb->depth_fail_op, SG_STENCILOP_KEEP)); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(_sg_def(sb->pass_op, SG_STENCILOP_KEEP)); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(_sg_def(sb->compare_func, SG_COMPAREFUNC_ALWAYS)); + ds_desc.backFaceStencil.readMask = desc->depth_stencil.stencil_read_mask; + ds_desc.backFaceStencil.writeMask = desc->depth_stencil.stencil_write_mask; + const sg_stencil_state* sf = &desc->depth_stencil.stencil_front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(_sg_def(sf->fail_op, SG_STENCILOP_KEEP)); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(_sg_def(sf->depth_fail_op, SG_STENCILOP_KEEP)); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(_sg_def(sf->pass_op, SG_STENCILOP_KEEP)); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(_sg_def(sf->compare_func, SG_COMPAREFUNC_ALWAYS)); + ds_desc.frontFaceStencil.readMask = desc->depth_stencil.stencil_read_mask; + ds_desc.frontFaceStencil.writeMask = desc->depth_stencil.stencil_write_mask; + } + id mtl_dss = [_sg_mtl_device newDepthStencilStateWithDescriptor:ds_desc]; + + pip->mtl_rps = _sg_mtl_add_resource(mtl_rps); + pip->mtl_dss = _sg_mtl_add_resource(mtl_dss); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + /* it's valid to call release resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_rps); + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl_dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + + /* copy image pointers and desc attributes */ + const sg_attachment_desc* att_desc; + _sg_attachment_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + SOKOL_ASSERT(0 == pass->color_atts[i].image); + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + pass->num_color_atts++; + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->pixel_format)); + att = &pass->color_atts[i]; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + SOKOL_ASSERT(0 == pass->ds_att.image); + att_desc = &desc->depth_stencil_attachment; + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->pixel_format)); + att = &pass->ds_att; + SOKOL_ASSERT((att->image == 0) && (att->image_id.id == SG_INVALID_ID)); + att->image = att_images[ds_img_index]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(_sg_mtl_cmd_queue); + SOKOL_ASSERT(!_sg_mtl_cmd_encoder); + SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb); + _sg.mtl.in_pass = true; + _sg.mtl.cur_width = w; + _sg.mtl.cur_height = h; + _sg_mtl_clear_state_cache(); + + /* if this is the first pass in the frame, create a command buffer */ + if (nil == _sg_mtl_cmd_buffer) { + /* block until the oldest frame in flight has finished */ + dispatch_semaphore_wait(_sg_mtl_sem, DISPATCH_TIME_FOREVER); + _sg_mtl_cmd_buffer = [_sg_mtl_cmd_queue commandBufferWithUnretainedReferences]; + } + + /* if this is first pass in frame, get uniform buffer base pointer */ + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + /* initialize a render pass descriptor */ + MTLRenderPassDescriptor* pass_desc = nil; + if (pass) { + /* offscreen render pass */ + pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + } + else { + /* default render pass, call user-provided callback to provide render pass descriptor */ + pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); + + } + if (pass_desc) { + _sg.mtl.pass_valid = true; + } + else { + /* default pass descriptor will not be valid if window is minimized, + don't do any rendering in this case */ + _sg.mtl.pass_valid = false; + return; + } + if (pass) { + /* setup pass descriptor for offscreen rendering */ + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_attachment_t* att = &pass->color_atts[i]; + if (0 == att->image) { + break; + } + SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(att->image->slot.id == att->image_id.id); + const bool is_msaa = (att->image->sample_count > 1); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); + pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; + const float* c = &(action->colors[i].val[0]); + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c[0], c[1], c[2], c[3]); + if (is_msaa) { + SOKOL_ASSERT(att->image->mtl_msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_msaa_tex]; + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; + pass_desc.colorAttachments[i].resolveLevel = att->mip_level; + switch (att->image->type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = att->slice; + break; + default: break; + } + } + else { + SOKOL_ASSERT(att->image->mtl_tex[att->image->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_idpool[att->image->mtl_tex[att->image->active_slot]]; + pass_desc.colorAttachments[i].level = att->mip_level; + switch (att->image->type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = att->slice; + break; + default: break; + } + } + } + if (0 != pass->ds_att.image) { + const _sg_attachment_t* att = &pass->ds_att; + SOKOL_ASSERT(att->image->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(att->image->slot.id == att->image_id.id); + SOKOL_ASSERT(att->image->mtl_depth_tex != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.val; + if (_sg_is_depth_stencil_format(att->image->pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_idpool[att->image->mtl_depth_tex]; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.val; + } + } + } + else { + /* setup pass descriptor for default rendering */ + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); + const float* c = &(action->colors[0].val[0]); + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c[0], c[1], c[2], c[3]); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.val; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.val; + } + + /* create a render command encoder, this might return nil if window is minimized */ + _sg_mtl_cmd_encoder = [_sg_mtl_cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (_sg_mtl_cmd_encoder == nil) { + _sg.mtl.pass_valid = false; + return; + } + + /* bind the global uniform buffer, this only happens once per pass */ + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg_mtl_cmd_encoder + setVertexBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + [_sg_mtl_cmd_encoder + setFragmentBuffer:_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:slot]; + } +} + +_SOKOL_PRIVATE void _sg_end_pass(void) { + SOKOL_ASSERT(_sg.mtl.in_pass); + _sg.mtl.in_pass = false; + _sg.mtl.pass_valid = false; + if (nil != _sg_mtl_cmd_encoder) { + [_sg_mtl_cmd_encoder endEncoding]; + _sg_mtl_cmd_encoder = nil; + } +} + +_SOKOL_PRIVATE void _sg_commit(void) { + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(!_sg.mtl.pass_valid); + SOKOL_ASSERT(_sg.mtl.drawable_cb); + SOKOL_ASSERT(nil == _sg_mtl_cmd_encoder); + SOKOL_ASSERT(nil != _sg_mtl_cmd_buffer); + + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + [_sg_mtl_uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, _sg.mtl.cur_ub_offset)]; + #endif + + /* present, commit and signal semaphore when done */ + id cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); + [_sg_mtl_cmd_buffer presentDrawable:cur_drawable]; + __block dispatch_semaphore_t sem = _sg_mtl_sem; + [_sg_mtl_cmd_buffer addCompletedHandler:^(id cmd_buffer) { + dispatch_semaphore_signal(sem); + }]; + [_sg_mtl_cmd_buffer commit]; + + /* garbage-collect resources pending for release */ + _sg_mtl_garbage_collect(_sg.mtl.frame_index); + + /* rotate uniform buffer slot */ + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.frame_index++; + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + _sg_mtl_cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg_mtl_cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); + if ((x + w) > _sg.mtl.cur_width) { + w = _sg.mtl.cur_width - x; + } + if ((y + h) > _sg.mtl.cur_height) { + h = _sg.mtl.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + MTLScissorRect r; + r.x = x; + r.y = origin_top_left ? y : (_sg.mtl.cur_height - (y + h)); + r.width = w; + r.height = h; + [_sg_mtl_cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader); + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + + if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.mtl.state_cache.cur_pipeline = pip; + _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; + const float* c = pip->blend_color; + [_sg_mtl_cmd_encoder setBlendColorRed:c[0] green:c[1] blue:c[2] alpha:c[3]]; + [_sg_mtl_cmd_encoder setCullMode:pip->mtl_cull_mode]; + [_sg_mtl_cmd_encoder setFrontFacingWinding:pip->mtl_winding]; + [_sg_mtl_cmd_encoder setStencilReferenceValue:pip->mtl_stencil_ref]; + [_sg_mtl_cmd_encoder setDepthBias:pip->depth_bias slopeScale:pip->depth_bias_slope_scale clamp:pip->depth_bias_clamp]; + SOKOL_ASSERT(pip->mtl_rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setRenderPipelineState:_sg_mtl_idpool[pip->mtl_rps]]; + SOKOL_ASSERT(pip->mtl_dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setDepthStencilState:_sg_mtl_idpool[pip->mtl_dss]]; + } +} + +_SOKOL_PRIVATE void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + + /* store index buffer binding, this will be needed later in sg_draw() */ + _sg.mtl.state_cache.cur_indexbuffer = ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; + if (ib) { + SOKOL_ASSERT(pip->index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; + } + else { + SOKOL_ASSERT(pip->index_type == SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; + } + + /* apply vertex buffers */ + int slot; + for (slot = 0; slot < num_vbs; slot++) { + const _sg_buffer_t* vb = vbs[slot]; + if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || + (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) + { + _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + SOKOL_ASSERT(vb->mtl_buf[vb->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setVertexBuffer:_sg_mtl_idpool[vb->mtl_buf[vb->active_slot]] + offset:vb_offsets[slot] + atIndex:mtl_slot]; + } + } + + /* apply vertex shader images */ + for (slot = 0; slot < num_vs_imgs; slot++) { + const _sg_image_t* img = vs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_vs_images[slot] = img; + _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setVertexTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; + SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setVertexSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; + } + } + + /* apply fragment shader images */ + for (slot = 0; slot < num_fs_imgs; slot++) { + const _sg_image_t* img = fs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_fs_images[slot] = img; + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl_tex[img->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setFragmentTexture:_sg_mtl_idpool[img->mtl_tex[img->active_slot]] atIndex:slot]; + SOKOL_ASSERT(img->mtl_sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg_mtl_cmd_encoder setFragmentSamplerState:_sg_mtl_idpool[img->mtl_sampler_state] atIndex:slot]; + } + } +} + +#define _sg_mtl_roundup(val, round_to) (((val)+((round_to)-1))&~((round_to)-1)) + +_SOKOL_PRIVATE void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(data && (num_bytes > 0)); + SOKOL_ASSERT((stage_index >= 0) && ((int)stage_index < SG_NUM_SHADER_STAGES)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset + num_bytes) <= _sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(num_bytes <= _sg.mtl.state_cache.cur_pipeline->shader->stage[stage_index].uniform_blocks[ub_index].size); + + /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ + uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; + memcpy(dst, data, num_bytes); + if (stage_index == SG_SHADERSTAGE_VS) { + [_sg_mtl_cmd_encoder setVertexBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; + } + else { + [_sg_mtl_cmd_encoder setFragmentBufferOffset:_sg.mtl.cur_ub_offset atIndex:ub_index]; + } + _sg.mtl.cur_ub_offset = _sg_mtl_roundup(_sg.mtl.cur_ub_offset + num_bytes, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(_sg_mtl_cmd_encoder); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->index_type) { + /* indexed rendering */ + SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); + const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; + SOKOL_ASSERT(ib->mtl_buf[ib->active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger index_buffer_offset = _sg.mtl.state_cache.cur_indexbuffer_offset + + base_element * _sg.mtl.state_cache.cur_pipeline->mtl_index_size; + [_sg_mtl_cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type + indexCount:num_elements + indexType:_sg.mtl.state_cache.cur_pipeline->mtl_index_type + indexBuffer:_sg_mtl_idpool[ib->mtl_buf[ib->active_slot]] + indexBufferOffset:index_buffer_offset + instanceCount:num_instances]; + } + else { + /* non-indexed rendering */ + [_sg_mtl_cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl_prim_type + vertexStart:base_element + vertexCount:num_elements + instanceCount:num_instances]; + } +} + +_SOKOL_PRIVATE void _sg_update_buffer(_sg_buffer_t* buf, const void* data, int data_size) { + SOKOL_ASSERT(buf && data && (data_size > 0)); + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data, data_size); + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + [mtl_buf didModifyRange:NSMakeRange(0, data_size)]; + #endif +} + +_SOKOL_PRIVATE void _sg_append_buffer(_sg_buffer_t* buf, const void* data, int data_size, bool new_frame) { + SOKOL_ASSERT(buf && data && (data_size > 0)); + if (new_frame) { + if (++buf->active_slot >= buf->num_slots) { + buf->active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_idpool[buf->mtl_buf[buf->active_slot]]; + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->append_pos; + memcpy(dst_ptr, data, data_size); + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + [mtl_buf didModifyRange:NSMakeRange(buf->append_pos, data_size)]; + #endif +} + +_SOKOL_PRIVATE void _sg_update_image(_sg_image_t* img, const sg_image_content* data) { + SOKOL_ASSERT(img && data); + if (++img->active_slot >= img->num_slots) { + img->active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_idpool[img->mtl_tex[img->active_slot]]; + _sg_mtl_copy_image_content(img, mtl_tex, data); +} + +#endif + +/*== RESOURCE POOLS ==========================================================*/ + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * pool->size; + pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + memset(pool->gen_ctrs, 0, gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int)*num); + SOKOL_ASSERT(pool->free_queue); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_FREE(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + SOKOL_FREE(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + memset(buf, 0, sizeof(_sg_buffer_t)); +} + +_SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + memset(img, 0, sizeof(_sg_image_t)); +} + +_SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + memset(shd, 0, sizeof(_sg_shader_t)); +} + +_SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + memset(pip, 0, sizeof(_sg_pipeline_t)); +} + +_SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + memset(pass, 0, sizeof(_sg_pass_t)); +} + +_SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + memset(ctx, 0, sizeof(_sg_context_t)); +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((desc->buffer_pool_size >= 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->buffer_pool, _sg_def(desc->buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE)); + size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * p->buffer_pool.size; + p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); + SOKOL_ASSERT(p->buffers); + memset(p->buffers, 0, buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size >= 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->image_pool, _sg_def(desc->image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE)); + size_t image_pool_byte_size = sizeof(_sg_image_t) * p->image_pool.size; + p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); + SOKOL_ASSERT(p->images); + memset(p->images, 0, image_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size >= 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->shader_pool, _sg_def(desc->shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE)); + size_t shader_pool_byte_size = sizeof(_sg_shader_t) * p->shader_pool.size; + p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); + SOKOL_ASSERT(p->shaders); + memset(p->shaders, 0, shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size >= 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pipeline_pool, _sg_def(desc->pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE)); + size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * p->pipeline_pool.size; + p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); + SOKOL_ASSERT(p->pipelines); + memset(p->pipelines, 0, pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->pass_pool_size >= 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pass_pool, _sg_def(desc->pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE)); + size_t pass_pool_byte_size = sizeof(_sg_pass_t) * p->pass_pool.size; + p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); + SOKOL_ASSERT(p->passes); + memset(p->passes, 0, pass_pool_byte_size); + + SOKOL_ASSERT((desc->context_pool_size >= 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->context_pool, _sg_def(desc->context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE)); + size_t context_pool_byte_size = sizeof(_sg_context_t) * p->context_pool.size; + p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); + SOKOL_ASSERT(p->contexts); + memset(p->contexts, 0, context_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + SOKOL_FREE(p->contexts); p->contexts = 0; + SOKOL_FREE(p->passes); p->passes = 0; + SOKOL_FREE(p->pipelines); p->pipelines = 0; + SOKOL_FREE(p->shaders); p->shaders = 0; + SOKOL_FREE(p->images); p->images = 0; + SOKOL_FREE(p->buffers); p->buffers = 0; + _sg_discard_pool(&p->context_pool); + _sg_discard_pool(&p->pass_pool); + _sg_discard_pool(&p->pipeline_pool); + _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->image_pool); + _sg_discard_pool(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* returns pointer to resource by id without matching id check */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); + return &p->buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); + return &p->images[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); + return &p->shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); + return &p->pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); + int slot_index = _sg_slot_index(pass_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); + return &p->passes[slot_index]; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); + int slot_index = _sg_slot_index(context_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); + return &p->contexts[slot_index]; +} + +/* returns pointer to resource with matching id check, may return 0 */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(p, img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(p, shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pass_id) { + _sg_pass_t* pass = _sg_pass_at(p, pass_id); + if (pass->slot.id == pass_id) { + return pass; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != ctx_id) { + _sg_context_t* ctx = _sg_context_at(p, ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +_SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { + /* this is a bit dumb since it loops over all pool slots to + find the occupied slots, on the other hand it is only ever + executed at shutdown + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < p->buffer_pool.size; i++) { + if (p->buffers[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_buffer(&p->buffers[i]); + } + } + } + for (int i = 1; i < p->image_pool.size; i++) { + if (p->images[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_image(&p->images[i]); + } + } + } + for (int i = 1; i < p->shader_pool.size; i++) { + if (p->shaders[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_shader(&p->shaders[i]); + } + } + } + for (int i = 1; i < p->pipeline_pool.size; i++) { + if (p->pipelines[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pipeline(&p->pipelines[i]); + } + } + } + for (int i = 1; i < p->pass_pool.size; i++) { + if (p->passes[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->passes[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pass(&p->passes[i]); + } + } + } +} + +/*== VALIDATION LAYER ========================================================*/ +#if defined(SOKOL_DEBUG) +/* return a human readable string for an _sg_validate_error */ +_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { + switch (err) { + /* buffer creation validation errors */ + case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; + case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; + case _SG_VALIDATE_BUFFERDESC_CONTENT: return "immutable buffers must be initialized with content (sg_buffer_desc.content)"; + case _SG_VALIDATE_BUFFERDESC_NO_CONTENT: return "dynamic/stream usage buffers cannot be initialized with content"; + + /* image creation validation errros */ + case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; + case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; + case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; + case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; + case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; + case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; + case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA render targets not supported (SG_FEATURE_MSAA_RENDER_TARGETS)"; + case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; + case _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT: return "render target images cannot be initialized with content"; + case _SG_VALIDATE_IMAGEDESC_CONTENT: return "missing or invalid content for immutable image"; + case _SG_VALIDATE_IMAGEDESC_NO_CONTENT: return "dynamic/stream usage images cannot be initialized with content"; + + /* shader creation */ + case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; + case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; + case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; + case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; + case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; + case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; + case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; + + /* pipeline creation */ + case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; + case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; + case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; + case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL vertex layouts must have attribute names"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 vertex layouts must have attribute semantics (sem_name and sem_index)"; + + /* pass creation */ + case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; + case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; + case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; + case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; + case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; + case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; + case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; + case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; + case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; + case _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS: return "all pass color attachment images must have the same pixel format"; + case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; + case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; + case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; + case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; + + /* sg_begin_pass */ + case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; + case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; + + /* sg_apply_pipeline */ + case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; + case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; + case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; + case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; + case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; + case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: color_attachment_count in pipeline doesn't match number of pass color attachments"; + case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: color_format in pipeline doesn't match pass color attachment pixel format"; + case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: depth_format in pipeline doesn't match pass depth attachment pixel format"; + case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: MSAA sample count in pipeline doesn't match render pass attachment sample count"; + + /* sg_apply_bindings */ + case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; + case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; + case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; + case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; + case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; + case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; + case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; + case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; + case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; + case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; + case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; + case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; + case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; + case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; + case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; + + /* sg_apply_uniforms */ + case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; + case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; + case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; + + /* sg_update_buffer */ + case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; + case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; + case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; + case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; + + /* sg_append_buffer */ + case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; + case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; + case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; + + /* sg_update_image */ + case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; + case _SG_VALIDATE_UPDIMG_NOTENOUGHDATA: return "sg_update_image: not enough subimage data provided"; + case _SG_VALIDATE_UPDIMG_SIZE: return "sg_update_image: provided subimage data size too big"; + case _SG_VALIDATE_UPDIMG_COMPRESSED: return "sg_update_image: cannot update images with compressed format"; + case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; + + default: return "unknown validation error"; + } +} +#endif /* defined(SOKOL_DEBUG) */ + +/*-- validation checks -------------------------------------------------------*/ +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = _SG_VALIDATE_SUCCESS; +} + +_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { + if (!cond) { + _sg.validate_error = err; + SOKOL_LOG(_sg_validate_string(err)); + } +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { + #if !defined(SOKOL_VALIDATE_NON_FATAL) + SOKOL_LOG("^^^^ VALIDATION FAILED, TERMINATING ^^^^"); + SOKOL_ASSERT(false); + #endif + return false; + } + else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); + bool ext = (0 != desc->gl_buffers[0]) || (0 != desc->mtl_buffers[0]) || (0 != desc->d3d11_buffer); + if (!ext && (_sg_def(desc->usage, SG_USAGE_IMMUTABLE) == SG_USAGE_IMMUTABLE)) { + SOKOL_VALIDATE(0 != desc->content, _SG_VALIDATE_BUFFERDESC_CONTENT); + } + else { + SOKOL_VALIDATE(0 == desc->content, _SG_VALIDATE_BUFFERDESC_NO_CONTENT); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); + SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + const sg_pixel_format fmt = _sg_def(desc->pixel_format, SG_PIXELFORMAT_RGBA8); + const sg_usage usage = _sg_def(desc->usage, SG_USAGE_IMMUTABLE); + const bool ext = (0 != desc->gl_textures[0]) || (0 != desc->mtl_textures[0]) || (0 != desc->d3d11_texture); + if (desc->render_target) { + if (desc->sample_count > 1) { + SOKOL_VALIDATE(_sg_query_feature(SG_FEATURE_MSAA_RENDER_TARGETS), _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + } + const bool valid_color_fmt = _sg_is_valid_rendertarget_color_format(fmt); + const bool valid_depth_fmt = _sg_is_valid_rendertarget_depth_format(fmt); + SOKOL_VALIDATE(valid_color_fmt || valid_depth_fmt, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); + SOKOL_VALIDATE(desc->content.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_CONTENT); + } + else { + SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); + SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + /* FIXME: should use the same "expected size" computation as in _sg_validate_update_image() here */ + if (!ext && (usage == SG_USAGE_IMMUTABLE)) { + const int num_faces = _sg_def(desc->type, SG_IMAGETYPE_2D)==SG_IMAGETYPE_CUBE ? 6:1; + const int num_mips = _sg_def(desc->num_mipmaps, 1); + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = desc->content.subimage[face_index][mip_index].ptr != 0; + const bool has_size = desc->content.subimage[face_index][mip_index].size > 0; + SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDESC_CONTENT); + } + } + } + else { + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->content.subimage[face_index][mip_index].ptr; + const bool no_size = 0 == desc->content.subimage[face_index][mip_index].size; + SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_NO_CONTENT); + } + } + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + /* on GL, must provide shader source code */ + SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11_SHADER_COMPILER) + /* on Metal or D3D with shader compiler, must provide shader source code or byte code */ + SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.byte_code), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + #elif defined(SOKOL_D3D11) + /* on D3D11 without shader compiler, must provide byte code */ + SOKOL_VALIDATE(0 != desc->vs.byte_code, _SG_VALIDATE_SHADERDESC_BYTECODE); + SOKOL_VALIDATE(0 != desc->fs.byte_code, _SG_VALIDATE_SHADERDESC_BYTECODE); + #else + /* Dummy Backend, don't require source or bytecode */ + #endif + /* if shader byte code, the size must also be provided */ + if (0 != desc->vs.byte_code) { + SOKOL_VALIDATE(desc->vs.byte_code_size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fs.byte_code) { + SOKOL_VALIDATE(desc->fs.byte_code_size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; + bool uniform_blocks_continuous = true; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (ub_desc->size > 0) { + SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); + bool uniforms_continuous = true; + int uniform_offset = 0; + int num_uniforms = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + SOKOL_VALIDATE(u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); + #endif + const int array_count = _sg_def(u_desc->array_count, 1); + uniform_offset += _sg_uniform_size(u_desc->type, array_count); + num_uniforms++; + } + else { + uniforms_continuous = false; + } + } + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + SOKOL_VALIDATE(uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); + #endif + } + else { + uniform_blocks_continuous = false; + } + } + bool images_continuous = true; + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->type != _SG_IMAGETYPE_DEFAULT) { + SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); + #if defined(SOKOL_GLES2) + SOKOL_VALIDATE(img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); + #endif + } + else { + images_continuous = false; + } + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + SOKOL_VALIDATE(shd && shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; + if (l_desc->stride == 0) { + continue; + } + SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + #if defined(SOKOL_GLES2) + /* on GLES2, vertex attribute names must be provided */ + SOKOL_VALIDATE((0 != a_desc->name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); + #elif defined(SOKOL_D3D11) + /* on D3D11, semantic names (and semantic indices) must be provided */ + SOKOL_VALIDATE((0 != a_desc->sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + bool atts_cont = true; + sg_pixel_format color_fmt = SG_PIXELFORMAT_NONE; + int width = -1, height = -1, sample_count = -1; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const sg_attachment_desc* att = &desc->color_attachments[att_index]; + if (att->image.id == SG_INVALID_ID) { + SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); + atts_cont = false; + continue; + } + SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + if (att_index == 0) { + color_fmt = img->pixel_format; + width = img->width >> att->mip_level; + height = img->height >> att->mip_level; + sample_count = img->sample_count; + } + else { + SOKOL_VALIDATE(img->pixel_format == color_fmt, _SG_VALIDATE_PASSDESC_COLOR_PIXELFORMATS); + SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + } + SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const sg_attachment_desc* att = &desc->depth_stencil_attachment; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_VALIDATE(img && img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->face < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->layer < img->depth, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->depth, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + SOKOL_VALIDATE(width == img->width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_attachment_t* att = &pass->color_atts[i]; + if (att->image) { + SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + } + if (pass->ds_att.image) { + const _sg_attachment_t* att = &pass->ds_att; + SOKOL_VALIDATE(att->image->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(att->image->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + /* the pipeline object must be alive and valid */ + SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); + /* the pipeline's shader must be alive and valid */ + SOKOL_ASSERT(pip->shader); + SOKOL_VALIDATE(pip->shader->slot.id == pip->shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); + SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); + /* check that pipeline attributes match current pass attributes */ + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); + if (pass) { + /* an offscreen pass */ + SOKOL_VALIDATE(pip->color_attachment_count == pass->num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->color_format == pass->color_atts[0].image->pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->sample_count == pass->color_atts[0].image->sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + if (pass->ds_att.image) { + SOKOL_VALIDATE(pip->depth_format == pass->ds_att.image->pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + else { + SOKOL_VALIDATE(pip->depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + } + else { + /* default pass */ + SOKOL_VALIDATE(pip->color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->color_format == SG_PIXELFORMAT_RGBA8, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->depth_format == SG_PIXELFORMAT_DEPTHSTENCIL, _SG_VALIDATE_APIP_DEPTH_FORMAT); + /* FIXME: hmm, we don't know if the default framebuffer is multisampled here */ + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + + /* a pipeline object must have been applied */ + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + SOKOL_ASSERT(pip->shader); + + /* has expected vertex buffers, and vertex buffers still exist */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->type, _SG_VALIDATE_ABND_VB_TYPE); + SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + } + } + else { + /* vertex buffer provided in a slot which has no vertex layout in pipeline */ + SOKOL_VALIDATE(!pip->vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + } + } + + /* index buffer expected or not, and index buffer still exists */ + if (pip->index_type == SG_INDEXTYPE_NONE) { + /* pipeline defines non-indexed rendering, but index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); + } + else { + /* pipeline defines indexed rendering, but no index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->type, _SG_VALIDATE_ABND_IB_TYPE); + SOKOL_VALIDATE(!buf->append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + } + } + + /* has expected vertex shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_VS]; + if (bindings->vs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + } + } + + /* has expected fragment shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->stage[SG_SHADERSTAGE_FS]; + if (bindings->fs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->type == stage->images[i].type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const void* data, int num_bytes) { + _SOKOL_UNUSED(data); + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(num_bytes); + return true; + #else + SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); + + /* check that there is a uniform block at 'stage' and 'ub_index' */ + const _sg_shader_stage_t* stage = &pip->shader->stage[stage_index]; + SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); + + /* check that the provided data size doesn't exceed the uniform block size */ + SOKOL_VALIDATE(num_bytes <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const void* data, int size) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(size); + return true; + #else + SOKOL_ASSERT(buf && data); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); + SOKOL_VALIDATE(buf->size >= size, _SG_VALIDATE_UPDATEBUF_SIZE); + SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); + SOKOL_VALIDATE(buf->append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const void* data, int size) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + _SOKOL_UNUSED(size); + return true; + #else + SOKOL_ASSERT(buf && data); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); + SOKOL_VALIDATE(buf->size >= (buf->append_pos+size), _SG_VALIDATE_APPENDBUF_SIZE); + SOKOL_VALIDATE(buf->update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_content* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; + #else + SOKOL_ASSERT(img && data); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(img->usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); + SOKOL_VALIDATE(img->upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED); + const int num_faces = (img->type == SG_IMAGETYPE_CUBE) ? 6 : 1; + const int num_mips = img->num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + SOKOL_VALIDATE(0 != data->subimage[face_index][mip_index].ptr, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA); + const int mip_width = _sg_max(img->width >> mip_index, 1); + const int mip_height = _sg_max(img->height >> mip_index, 1); + const int bytes_per_slice = _sg_surface_pitch(img->pixel_format, mip_width, mip_height); + const int expected_size = bytes_per_slice * img->depth; + SOKOL_VALIDATE(data->subimage[face_index][mip_index].size <= expected_size, _SG_VALIDATE_UPDIMG_SIZE); + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +/*== allocate/initialize resource private functions ==========================*/ +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { + sg_pass res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_buffer_desc(desc)) { + buf->slot.state = _sg_create_buffer(buf, desc); + } + else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_image_desc(desc)) { + img->slot.state = _sg_create_image(img, desc); + } + else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_shader_desc(desc)) { + shd->slot.state = _sg_create_shader(shd, desc); + } + else { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_VALID); + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pass_desc(desc)) { + /* lookup pass attachment image pointers */ + _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->color_attachments[i].image.id) { + att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[i] = 0; + } + } + const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; + if (desc->depth_stencil_attachment.image.id) { + att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[ds_att_index] = 0; + } + pass->slot.state = _sg_create_pass(pass, att_imgs, desc); + } + else { + pass->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); +} + +/*== PUBLIC API FUNCTIONS ====================================================*/ +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + memset(&_sg, 0, sizeof(_sg)); + _sg_setup_pools(&_sg.pools, desc); + _sg.frame_index = 1; + _sg_setup_backend(desc); + sg_setup_context(); + _sg.valid = true; +} + +SOKOL_API_IMPL void sg_shutdown(void) { + /* can only delete resources for the currently set context here, if multiple + contexts are used, the app code must take care of properly releasing them + (since only the app code can switch between 3D-API contexts) + */ + if (_sg.active_context.id != SG_INVALID_ID) { + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + if (ctx) { + _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); + _sg_destroy_context(ctx); + } + } + _sg_discard_backend(); + _sg_discard_pools(&_sg.pools); + _sg.valid = false; +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL bool sg_query_feature(sg_feature f) { + bool res = _sg_query_feature(f); + _SG_TRACE_ARGS(query_feature, f, res); + return res; +} + +SOKOL_API_IMPL sg_context sg_setup_context(void) { + sg_context res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); + _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); + ctx->slot.state = _sg_create_context(ctx); + SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); + _sg_activate_context(ctx); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + _sg.active_context = res; + return res; +} + +SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { + _sg_destroy_all_resources(&_sg.pools, ctx_id.id); + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + if (ctx) { + _sg_destroy_context(ctx); + _sg_reset_context(ctx); + _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); + } + _sg.active_context.id = SG_INVALID_ID; + _sg_activate_context(0); +} + +SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { + _sg.active_context = ctx_id; + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + /* NOTE: ctx can be 0 here if the context is no longer valid */ + _sg_activate_context(ctx); +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(trace_hooks); + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; + #else + static sg_trace_hooks old_hooks; + SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); + #endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { + sg_pass res = _sg_alloc_pass(); + _SG_TRACE_ARGS(alloc_pass, res); + return res; +} + +SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + _sg_init_buffer(buf_id, desc); + _SG_TRACE_ARGS(init_buffer, buf_id, desc); +} + +SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { + _sg_init_image(img_id, desc); + _SG_TRACE_ARGS(init_image, img_id, desc); +} + +SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + _sg_init_shader(shd_id, desc); + _SG_TRACE_ARGS(init_shader, shd_id, desc); +} + +SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + _sg_init_pipeline(pip_id, desc); + _SG_TRACE_ARGS(init_pipeline, pip_id, desc); +} + +SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + _sg_init_pass(pass_id, desc); + _SG_TRACE_ARGS(init_pass, pass_id, desc); +} + +/*-- set allocated resource to failed state ----------------------------------*/ +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + buf->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + img->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_image, img_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + shd->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_shader, shd_id); +} + +SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + pip->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + pass->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pass, pass_id); +} + +/*-- get resource state */ +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + _SG_TRACE_ARGS(query_buffer_state, buf_id, res); + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + _SG_TRACE_ARGS(query_image_state, img_id, res); + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + _SG_TRACE_ARGS(query_shader_state, shd_id, res); + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + _SG_TRACE_ARGS(query_pipeline_state, pip_id, res); + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; + _SG_TRACE_ARGS(query_pass_state, pass_id, res); + return res; +} + +/*-- allocate and initialize resource ----------------------------------------*/ +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(desc); + sg_buffer buf_id = _sg_alloc_buffer(); + if (buf_id.id != SG_INVALID_ID) { + _sg_init_buffer(buf_id, desc); + } + else { + SOKOL_LOG("buffer pool exhausted!"); + _SG_TRACE_NOARGS(err_buffer_pool_exhausted); + } + _SG_TRACE_ARGS(make_buffer, desc, buf_id); + return buf_id; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(desc); + sg_image img_id = _sg_alloc_image(); + if (img_id.id != SG_INVALID_ID) { + _sg_init_image(img_id, desc); + } + else { + SOKOL_LOG("image pool exhausted!"); + _SG_TRACE_NOARGS(err_image_pool_exhausted); + } + _SG_TRACE_ARGS(make_image, desc, img_id); + return img_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(desc); + sg_shader shd_id = _sg_alloc_shader(); + if (shd_id.id != SG_INVALID_ID) { + _sg_init_shader(shd_id, desc); + } + else { + SOKOL_LOG("shader pool exhausted!"); + _SG_TRACE_NOARGS(err_shader_pool_exhausted); + } + _SG_TRACE_ARGS(make_shader, desc, shd_id); + return shd_id; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(desc); + sg_pipeline pip_id = _sg_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sg_init_pipeline(pip_id, desc); + } + else { + SOKOL_LOG("pipeline pool exhausted!"); + _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); + } + _SG_TRACE_ARGS(make_pipeline, desc, pip_id); + return pip_id; +} + +SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { + SOKOL_ASSERT(desc); + sg_pass pass_id = _sg_alloc_pass(); + if (pass_id.id != SG_INVALID_ID) { + _sg_init_pass(pass_id, desc); + } + else { + SOKOL_LOG("pass pool exhausted!"); + _SG_TRACE_NOARGS(err_pass_pool_exhausted); + } + _SG_TRACE_ARGS(make_pass, desc, pass_id); + return pass_id; +} + +/*-- destroy resource --------------------------------------------------------*/ +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_buffer(buf); + _sg_reset_buffer(buf); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); + } + else { + SOKOL_LOG("sg_destroy_buffer: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + _SG_TRACE_ARGS(destroy_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_image(img); + _sg_reset_image(img); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); + } + else { + SOKOL_LOG("sg_destroy_image: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + _SG_TRACE_ARGS(destroy_image, img_id); +} + +SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_shader(shd); + _sg_reset_shader(shd); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); + } + else { + SOKOL_LOG("sg_destroy_shader: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + _SG_TRACE_ARGS(destroy_shader, shd_id); +} + +SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pipeline(pip); + _sg_reset_pipeline(pip); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); + } + else { + SOKOL_LOG("sg_destroy_pipeline: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + _SG_TRACE_ARGS(destroy_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if (pass->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pass(pass); + _sg_reset_pass(pass); + _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); + } + else { + SOKOL_LOG("sg_destroy_pass: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + _SG_TRACE_ARGS(destroy_pass, pass_id); +} + +SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.pass_valid = true; + _sg_begin_pass(0, &pa, width, height); + _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); +} + +SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + _sg.cur_pass = pass_id; + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass && _sg_validate_begin_pass(pass)) { + _sg.pass_valid = true; + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + const int w = pass->color_atts[0].image->width; + const int h = pass->color_atts[0].image->height; + _sg_begin_pass(pass, &pa, w, h); + _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); + } + else { + _sg.pass_valid = false; + _SG_TRACE_NOARGS(err_pass_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + _sg.bindings_valid = false; + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg.cur_pipeline = pip_id; + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip); + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->shader_id.id)); + _sg_apply_pipeline(pip); + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(bindings); + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg.bindings_valid = true; + + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip); + + _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; + int num_vbs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { + if (bindings->vertex_buffers[i].id) { + vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_ASSERT(vbs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); + _sg.next_draw_valid &= !vbs[i]->append_overflow; + } + else { + break; + } + } + + _sg_buffer_t* ib = 0; + if (bindings->index_buffer.id) { + ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_ASSERT(ib); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); + _sg.next_draw_valid &= !ib->append_overflow; + } + + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_vs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { + if (bindings->vs_images[i].id) { + vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_ASSERT(vs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); + } + else { + break; + } + } + + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_fs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { + if (bindings->fs_images[i].id) { + fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_ASSERT(fs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + } + else { + break; + } + } + if (_sg.next_draw_valid) { + const int* vb_offsets = bindings->vertex_buffer_offsets; + int ib_offset = bindings->index_buffer_offset; + _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _SG_TRACE_ARGS(apply_bindings, bindings); + } + else { + _SG_TRACE_NOARGS(err_draw_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) { + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(data && (num_bytes > 0)); + if (!_sg_validate_apply_uniforms(stage, ub_index, data, num_bytes)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + } + _sg_apply_uniforms(stage, ub_index, data, num_bytes); + _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data, num_bytes); +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + #if defined(SOKOL_DEBUG) + if (!_sg.bindings_valid) { + SOKOL_LOG("attempting to draw without resource bindings"); + } + #endif + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.bindings_valid) { + _SG_TRACE_NOARGS(err_bindings_invalid); + return; + } + _sg_draw(base_element, num_elements, num_instances); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_end_pass(); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.cur_pipeline.id = SG_INVALID_ID; + _sg.pass_valid = false; + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + _sg_commit(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const void* data, int num_bytes) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if ((num_bytes > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data, num_bytes)) { + SOKOL_ASSERT(num_bytes <= buf->size); + /* only one update allowed per buffer and frame */ + SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data, num_bytes); + buf->update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data, num_bytes); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const void* data, int num_bytes) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + int result; + if (buf) { + /* rewind append cursor in a new frame */ + if (buf->append_frame_index != _sg.frame_index) { + buf->append_pos = 0; + buf->append_overflow = false; + } + if ((buf->append_pos + num_bytes) > buf->size) { + buf->append_overflow = true; + } + const int start_pos = buf->append_pos; + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data, num_bytes)) { + if (!buf->append_overflow && (num_bytes > 0)) { + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->update_frame_index != _sg.frame_index); + _sg_append_buffer(buf, data, num_bytes, buf->append_frame_index != _sg.frame_index); + buf->append_pos += num_bytes; + buf->append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } + else { + /* FIXME: should we return -1 here? */ + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, num_bytes, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = buf ? buf->append_overflow : false; + _SG_TRACE_ARGS(query_buffer_overflow, buf_id, result); + return result; +} + +SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_content* data) { + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + _SG_TRACE_NOARGS(pop_debug_group); +} + +/*--- DEPRECATED ---*/ +#ifndef SOKOL_NO_DEPRECATED +SOKOL_API_IMPL void sg_apply_draw_state(const sg_draw_state* ds) { + SOKOL_ASSERT(ds); + SOKOL_ASSERT((ds->_start_canary==0) && (ds->_end_canary==0)); + sg_apply_pipeline(ds->pipeline); + sg_bindings bindings; + memset(&bindings, 0, sizeof(bindings)); + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + bindings.vertex_buffers[i] = ds->vertex_buffers[i]; + bindings.vertex_buffer_offsets[i] = ds->vertex_buffer_offsets[i]; + } + bindings.index_buffer = ds->index_buffer; + bindings.index_buffer_offset = ds->index_buffer_offset; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + bindings.vs_images[i] = ds->vs_images[i]; + bindings.fs_images[i] = ds->fs_images[i]; + } + sg_apply_bindings(&bindings); +} + +SOKOL_API_IMPL void sg_apply_uniform_block(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) { + sg_apply_uniforms(stage, ub_index, data, num_bytes); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* SOKOL_IMPL */ diff --git a/code/Modules/Gfx/private/win/winDisplayMgr.h b/code/Modules/Gfx/private/win/winDisplayMgr.h deleted file mode 100644 index d150ebef5..000000000 --- a/code/Modules/Gfx/private/win/winDisplayMgr.h +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::_priv::winDisplayMgr - @ingroup _priv - @brief common display manager functionality for Windows D3D11 renders - - NOTE: most of the window and messaging code is taken from GLFW3! -*/ -#include "Gfx/private/displayMgrBase.h" -#include "Gfx/private/win/win_decl.h" -#include "Gfx/private/win/winInputDefs.h" - -namespace Oryol { -namespace _priv { - -class winDisplayMgr : public displayMgrBase { -public: - /// constructor - winDisplayMgr(); - /// destructor - ~winDisplayMgr(); - - /// setup the display system, must happen before rendering - void SetupDisplay(const GfxSetup& gfxSetup, const gfxPointers& ptrs, const char* windowTitlePostfix); - /// discard the display, rendering cannot happen after - void DiscardDisplay(); - /// process window system events (call near start of frame) - void ProcessSystemEvents(); - /// check whether the window system requests to quit the application - bool QuitRequested() const; - - /// register the window class - void registerWindowClass(); - /// unregister the window class - void unregisterWindowClass(); - /// create the application window - void createWindow(const char* titlePostFix); - /// destroy the application window - void destroyWindow(); - /// initialize DPI-related stuff - void initDPI(bool highDpiRequested); - - /// compute actual window size from client rect size plus window chrome - void computeWindowSize(int clientWidth, int clientHeight, int& outWidth, int& outHeight); - /// setup the key translation table - void setupKeyTranslationTable(); - - /// set input mode (called from d3d11InputMgr) - void setInputMode(int mode, int value); - /// set cursor mode (called from setInputMode) - void setCursorMode(int newMode); - /// lowlevel set cursor mode - void winSetCursorMode(int mode); - /// lowlevel set cursor pos - void winSetCursorPos(double xpos, double ypos); - /// lowlevel get cursor pos - void winGetCursorPos(double* xpos, double* ypos); - /// lowlevel get window size - void winGetWindowSize(int* width, int* height); - /// update cursor clip rect - void updateClipCursor(); - - /// get modifier keys - int inputGetKeyMods(); - /// translate key code from WM wParam, lParam - int inputTranslateKey(WPARAM wParam, LPARAM lParam); - /// called from WinProc when key has been pressed - void inputKey(int keyCode, int scanCode, int action, int mods); - /// called from WinProc on WM_CHAR - void inputChar(unsigned int codePoint, int mods, int plain); - // called from WinProc on WM mouse button messages - void inputMouseClick(int button, int action, int mods); - /// called from WinProc window focus messages - void inputWindowFocus(bool focused); - /// called on mouse moved - void inputCursorMotion(double x, double y); - /// cursor enters/leaves windows, called from winproc - void inputCursorEnter(bool entered); - /// called from winproc on mouse wheel - void inputScroll(double x, double y); - /// called on WM_SIZE - void inputFramebufferSize(int width, int height); - /// called on WM_SIZE - void inputWindowSize(int width, int height); - /// called on WM_MOVE - void inputWindowPos(int xpos, int ypos); - /// window has been iconified/restored - void inputWindowIconify(bool iconified); - - /// check if window size was changed, and track those changes - void checkWindowResize(); - /// called from checkWindowResize when window did actually resize - virtual void onWindowDidResize(); - - static winDisplayMgr* self; - - bool quitRequested; - HWND hwnd; - DWORD dwStyle; - DWORD dwExStyle; - bool inCreateWindow; - bool dpiAware; - int windowScale; - int contentScale; - - int cursorMode; - double cursorPosX; - double cursorPosY; - double windowCursorPosX; - double windowCursorPosY; - int win32CursorPosX; - int win32CursorPosY; - bool cursorTracked; - bool iconified; - char mouseButtons[ORYOL_WIN_MOUSE_BUTTON_LAST + 1]; - char keys[ORYOL_WIN_KEY_LAST + 1]; - short int publicKeys[512]; // key-code translation table - - // callback signatures (see glfw3.h) - typedef void(*windowposfun)(int, int); - typedef void(*windowsizefun)(int, int); - typedef void(*windowclosefun)(); - typedef void(*windowrefreshfun)(); - typedef void(*windowfocusfun)(int); - typedef void(*windowiconifyfun)(int); - typedef void(*framebuffersizefun)(int, int); - typedef void(*mousebuttonfun)(int, int, int); - typedef void(*cursorposfun)(double, double); - typedef void(*cursorenterfun)(int); - typedef void(*scrollfun)(double, double); - typedef void(*keyfun)(int, int, int, int); - typedef void(*charfun)(unsigned int); - typedef void(*charmodsfun)(unsigned int, int); - - // callback pointers, these are usually populated by the Oryol Input module - struct callbackTable { - windowposfun pos; - windowsizefun size; - windowclosefun close; - windowrefreshfun refresh; - windowfocusfun focus; - windowiconifyfun iconify; - framebuffersizefun fbsize; - mousebuttonfun mouseButton; - cursorposfun cursorPos; - cursorenterfun cursorEnter; - scrollfun scroll; - keyfun key; - charfun character; - charmodsfun charmods; - } callbacks; -}; - -} // namespace _priv -} // namespace Oryol diff --git a/code/Modules/Gfx/private/win/win_decl.h b/code/Modules/Gfx/private/win/win_decl.h deleted file mode 100644 index 799169b97..000000000 --- a/code/Modules/Gfx/private/win/win_decl.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @file win_decl.h - @brief Windows forward declarations -*/ -#if defined(_WIN64) -typedef __int64 INT_PTR, *PINT_PTR; -typedef unsigned __int64 UINT_PTR, *PUINT_PTR; -typedef __int64 LONG_PTR, *PLONG_PTR; -typedef unsigned __int64 ULONG_PTR, *PULONG_PTR; -#define __int3264 __int64 -#else -typedef int INT_PTR, *PINT_PTR; -typedef unsigned int UINT_PTR, *PUINT_PTR; -typedef long LONG_PTR, *PLONG_PTR; -typedef unsigned long ULONG_PTR, *PULONG_PTR; -#define __int3264 __int32 -#endif - -typedef void* HANDLE; -typedef unsigned long DWORD; -struct HWND__; -typedef HWND__* HWND; -typedef UINT_PTR WPARAM; -typedef LONG_PTR LPARAM; -typedef LONG_PTR LRESULT; -typedef ULONG_PTR SIZE_T, *PSIZE_T; \ No newline at end of file diff --git a/code/Modules/HttpFS/CMakeLists.txt b/code/Modules/HttpFS/CMakeLists.txt index 694d60152..99b13195c 100644 --- a/code/Modules/HttpFS/CMakeLists.txt +++ b/code/Modules/HttpFS/CMakeLists.txt @@ -37,10 +37,10 @@ fips_begin_module(HttpFS) endif() fips_end_module() -fips_begin_unittest(HTTP) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files(HTTPFileSystemTest.cc) - fips_deps(IO HttpFS Core) - fips_frameworks_osx(Foundation) -fips_end_unittest() +#fips_begin_unittest(HTTP) +# fips_vs_warning_level(3) +# fips_dir(UnitTests) +# fips_files(HTTPFileSystemTest.cc) +# fips_deps(IO HttpFS Core) +# fips_frameworks_osx(Foundation) +#fips_end_unittest() diff --git a/code/Modules/HttpFS/UnitTests/HTTPFileSystemTest.cc b/code/Modules/HttpFS/UnitTests/HTTPFileSystemTest.cc index 5b1f0d41e..eb532924b 100644 --- a/code/Modules/HttpFS/UnitTests/HTTPFileSystemTest.cc +++ b/code/Modules/HttpFS/UnitTests/HTTPFileSystemTest.cc @@ -16,9 +16,7 @@ TEST(HTTPFileSystemTest) { Core::Setup(); // setup an IO facade, and associate http: with the HTTPFileSystem - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - IO::Setup(ioSetup); + IO::Setup(IOSetup.FileSystem("http", HTTPFileSystem::Create())); // asynchronously load the index.html file Ptr req = IO::LoadFile("http://www.flohofwoe.net/index.html"); diff --git a/code/Modules/HttpFS/private/curl/curlURLLoader.cc b/code/Modules/HttpFS/private/curl/curlURLLoader.cc index 768b9db65..62b582e95 100644 --- a/code/Modules/HttpFS/private/curl/curlURLLoader.cc +++ b/code/Modules/HttpFS/private/curl/curlURLLoader.cc @@ -4,7 +4,7 @@ #include "Pre.h" #include "curlURLLoader.h" #include "Core/String/StringConverter.h" -#include "Core/Containers/Buffer.h" +#include "Core/Containers/MemoryBuffer.h" #include "curl/curl.h" #include @@ -89,7 +89,7 @@ curlURLLoader::curlWriteDataCallback(char* ptr, size_t size, size_t nmemb, void* // userData is expected to point to a Buffer object int bytesToWrite = (int) (size * nmemb); if (bytesToWrite > 0) { - Buffer* buf = (Buffer*) userData; + MemoryBuffer* buf = (MemoryBuffer*) userData; buf->Add((const uint8_t*)ptr, bytesToWrite); return bytesToWrite; } diff --git a/code/Modules/IO/CMakeLists.txt b/code/Modules/IO/CMakeLists.txt index 8e5d78ed8..7d6923fb0 100644 --- a/code/Modules/IO/CMakeLists.txt +++ b/code/Modules/IO/CMakeLists.txt @@ -21,16 +21,16 @@ fips_begin_module(IO) fips_deps(Core) fips_end_module() -fips_begin_unittest(IO) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - IOFacadeTest.cc - IOStatusTest.cc - URLBuilderTest.cc - URLTest.cc - assignRegistryTest.cc - schemeRegistryTest.cc - ) - fips_deps(IO Core) -fips_end_unittest() +#fips_begin_unittest(IO) +# fips_vs_warning_level(3) +# fips_dir(UnitTests) +# fips_files( +# IOFacadeTest.cc +# IOStatusTest.cc +# URLBuilderTest.cc +# URLTest.cc +# assignRegistryTest.cc +# schemeRegistryTest.cc +# ) +# fips_deps(IO Core) +#fips_end_unittest() diff --git a/code/Modules/IO/IO.cc b/code/Modules/IO/IO.cc index ed5db00f3..da3ad9b95 100644 --- a/code/Modules/IO/IO.cc +++ b/code/Modules/IO/IO.cc @@ -29,7 +29,7 @@ namespace { //------------------------------------------------------------------------------ void -IO::Setup(const IOSetup& setup) { +IO::Setup(const IODesc& desc) { o_assert(!IsValid()); state = Memory::New<_state>(); @@ -39,12 +39,12 @@ IO::Setup(const IOSetup& setup) { state->router.setup(ptrs); // setup initial assigns - for (const auto& assign : setup.Assigns) { + for (const auto& assign : desc.Assigns) { SetAssign(assign.Key(), assign.Value()); } // setup initial filesystems - for (const auto& fs : setup.FileSystems) { + for (const auto& fs : desc.FileSystems) { RegisterFileSystem(fs.Key(), fs.Value()); } @@ -171,7 +171,7 @@ IO::LoadFile(const URL& url) { //------------------------------------------------------------------------------ Ptr -IO::WriteFile(const URL& url, const Buffer& data) { +IO::WriteFile(const URL& url, const MemoryBuffer& data) { o_assert_dbg(IsValid()); Ptr ioReq = IOWrite::Create(); ioReq->Url = url; diff --git a/code/Modules/IO/IO.h b/code/Modules/IO/IO.h index c7efa9e67..93c3754a8 100644 --- a/code/Modules/IO/IO.h +++ b/code/Modules/IO/IO.h @@ -18,7 +18,7 @@ namespace Oryol { class IO { public: /// setup the IO module - static void Setup(const IOSetup& setup); + static void Setup(const IODesc& desc); /// discard the IO module static void Discard(); /// check if IO module is valid @@ -59,7 +59,7 @@ class IO { /// low-level: start async loading of file from URL, return message for polling result static Ptr LoadFile(const URL& url); /// low-level: start async writing of file via URL, return message for polling result - static Ptr WriteFile(const URL& url, const Buffer& data); + static Ptr WriteFile(const URL& url, const MemoryBuffer& data); /// low-level: push a generic asynchronous IO request static void Put(const Ptr& ioReq); diff --git a/code/Modules/IO/IOTypes.cc b/code/Modules/IO/IOTypes.cc index f74d6c749..1ee9b659e 100644 --- a/code/Modules/IO/IOTypes.cc +++ b/code/Modules/IO/IOTypes.cc @@ -4,11 +4,24 @@ #include "Pre.h" #include "IOTypes.h" #include "IO/IO.h" +#include "IO/FileSystemBase.h" namespace Oryol { #define _TOSTRING(c) case c: return #c +//------------------------------------------------------------------------------ +IODesc& IODesc::AddAssign(const String& name, const String& path) { + this->Assigns.Add(name, path); + return *this; +} + +//------------------------------------------------------------------------------ +IODesc& IODesc::AddFileSystem(const StringAtom& scheme, std::function()> fsCreatorFunc) { + this->FileSystems.Add(scheme, fsCreatorFunc); + return *this; +} + //------------------------------------------------------------------------------ const char* IOStatus::ToString(Code c) { diff --git a/code/Modules/IO/IOTypes.h b/code/Modules/IO/IOTypes.h index 60657983a..24bacf8d6 100644 --- a/code/Modules/IO/IOTypes.h +++ b/code/Modules/IO/IOTypes.h @@ -17,12 +17,15 @@ class FileSystemBase; @ingroup IO @brief configure the IO system */ -class IOSetup { +class IODesc { public: - /// initial assigns + // initial assigns */ Map Assigns; - /// initial file systems + // initial filesystems */ Map()>> FileSystems; + + IODesc& AddAssign(const String& name, const String& path); + IODesc& AddFileSystem(const StringAtom& scheme, std::function()> fsCreatorFunc); }; //------------------------------------------------------------------------------ diff --git a/code/Modules/IO/UnitTests/IOFacadeTest.cc b/code/Modules/IO/UnitTests/IOFacadeTest.cc index ec2fe551c..09dcf8957 100644 --- a/code/Modules/IO/UnitTests/IOFacadeTest.cc +++ b/code/Modules/IO/UnitTests/IOFacadeTest.cc @@ -36,7 +36,7 @@ class TestFileSystem : public FileSystemBase { #if !ORYOL_EMSCRIPTEN && !ORYOL_UNITTESTS_HEADLESS TEST(IOFacadeTest) { Core::Setup(); - IO::Setup(IOSetup()); + IO::Setup(); // register our test file-system as URI scheme "test" IO::RegisterFileSystem("test", TestFileSystem::Creator()); diff --git a/code/Modules/IO/private/ioRequests.h b/code/Modules/IO/private/ioRequests.h index 85b367f01..dc75a224a 100644 --- a/code/Modules/IO/private/ioRequests.h +++ b/code/Modules/IO/private/ioRequests.h @@ -10,7 +10,7 @@ */ #include "Core/Config.h" #include "Core/RefCounted.h" -#include "Core/Containers/Buffer.h" +#include "Core/Containers/MemoryBuffer.h" #include "IO/IOTypes.h" namespace Oryol { @@ -39,7 +39,7 @@ class IORequest : public _priv::ioMsg { URL Url; int StartOffset = 0; int EndOffset = EndOfFile; - Buffer Data; + MemoryBuffer Data; IOStatus::Code Status = IOStatus::InvalidIOStatus; String ErrorDesc; }; diff --git a/code/Modules/IO/private/loadQueue.h b/code/Modules/IO/private/loadQueue.h index b867cd0f7..f155aa281 100644 --- a/code/Modules/IO/private/loadQueue.h +++ b/code/Modules/IO/private/loadQueue.h @@ -10,7 +10,7 @@ #include "Core/Types.h" #include "Core/String/StringAtom.h" #include "Core/Containers/Array.h" -#include "Core/Containers/Buffer.h" +#include "Core/Containers/MemoryBuffer.h" #include "IO/IOTypes.h" #include "IO/private/ioRequests.h" #include @@ -21,7 +21,7 @@ class loadQueue { public: /// loading result (iff successful) struct result { - result(const URL& url, Buffer&& data) : Url(url), Data(std::move(data)) { }; + result(const URL& url, MemoryBuffer&& data) : Url(url), Data(std::move(data)) { }; result(result&& rhs) { this->Url = std::move(rhs.Url); this->Data = std::move(rhs.Data); @@ -31,7 +31,7 @@ class loadQueue { this->Data = std::move(rhs.Data); }; URL Url; - Buffer Data; + MemoryBuffer Data; }; /// callback function signature for success diff --git a/code/Modules/Input/Input.cc b/code/Modules/Input/Input.cc index 13421ddb2..b4e33cf0f 100644 --- a/code/Modules/Input/Input.cc +++ b/code/Modules/Input/Input.cc @@ -17,10 +17,10 @@ namespace { //------------------------------------------------------------------------------ void -Input::Setup(const InputSetup& setup) { +Input::Setup(const InputDesc& desc) { o_assert_dbg(!IsValid()); state = Memory::New<_state>(); - state->inputManager.setup(setup); + state->inputManager.setup(desc); } //------------------------------------------------------------------------------ diff --git a/code/Modules/Input/Input.h b/code/Modules/Input/Input.h index fa432af4f..e38f01f37 100644 --- a/code/Modules/Input/Input.h +++ b/code/Modules/Input/Input.h @@ -19,7 +19,7 @@ namespace Oryol { class Input { public: /// setup the Input module - static void Setup(const InputSetup& inputSetup = InputSetup()); + static void Setup(const InputDesc& desc = InputDesc()); /// discard the Input module static void Discard(); /// check if Input module is valid diff --git a/code/Modules/Input/InputTypes.h b/code/Modules/Input/InputTypes.h index 40d560814..2db0e5d66 100644 --- a/code/Modules/Input/InputTypes.h +++ b/code/Modules/Input/InputTypes.h @@ -113,26 +113,46 @@ class GamepadMapping { //------------------------------------------------------------------------------ /** - @class Oryol::InputSetup + @class Oryol::InputDesc @ingroup Input @brief configure the Input module */ -class InputSetup { +class InputDesc { public: - /// initial set of gamepad mappings, use id="__default" to override default mapping - Map GamepadMappings; + /// initial set of gamepad mappings, use name="__default" to override default mapping + InputDesc& GamepadMapping(const StringAtom& name, const class GamepadMapping& mapping) { + gamepadMappings.Add(name, mapping); + return *this; + } + const Map& GamepadMappings() const { + return gamepadMappings; + } /// enable/disable single-tap touch gesture - bool TapEnabled = true; + InputDesc& TapEnabled(bool b) { tapEnabled = b; return *this; } + bool TapEnabled() const { return tapEnabled; } /// enable/disable double-tap touch gesture - bool DoubleTapEnabled = true; + InputDesc& DoubleTapEnabled(bool b) { doubleTapEnabled = b; return *this; } + bool DoubleTapEnabled() const { return doubleTapEnabled; } /// enable/disable pinch touch gesture - bool PinchEnabled = true; + InputDesc& PinchEnabled(bool b) { pinchEnabled = b; return *this; } + bool PinchEnabled() const { return pinchEnabled; } /// enable/disable pan touch gesture - bool PanEnabled = true; - /// accelerometer enabled - bool AccelerometerEnabled = true; - /// gyrometer enabled - bool GyrometerEnabled = true; + InputDesc& PanEnabled(bool b) { panEnabled = b; return *this; } + bool PanEnabled() const { return panEnabled; } + /// enable/disable accelerometer readings + InputDesc& AccelerometerEnabled(bool b) { accelerometerEnabled = b; return *this; } + bool AccelerometerEnabled() const { return accelerometerEnabled; } + /// enable/disable gyrometer readings + InputDesc& GyrometerEnabled(bool b) { gyrometerEnabled = b; return *this; } + bool GyrometerEnabled() const { return gyrometerEnabled; } + + Map gamepadMappings; + bool tapEnabled = true; + bool doubleTapEnabled = true; + bool pinchEnabled = true; + bool panEnabled = true; + bool accelerometerEnabled = true; + bool gyrometerEnabled = true; }; //------------------------------------------------------------------------------ diff --git a/code/Modules/Input/private/android/androidInputMgr.cc b/code/Modules/Input/private/android/androidInputMgr.cc index 59d386876..d0ba9c312 100644 --- a/code/Modules/Input/private/android/androidInputMgr.cc +++ b/code/Modules/Input/private/android/androidInputMgr.cc @@ -34,16 +34,16 @@ androidInputMgr::~androidInputMgr() { //------------------------------------------------------------------------------ void -androidInputMgr::setup(const InputSetup& setup) { +androidInputMgr::setup(const InputDesc& desc) { Log::Info("androidInputMgr::setup called!\n"); if (!Gfx::IsValid()) { o_error("androidInputMgr: Gfx::Setup() must be called before Input::Setup!\n"); return; } - this->highDPI = Gfx::GfxSetup().HighDPI; + this->highDPI = Gfx::Desc().HighDPI(); - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); this->touchpad.attached = true; this->sensors.attached = true; OryolAndroidAppState->onInputEvent = androidInputMgr::onInputEvent; @@ -128,7 +128,7 @@ androidInputMgr::onSensorEvent(const ASensorEvent* event) { switch (event->type) { case ASENSOR_TYPE_ACCELEROMETER: - if (self->inputSetup.AccelerometerEnabled) { + if (self->inputDesc.accelerometerEnabled) { // NOTE: x and y are swapped because the default orientation is landscape self->sensors.acceleration.x = event->acceleration.y; self->sensors.acceleration.y = -event->acceleration.x; @@ -141,7 +141,7 @@ androidInputMgr::onSensorEvent(const ASensorEvent* event) { /* case ASENSOR_TYPE_GAME_ROTATION_VECTOR: // FIXME FIXME FIXME: this doesn't seem to work! - if (self->inputSetup.GyrometerEnabled) { + if (self->inputDesc.gyrometerEnabled) { glm::vec3 axis(event->vector.x, event->vector.y, event->vector.z); float mag = glm::length(axis); glm::quat q(glm::cos(mag), event->vector.x, event->vector.y, event->vector.z); diff --git a/code/Modules/Input/private/android/androidInputMgr.h b/code/Modules/Input/private/android/androidInputMgr.h index f02916ce7..51e801f04 100644 --- a/code/Modules/Input/private/android/androidInputMgr.h +++ b/code/Modules/Input/private/android/androidInputMgr.h @@ -22,7 +22,7 @@ class androidInputMgr : public inputMgrBase { ~androidInputMgr(); /// setup the input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the input manager void discard(); diff --git a/code/Modules/Input/private/emsc/emscInputMgr.cc b/code/Modules/Input/private/emsc/emscInputMgr.cc index d51452afc..48ff77fc1 100644 --- a/code/Modules/Input/private/emsc/emscInputMgr.cc +++ b/code/Modules/Input/private/emsc/emscInputMgr.cc @@ -27,9 +27,9 @@ emscInputMgr::~emscInputMgr() { //------------------------------------------------------------------------------ void -emscInputMgr::setup(const InputSetup& setup) { +emscInputMgr::setup(const InputDesc& desc) { this->setupGamepadMappings(); // needs to happen before calling base class - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); this->setupKeyTable(); this->keyboard.attached = true; this->mouse.attached = true; @@ -64,10 +64,10 @@ emscInputMgr::setupCallbacks() { emscripten_set_touchend_callback("#canvas", this, true, emscTouch); emscripten_set_touchmove_callback("#canvas", this, true, emscTouch); emscripten_set_touchcancel_callback("#canvas", this, true, emscTouch); - if (this->inputSetup.AccelerometerEnabled) { + if (this->inputDesc.accelerometerEnabled) { emscripten_set_devicemotion_callback(this, true, emscDeviceMotion); } - if (this->inputSetup.GyrometerEnabled) { + if (this->inputDesc.gyrometerEnabled) { emscripten_set_deviceorientation_callback(this, true, emscDeviceOrientation); } } diff --git a/code/Modules/Input/private/emsc/emscInputMgr.h b/code/Modules/Input/private/emsc/emscInputMgr.h index 4fd6fc7ef..534e8e074 100644 --- a/code/Modules/Input/private/emsc/emscInputMgr.h +++ b/code/Modules/Input/private/emsc/emscInputMgr.h @@ -20,7 +20,7 @@ class emscInputMgr : public inputMgrBase { ~emscInputMgr(); /// setup the input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the input manager void discard(); diff --git a/code/Modules/Input/private/glfw/glfwInputMgr.cc b/code/Modules/Input/private/glfw/glfwInputMgr.cc index 89305d610..d4f976d38 100644 --- a/code/Modules/Input/private/glfw/glfwInputMgr.cc +++ b/code/Modules/Input/private/glfw/glfwInputMgr.cc @@ -3,7 +3,7 @@ //------------------------------------------------------------------------------ #include "Pre.h" #include "glfwInputMgr.h" -#include "Gfx/private/glfw/glfwDisplayMgr.h" +#include "Gfx/private/gl/glfwDisplayMgr.h" #include "Core/Core.h" #include "Core/RunLoop.h" #include "GLFW/glfw3.h" @@ -31,10 +31,10 @@ glfwInputMgr::~glfwInputMgr() { //------------------------------------------------------------------------------ void -glfwInputMgr::setup(const InputSetup& setup) { +glfwInputMgr::setup(const InputDesc& desc) { this->setupGamepadMappings(); // must be called before parent class! - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); this->keyboard.attached = true; this->mouse.attached = true; diff --git a/code/Modules/Input/private/glfw/glfwInputMgr.h b/code/Modules/Input/private/glfw/glfwInputMgr.h index fd25dab79..0aa3c4e0b 100644 --- a/code/Modules/Input/private/glfw/glfwInputMgr.h +++ b/code/Modules/Input/private/glfw/glfwInputMgr.h @@ -21,7 +21,7 @@ class glfwInputMgr : public inputMgrBase { ~glfwInputMgr(); /// setup the GLFW input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the GLFW input manager void discard(); diff --git a/code/Modules/Input/private/inputMgrBase.cc b/code/Modules/Input/private/inputMgrBase.cc index 1d617e659..6d982be14 100644 --- a/code/Modules/Input/private/inputMgrBase.cc +++ b/code/Modules/Input/private/inputMgrBase.cc @@ -21,13 +21,13 @@ inputMgrBase::~inputMgrBase() { //------------------------------------------------------------------------------ void -inputMgrBase::setup(const InputSetup& setup) { +inputMgrBase::setup(const InputDesc& desc) { o_assert_dbg(!this->isValid()); this->singleTapDetector.numRequiredTaps = 1; - this->doubleTapDetector.numRequiredTaps = 2; + this->doubleTapDetector.numRequiredTaps = 2; this->valid = true; - this->inputSetup = setup; - for (const auto& item : setup.GamepadMappings) { + this->inputDesc = desc; + for (const auto& item : desc.gamepadMappings) { this->addGamepadMapping(item.Key(), item.Value()); } } @@ -68,13 +68,13 @@ inputMgrBase::reset() { void inputMgrBase::onTouchEvent(const touchEvent& event) { o_assert_dbg(event.numTouches > 0); - if (this->touchpad.attached) { - // track raw touch events + if (this->touchpad.attached) { + // track raw touch events if (event.numTouches == 1) { - this->touchpad.onTouchEvent(event.type, event.points[0].pos); - } - // panning detection - if (this->inputSetup.PanEnabled) { + this->touchpad.onTouchEvent(event.type, event.points[0].pos); + } + // panning detection + if (this->inputDesc.panEnabled) { switch (this->panDetector.detect(event)) { case gestureState::start: this->touchpad.onPanningStarted(this->panDetector.position, this->panDetector.startPosition); @@ -91,21 +91,21 @@ inputMgrBase::onTouchEvent(const touchEvent& event) { } break; } - } + } // tapping detection - if (this->inputSetup.TapEnabled) { + if (this->inputDesc.tapEnabled) { if (gestureState::action == this->singleTapDetector.detect(event)) { this->touchpad.onTapped(this->singleTapDetector.position); } - } + } // double tap detection - if (this->inputSetup.DoubleTapEnabled) { + if (this->inputDesc.doubleTapEnabled) { if (gestureState::action == this->doubleTapDetector.detect(event)) { this->touchpad.onDoubleTapped(this->doubleTapDetector.position); } - } + } // pinc detection - if (this->inputSetup.PinchEnabled) { + if (this->inputDesc.pinchEnabled) { switch (this->pinchDetector.detect(event)) { case gestureState::start: this->touchpad.onPinchingStarted( diff --git a/code/Modules/Input/private/inputMgrBase.h b/code/Modules/Input/private/inputMgrBase.h index f2d211243..b8b6f30ab 100644 --- a/code/Modules/Input/private/inputMgrBase.h +++ b/code/Modules/Input/private/inputMgrBase.h @@ -18,7 +18,7 @@ class inputMgrBase { inputMgrBase(); ~inputMgrBase(); - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); void discard(); bool isValid() const; void reset(); @@ -34,7 +34,7 @@ class inputMgrBase { bool valid = false; - InputSetup inputSetup; + InputDesc inputDesc; inputDispatcher dispatcher; tapDetector singleTapDetector; tapDetector doubleTapDetector; diff --git a/code/Modules/Input/private/ios/iosInputMgr.h b/code/Modules/Input/private/ios/iosInputMgr.h index 065f4c57f..97ba8af5c 100644 --- a/code/Modules/Input/private/ios/iosInputMgr.h +++ b/code/Modules/Input/private/ios/iosInputMgr.h @@ -19,7 +19,7 @@ class iosInputMgr : public inputMgrBase { ~iosInputMgr(); /// setup the object - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the object void discard(); diff --git a/code/Modules/Input/private/ios/iosInputMgr.mm b/code/Modules/Input/private/ios/iosInputMgr.mm index 21e48d9b0..0473530eb 100644 --- a/code/Modules/Input/private/ios/iosInputMgr.mm +++ b/code/Modules/Input/private/ios/iosInputMgr.mm @@ -84,9 +84,9 @@ - (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { //------------------------------------------------------------------------------ void -iosInputMgr::setup(const InputSetup& setup) { +iosInputMgr::setup(const InputDesc& desc) { - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); if (!Gfx::IsValid()) { o_error("iosInputMgr: Gfx::Setup() must be called before Input::Setup()!\n"); @@ -98,7 +98,7 @@ - (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { this->inputDelegate = [[iosInputDelegate alloc] init]; // create CoreMotionManager to sample device motion data - if (setup.AccelerometerEnabled || setup.GyrometerEnabled) { + if (desc.accelerometerEnabled || desc.gyrometerEnabled) { this->motionManager = [[CMMotionManager alloc] init]; if ([this->motionManager isDeviceMotionAvailable]) { [this->motionManager startDeviceMotionUpdates]; @@ -165,14 +165,14 @@ - (void) touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { CMAcceleration cmUserAccel = motionData.userAcceleration; // acceleration - if (this->inputSetup.AccelerometerEnabled) { + if (this->inputDesc.accelerometerEnabled) { static const float earthGravity = 9.80665f; glm::vec3 accel(cmGravity.x + cmUserAccel.x, cmGravity.y + cmUserAccel.y, cmGravity.z + cmUserAccel.z); this->sensors.acceleration = accel * earthGravity; } // attitude - if (this->inputSetup.GyrometerEnabled) { + if (this->inputDesc.gyrometerEnabled) { this->sensors.yawPitchRoll.x = motionData.attitude.yaw; this->sensors.yawPitchRoll.y = motionData.attitude.pitch; this->sensors.yawPitchRoll.z = motionData.attitude.roll; diff --git a/code/Modules/Input/private/osx/osxInputMgr.cc b/code/Modules/Input/private/osx/osxInputMgr.cc index a0a248e4c..9ee376677 100644 --- a/code/Modules/Input/private/osx/osxInputMgr.cc +++ b/code/Modules/Input/private/osx/osxInputMgr.cc @@ -30,9 +30,9 @@ osxInputMgr::~osxInputMgr() { //------------------------------------------------------------------------------ void -osxInputMgr::setup(const InputSetup& setup) { +osxInputMgr::setup(const InputDesc& desc) { - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); this->keyboard.attached = true; this->mouse.attached = true; diff --git a/code/Modules/Input/private/osx/osxInputMgr.h b/code/Modules/Input/private/osx/osxInputMgr.h index ba025c76c..812331a5e 100644 --- a/code/Modules/Input/private/osx/osxInputMgr.h +++ b/code/Modules/Input/private/osx/osxInputMgr.h @@ -18,7 +18,7 @@ class osxInputMgr : public inputMgrBase { ~osxInputMgr(); /// setup the input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the input manager void discard(); diff --git a/code/Modules/Input/private/raspi/raspiInputMgr.cc b/code/Modules/Input/private/raspi/raspiInputMgr.cc index e89f2687d..3c336b49a 100644 --- a/code/Modules/Input/private/raspi/raspiInputMgr.cc +++ b/code/Modules/Input/private/raspi/raspiInputMgr.cc @@ -37,8 +37,8 @@ raspiInputMgr::~raspiInputMgr() { //------------------------------------------------------------------------------ void -raspiInputMgr::setup(const InputSetup& setup) { - inputMgrBase::setup(setup); +raspiInputMgr::setup(const InputDesc& desc) { + inputMgrBase::setup(desc); if (this->openDevices()) { if (-1 != this->kbdFd) { this->keyboard.attached = true; diff --git a/code/Modules/Input/private/raspi/raspiInputMgr.h b/code/Modules/Input/private/raspi/raspiInputMgr.h index 4bc0db961..19231b2dd 100644 --- a/code/Modules/Input/private/raspi/raspiInputMgr.h +++ b/code/Modules/Input/private/raspi/raspiInputMgr.h @@ -19,7 +19,7 @@ class raspiInputMgr : public inputMgrBase { ~raspiInputMgr(); /// setup the input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the input manager void discard(); diff --git a/code/Modules/Input/private/win/winInputMgr.cc b/code/Modules/Input/private/win/winInputMgr.cc index 8fc776375..fbce5b4be 100644 --- a/code/Modules/Input/private/win/winInputMgr.cc +++ b/code/Modules/Input/private/win/winInputMgr.cc @@ -5,7 +5,7 @@ #include "winInputMgr.h" #include "Core/Core.h" #include "Core/RunLoop.h" -#include "Gfx/private/win/winDisplayMgr.h" +#include "Gfx/private/d3d11/winDisplayMgr.h" namespace Oryol { namespace _priv { @@ -29,9 +29,9 @@ winInputMgr::~winInputMgr() { //------------------------------------------------------------------------------ void -winInputMgr::setup(const InputSetup& setup) { +winInputMgr::setup(const InputDesc& desc) { - inputMgrBase::setup(setup); + inputMgrBase::setup(desc); this->keyboard.attached = true; this->mouse.attached = true; diff --git a/code/Modules/Input/private/win/winInputMgr.h b/code/Modules/Input/private/win/winInputMgr.h index 854243b16..5a9457df2 100644 --- a/code/Modules/Input/private/win/winInputMgr.h +++ b/code/Modules/Input/private/win/winInputMgr.h @@ -22,7 +22,7 @@ class winInputMgr : public inputMgrBase { ~winInputMgr(); /// setup the window input manager - void setup(const InputSetup& setup); + void setup(const InputDesc& desc); /// discard the windows input manager void discard(); diff --git a/code/Modules/LocalFS/CMakeLists.txt b/code/Modules/LocalFS/CMakeLists.txt index b7beb475f..cdd15d710 100644 --- a/code/Modules/LocalFS/CMakeLists.txt +++ b/code/Modules/LocalFS/CMakeLists.txt @@ -26,12 +26,12 @@ fips_begin_module(LocalFS) fips_deps(IO Core) fips_end_module() -fips_begin_unittest(LocalFS) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - LocalFileSystemTest.cc - FSWrapperTest.cc - ) - fips_deps(LocalFS) -fips_end_unittest() +#fips_begin_unittest(LocalFS) +# fips_vs_warning_level(3) +# fips_dir(UnitTests) +# fips_files( +# LocalFileSystemTest.cc +# FSWrapperTest.cc +# ) +# fips_deps(LocalFS) +#fips_end_unittest() diff --git a/code/Modules/LocalFS/UnitTests/LocalFileSystemTest.cc b/code/Modules/LocalFS/UnitTests/LocalFileSystemTest.cc index a7c7ede6c..6a2bf6c2c 100644 --- a/code/Modules/LocalFS/UnitTests/LocalFileSystemTest.cc +++ b/code/Modules/LocalFS/UnitTests/LocalFileSystemTest.cc @@ -24,11 +24,10 @@ wait(const Ptr& msg) { TEST(LocalFileSystemTest) { Core::Setup(); - IOSetup ioSetup; - ioSetup.Assigns.Add("bla:", "root:bla/"); - ioSetup.Assigns.Add("blub:", "cwd:blub/"); - ioSetup.FileSystems.Add("file", LocalFileSystem::Creator()); - IO::Setup(ioSetup); + IO::Setup(IOSetup() + .Assign("bla:", "root:bla/") + .Assign("blub:", "cwd:blub/") + .FileSystem("file", LocalFileSystem::Creator())); // check whether assigns have been set StringBuilder strBuilder; @@ -90,9 +89,7 @@ TEST(LocalFileSystemTest) { TEST(SameExtensionTest) { Core::Setup(); - IOSetup ioSetup; - ioSetup.FileSystems.Add("file", LocalFileSystem::Creator()); - IO::Setup(ioSetup); + IO::Setup(IOSetup().FileSystem("file", LocalFileSystem::Creator())); // write 2 files with the same extension const String helloJSON("Hello JSON! Bla bla bla bla"); diff --git a/code/Modules/Resource/CMakeLists.txt b/code/Modules/Resource/CMakeLists.txt index 13cbdb7ee..23dba28ad 100644 --- a/code/Modules/Resource/CMakeLists.txt +++ b/code/Modules/Resource/CMakeLists.txt @@ -8,25 +8,23 @@ fips_begin_module(Resource) Locator.cc Locator.h ResourceLabel.h ResourceState.h - ResourceLoader.cc ResourceLoader.h ResourcePool.h - SetupAndData.h - ResourceContainerBase.cc ResourceContainerBase.h + ResourceLabelStack.cc ResourceLabelStack.h ResourceRegistry.cc ResourceRegistry.h ResourceBase.h ) fips_deps(Core) fips_end_module() -fips_begin_unittest(Resource) - fips_vs_warning_level(3) - fips_dir(UnitTests) - fips_files( - IdTest.cc - LocatorTest.cc - ResourcePoolTest.cc - resourceRegistryTest.cc - StateTest.cc - ) - fips_deps(Resource Core) -fips_end_unittest() +#fips_begin_unittest(Resource) +# fips_vs_warning_level(3) +# fips_dir(UnitTests) +# fips_files( +# IdTest.cc +# LocatorTest.cc +# ResourcePoolTest.cc +# resourceRegistryTest.cc +# StateTest.cc +# ) +# fips_deps(Resource Core) +#fips_end_unittest() diff --git a/code/Modules/Resource/Locator.h b/code/Modules/Resource/Locator.h index c432dced5..34f726496 100644 --- a/code/Modules/Resource/Locator.h +++ b/code/Modules/Resource/Locator.h @@ -107,12 +107,12 @@ Locator::operator!=(const Locator& rhs) const { //------------------------------------------------------------------------------ inline bool Locator::operator<(const Locator& rhs) const { - if (this->signature < rhs.signature) { - return true; - } - else { - return this->location < rhs.location; - } + if (this->location == rhs.location) { + return this->signature < rhs.signature; + } + else { + return this->location < rhs.location; + } } //------------------------------------------------------------------------------ diff --git a/code/Modules/Resource/ResourceBase.h b/code/Modules/Resource/ResourceBase.h index 1124fb97d..b9218f4ba 100644 --- a/code/Modules/Resource/ResourceBase.h +++ b/code/Modules/Resource/ResourceBase.h @@ -13,7 +13,6 @@ of a resource is controlled by a matching Setup object, which holds all information required to create a resource object. */ -#include "Core/Assertion.h" #include "Resource/Id.h" #include "Resource/ResourceState.h" @@ -25,8 +24,6 @@ class ResourceBase { class Id Id; /// current resource state ResourceState::Code State = ResourceState::Initial; - /// frame count of last state change - int StateStartFrame = 0; }; } // namespace Oryol diff --git a/code/Modules/Resource/ResourceContainerBase.h b/code/Modules/Resource/ResourceContainerBase.h deleted file mode 100644 index 47779fca2..000000000 --- a/code/Modules/Resource/ResourceContainerBase.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::ResourceContainerBase - @ingroup Resource - @brief base class for resource containers - - A resource container manages creation, pooling, querying and - discard for different types of related resources. Modules like - the Gfx module typically derive a single ResourceContainer subclass - to wrap their different resource types. -*/ -#include "Core/Types.h" -#include "Core/Containers/Array.h" -#include "Resource/ResourceRegistry.h" -#include "Resource/ResourceLabel.h" - -namespace Oryol { - -class ResourceContainerBase { -public: - /// destructor - ~ResourceContainerBase(); - - /// setup the resource container - void Setup(int labelStackCapacity, int registryCapacity); - /// discard the resource container - void Discard(); - /// return true if valid - bool IsValid() const; - /// peek top of label stack - ResourceLabel PeekLabel() const; - /// generate new resource label and push on label stack - ResourceLabel PushLabel(); - /// push explicit resource label on label stack - void PushLabel(ResourceLabel label); - /// pop resource label from label stack - ResourceLabel PopLabel(); - /// lookup a resource Id by Locator - Id Lookup(const Locator& locator) const; - - Array labelStack; - ResourceRegistry registry; - uint32_t curLabelCount = 0; - bool valid = false; -}; - -} // namespace Oryol diff --git a/code/Modules/Resource/ResourceInfo.h b/code/Modules/Resource/ResourceInfo.h index 745409586..09e37758a 100644 --- a/code/Modules/Resource/ResourceInfo.h +++ b/code/Modules/Resource/ResourceInfo.h @@ -13,8 +13,6 @@ class ResourceInfo { public: /// current state of the resource ResourceState::Code State = ResourceState::InvalidState; - /// age of current state in number of frame - int StateAge = 0; }; } // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Resource/ResourceContainerBase.cc b/code/Modules/Resource/ResourceLabelStack.cc similarity index 55% rename from code/Modules/Resource/ResourceContainerBase.cc rename to code/Modules/Resource/ResourceLabelStack.cc index 7d9bfe0fc..39a1267c9 100644 --- a/code/Modules/Resource/ResourceContainerBase.cc +++ b/code/Modules/Resource/ResourceLabelStack.cc @@ -1,48 +1,45 @@ //------------------------------------------------------------------------------ -// ResourceContainerBase.cc +// ResourceLabelStack.cc //------------------------------------------------------------------------------ #include "Pre.h" -#include "Core/Core.h" -#include "Core/Assertion.h" -#include "ResourceContainerBase.h" +#include "ResourceLabelStack.h" namespace Oryol { - + //------------------------------------------------------------------------------ -ResourceContainerBase::~ResourceContainerBase() { - o_assert_dbg(!this->valid); +ResourceLabelStack::~ResourceLabelStack() { + o_assert_dbg(!this->isValid); } //------------------------------------------------------------------------------ void -ResourceContainerBase::Setup(int labelStackCapacity, int registryCapacity) { - o_assert_dbg(!this->valid); - this->labelStack.Reserve(labelStackCapacity); - this->registry.Setup(registryCapacity); - this->valid = true; +ResourceLabelStack::Setup(int stackCapacity) { + o_assert_dbg(!this->isValid); + o_assert_dbg(stackCapacity > 0); + this->labelStack.Reserve(stackCapacity); + this->isValid = true; this->PushLabel(ResourceLabel::Default); } //------------------------------------------------------------------------------ void -ResourceContainerBase::Discard() { - o_assert_dbg(this->valid); +ResourceLabelStack::Discard() { + o_assert_dbg(this->isValid); o_assert_dbg(this->labelStack.Size() == 1); this->PopLabel(); - this->registry.Discard(); - this->valid = false; + this->isValid = false; } //------------------------------------------------------------------------------ bool -ResourceContainerBase::IsValid() const { - return this->valid; +ResourceLabelStack::IsValid() const { + return this->isValid; } //------------------------------------------------------------------------------ ResourceLabel -ResourceContainerBase::PushLabel() { - o_assert_dbg(this->valid); +ResourceLabelStack::PushLabel() { + o_assert_dbg(this->isValid); o_assert_dbg(this->curLabelCount < ResourceLabel::Default); this->labelStack.Add(this->curLabelCount++); return this->labelStack.Back(); @@ -50,15 +47,15 @@ ResourceContainerBase::PushLabel() { //------------------------------------------------------------------------------ void -ResourceContainerBase::PushLabel(ResourceLabel label) { - o_assert_dbg(this->valid); +ResourceLabelStack::PushLabel(ResourceLabel label) { + o_assert_dbg(this->isValid); this->labelStack.Add(label); } //------------------------------------------------------------------------------ ResourceLabel -ResourceContainerBase::PopLabel() { - o_assert_dbg(this->valid); +ResourceLabelStack::PopLabel() { + o_assert_dbg(this->isValid); ResourceLabel label = this->labelStack.Back(); this->labelStack.Erase(this->labelStack.Size() - 1); return label; @@ -66,16 +63,9 @@ ResourceContainerBase::PopLabel() { //------------------------------------------------------------------------------ ResourceLabel -ResourceContainerBase::PeekLabel() const { - o_assert_dbg(this->valid); +ResourceLabelStack::PeekLabel() const { + o_assert_dbg(this->isValid); return this->labelStack.Back(); } -//------------------------------------------------------------------------------ -Id -ResourceContainerBase::Lookup(const Locator& loc) const { - o_assert_dbg(this->valid); - return this->registry.Lookup(loc); -} - } // namespace Oryol diff --git a/code/Modules/Resource/ResourceLabelStack.h b/code/Modules/Resource/ResourceLabelStack.h new file mode 100644 index 000000000..4ab953f6e --- /dev/null +++ b/code/Modules/Resource/ResourceLabelStack.h @@ -0,0 +1,39 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + @class Oryol::ResourceLabelStack + @ingroup Resource + @brief a stack for resource labels +*/ +#include "Core/Containers/Array.h" +#include "Resource/ResourceLabel.h" + +namespace Oryol { + +class ResourceLabelStack { +public: + /// destructor + ~ResourceLabelStack(); + + /// setup the label stack + void Setup(int stackCapacity); + /// discard the label stack + void Discard(); + /// return true if object has been setup + bool IsValid() const; + + /// peek top of label stack + ResourceLabel PeekLabel() const; + /// generate new resource label and push on label stack + ResourceLabel PushLabel(); + /// push explicit resource label on label stack + void PushLabel(ResourceLabel label); + /// pop resource label from label stack + ResourceLabel PopLabel(); + + Array labelStack; + uint32_t curLabelCount = 0; + bool isValid = false; +}; + +} // namespace Oryol diff --git a/code/Modules/Resource/ResourceLoader.cc b/code/Modules/Resource/ResourceLoader.cc deleted file mode 100644 index 793169f8e..000000000 --- a/code/Modules/Resource/ResourceLoader.cc +++ /dev/null @@ -1,34 +0,0 @@ -//------------------------------------------------------------------------------ -// ResourceLoader.cc -//------------------------------------------------------------------------------ -#include "Pre.h" -#include "ResourceLoader.h" - -namespace Oryol { - -//------------------------------------------------------------------------------ -class Locator -ResourceLoader::Locator() const { - class Locator loc; - return loc; -} - -//------------------------------------------------------------------------------ -Id -ResourceLoader::Start() { - return Id::InvalidId(); -} - -//------------------------------------------------------------------------------ -ResourceState::Code -ResourceLoader::Continue() { - return ResourceState::InvalidState; -} - -//------------------------------------------------------------------------------ -void -ResourceLoader::Cancel() { - // empty -} - -} // namespace Oryol diff --git a/code/Modules/Resource/ResourceLoader.h b/code/Modules/Resource/ResourceLoader.h deleted file mode 100644 index 818a084a6..000000000 --- a/code/Modules/Resource/ResourceLoader.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::ResourceLoader - @ingroup Resource - @brief base class for resource loaders -*/ -#include "Core/RefCounted.h" -#include "Resource/Id.h" -#include "Resource/Locator.h" -#include "Resource/ResourceState.h" - -namespace Oryol { - -class ResourceLoader : public RefCounted { - OryolClassDecl(ResourceLoader); -public: - /// return resource locator - virtual class Locator Locator() const; - /// start loading, return a resource id - virtual Id Start(); - /// continue loading, return resource state (Pending, Valid, Failed) - virtual ResourceState::Code Continue(); - /// cancel the resource loading process - virtual void Cancel(); -}; - -} // namespace Oryol diff --git a/code/Modules/Resource/ResourcePool.h b/code/Modules/Resource/ResourcePool.h index 89ecf8882..cc3b52043 100644 --- a/code/Modules/Resource/ResourcePool.h +++ b/code/Modules/Resource/ResourcePool.h @@ -50,8 +50,6 @@ template class ResourcePool { bool Contains(const Id& id) const; /// query the loading state of a contained resource ResourceState::Code QueryState(const Id& id) const; - /// query additional info about a contained resource - ResourceInfo QueryResourceInfo(const Id& id) const; /// query additional info about the pool (slow) ResourcePoolInfo QueryPoolInfo() const; @@ -168,7 +166,6 @@ ResourcePool::Assign(const Id& id, ResourceState::Code state) { auto& slot = this->slots[id.SlotIndex]; o_assert_dbg(ResourceState::Valid != slot.State); slot.State = state; - slot.StateStartFrame = this->frameCounter; slot.Id = id; return slot; } @@ -183,7 +180,6 @@ ResourcePool::Unassign(const Id& id) { o_assert_dbg(ResourceState::Initial != slot.State); slot.Id.Invalidate(); slot.State = ResourceState::Initial; - slot.StateStartFrame = 0; this->FreeId(id); } else { @@ -233,7 +229,6 @@ ResourcePool::UpdateState(const Id& id, ResourceState::Code newState) if (id == slot.Id) { o_assert_dbg(ResourceState::Initial != slot.State); slot.State = newState; - slot.StateStartFrame = this->frameCounter; } else { o_warn("ResourcePool::UpdateState(): id not in pool (type: '%d', slot: '%d')\n", id.Type, id.SlotIndex); @@ -263,21 +258,6 @@ ResourcePool::QueryState(const Id& id) const { } } -//------------------------------------------------------------------------------ -template ResourceInfo -ResourcePool::QueryResourceInfo(const Id& id) const { - o_assert_dbg(this->isValid); - o_assert_dbg(id.Type == this->resourceType); - - ResourceInfo info; - const auto& slot = this->slots[id.SlotIndex]; - if (id == slot.Id) { - info.State = slot.State; - info.StateAge = this->frameCounter - slot.StateStartFrame; - } - return info; -} - //------------------------------------------------------------------------------ template ResourcePoolInfo ResourcePool::QueryPoolInfo() const { diff --git a/code/Modules/Resource/ResourceRegistry.cc b/code/Modules/Resource/ResourceRegistry.cc index 9b1bab291..90d72de87 100644 --- a/code/Modules/Resource/ResourceRegistry.cc +++ b/code/Modules/Resource/ResourceRegistry.cc @@ -112,7 +112,7 @@ ResourceRegistry::Remove(ResourceLabel label) { // FIXME: this can be slow if many resource are live! int entryIndex = this->entries.Size() - 1; for (; entryIndex >= 0; entryIndex--) { - if ((ResourceLabel::All == label) || (this->entries[entryIndex].label == label)) { + if ((ResourceLabel::All == label) || (this->entries[entryIndex].label == label)) { Id id = this->entries[entryIndex].id; Locator loc = this->entries[entryIndex].locator; removed.Add(id); @@ -120,7 +120,8 @@ ResourceRegistry::Remove(ResourceLabel label) { // remove entries this->entries.EraseSwapBack(entryIndex); this->idIndexMap.Erase(id); - if (loc.IsShared()) { + if (loc.IsShared()) { + o_assert_dbg(this->locatorIndexMap.Contains(loc)); this->locatorIndexMap.Erase(loc); } @@ -142,7 +143,8 @@ ResourceRegistry::Remove(ResourceLabel label) { } // make sure nothing broke - #if ORYOL_DEBUG + #if ORYOL_DEBUG + //this->DumpDebugInfo(); o_assert(this->CheckIntegrity()); #endif } @@ -189,11 +191,11 @@ ResourceRegistry::GetIdByIndex(int index) const { //------------------------------------------------------------------------------ #if ORYOL_DEBUG bool -ResourceRegistry::CheckIntegrity() const { +ResourceRegistry::CheckIntegrity() const { for (const auto& kvp : this->locatorIndexMap) { const Locator& loc = kvp.key; const int entryIndex = kvp.value; - const Locator& entryLoc = this->entries[entryIndex].locator; + const Locator& entryLoc = this->entries[entryIndex].locator; if (entryLoc != loc) { o_error("ResourceRegistry: locator mismatch at index '%d' (%s != %s)\n", entryIndex, entryLoc.Location().AsCStr(), loc.Location().AsCStr()); @@ -212,7 +214,39 @@ ResourceRegistry::CheckIntegrity() const { } } return true; -} +} #endif + +//------------------------------------------------------------------------------ +#if ORYOL_DEBUG +void +ResourceRegistry::DumpDebugInfo() const { + Log::Info("\n\n--- entries:\n"); + for (int i = 0; i < this->entries.Size(); i++) { + const auto& item = this->entries[i]; + Log::Info("%d: loc=%s/%08X, id=%lld, label=%d\n", + i, + item.locator.HasValidLocation() ? item.locator.Location().AsCStr() : "---", + item.locator.Signature(), + item.id.Value, item.label.Value); + } + Log::Info("--- locator/index map:\n"); + for (int i = 0; i < this->locatorIndexMap.Size(); i++) { + const Locator& loc = this->locatorIndexMap.KeyAtIndex(i); + int entryIndex = this->locatorIndexMap.ValueAtIndex(i); + Log::Info("%d: loc=%s/%08X, i=%d\n", + i, + loc.HasValidLocation() ? loc.Location().AsCStr() : "---", + loc.Signature(), + entryIndex); + } + Log::Info("--- id/index map:\n"); + for (int i = 0; i < this->idIndexMap.Size(); i++) { + const Id& id = this->idIndexMap.KeyAtIndex(i); + int entryIndex = this->idIndexMap.ValueAtIndex(i); + Log::Info("%d: id=%lld, i=%d\n", i, id.Value, entryIndex); + } +} +#endif } // namespace Oryol diff --git a/code/Modules/Resource/ResourceRegistry.h b/code/Modules/Resource/ResourceRegistry.h index fcca16e30..31a20bd3a 100644 --- a/code/Modules/Resource/ResourceRegistry.h +++ b/code/Modules/Resource/ResourceRegistry.h @@ -46,7 +46,9 @@ class ResourceRegistry { #if ORYOL_DEBUG /// validate integrity of internal data structures - bool CheckIntegrity() const; + bool CheckIntegrity() const; + /// dump debugging info to stdout + void DumpDebugInfo() const; #endif struct Entry { diff --git a/code/Modules/Resource/ResourceState.h b/code/Modules/Resource/ResourceState.h index 5ca26325b..bf1b036ad 100644 --- a/code/Modules/Resource/ResourceState.h +++ b/code/Modules/Resource/ResourceState.h @@ -8,11 +8,10 @@ These are the states a resource object goes through during its lifetime: * Initial: resource object has just been created - * Setup: resource object has a valid Setup object, but is not loaded - * Pending: asynchronous loading is underway + * Alloc: asynchronous loading is underway * Valid: resource is valid and can be used - * Failed: resource creation has failed - + * Failed: resource creation has failed + Resources can be unloaded, which changes the state from Valid back to Setup, and then be loaded again. */ @@ -23,8 +22,7 @@ class ResourceState { /// state codes enum Code { Initial, ///< resource has just been created - Setup, ///< the resource has a setup object, but is not loaded - Pending, ///< resource is pending (asynchronous loading) + Alloc, ///< resource id has been allocated but not initialized yet Valid, ///< resource has become valid Failed, ///< resource creation has failed @@ -36,8 +34,7 @@ class ResourceState { static const char* ToString(Code c) { switch (c) { case Initial: return "Initial"; - case Setup: return "Setup"; - case Pending: return "Pending"; + case Alloc: return "Alloc"; case Valid: return "Valid"; case Failed: return "Failed"; default: return "InvalidState"; diff --git a/code/Modules/Resource/SetupAndData.h b/code/Modules/Resource/SetupAndData.h deleted file mode 100644 index 767f677d3..000000000 --- a/code/Modules/Resource/SetupAndData.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -//------------------------------------------------------------------------------ -/** - @class Oryol::SetupAndData - @ingroup Resource - @brief holds a setup and a data buffer object - - This is used to transfer both a resource setup object and - a stream object to resource creation functions. -*/ -#include "Core/Containers/Buffer.h" - -namespace Oryol { - -template class SetupAndData { -public: - /// default constructor - SetupAndData() { }; - /// construct from Setup and Stream object - SetupAndData(const SETUP& setup, Buffer&& data) : - Setup(setup), - Data(std::move(data)) { - // empty - }; - /// move construct - SetupAndData(SetupAndData&& rhs) { - this->Setup = std::move(rhs.Setup); - this->Data = std::move(rhs.Data); - }; - /// move assignment - void operator=(SetupAndData&& rhs) { - this->Setup = std::move(rhs.Setup); - this->Data = std::move(rhs.Data); - }; - - /// disable copy constructor - SetupAndData(const SetupAndData& rhs) = delete; - /// disable copy assignment - void operator=(const SetupAndData& rhs) = delete; - - /// embedded setup object - SETUP Setup; - /// embedded data buffer - Buffer Data; -}; - -} // namespace Oryol \ No newline at end of file diff --git a/code/Modules/Resource/UnitTests/ResourcePoolTest.cc b/code/Modules/Resource/UnitTests/ResourcePoolTest.cc index 55ab0b089..bf036946b 100644 --- a/code/Modules/Resource/UnitTests/ResourcePoolTest.cc +++ b/code/Modules/Resource/UnitTests/ResourcePoolTest.cc @@ -41,7 +41,6 @@ TEST(ResourcePoolTest) { const myResource* res = resourcePool.Lookup(resId); CHECK(nullptr != res); CHECK(res->Id == resId); - CHECK(resourcePool.QueryResourceInfo(resId).State == ResourceState::Valid); Id resId1 = resourcePool.AllocId(); CHECK(resId1.IsValid()); @@ -58,7 +57,6 @@ TEST(ResourcePoolTest) { const myResource* res1 = resourcePool.Lookup(resId1); CHECK(nullptr != res1); CHECK(res1->Id == resId1); - CHECK(resourcePool.QueryResourceInfo(resId1).State == ResourceState::Valid); const ResourcePoolInfo poolInfo = resourcePool.QueryPoolInfo(); CHECK(poolInfo.ResourceType == myResourceType); diff --git a/code/Samples/ArrayTexture/ArrayTexture.cc b/code/Samples/ArrayTexture/ArrayTexture.cc index 1974c4c4d..8fc982c51 100644 --- a/code/Samples/ArrayTexture/ArrayTexture.cc +++ b/code/Samples/ArrayTexture/ArrayTexture.cc @@ -24,18 +24,22 @@ class ArrayTextureApp : public App { AppState::Code notSupported(); Shader::vsParams computeShaderParams(); - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; int frameIndex = 0; - glm::mat4 proj; }; OryolMain(ArrayTextureApp); //------------------------------------------------------------------------------ AppState::Code ArrayTextureApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(800, 512, "Array Texture Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.2f, 0.2f, 0.3f, 1.0f)); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(512) + .SetSampleCount(4) + .SetTitle("Array Texture Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); // if array textures are not supported, only show a warning @@ -64,33 +68,33 @@ ArrayTextureApp::OnInit() { } } } - auto texSetup = TextureSetup::FromPixelDataArray(16, 16, numLayers, 1, PixelFormat::RGBA8); - texSetup.Sampler.MinFilter = TextureFilterMode::Linear; - texSetup.Sampler.MagFilter = TextureFilterMode::Linear; - texSetup.ImageData.Sizes[0][0] = sizeof(data); - this->drawState.FSTexture[Shader::tex] = Gfx::CreateResource(texSetup, data, sizeof(data)); + this->bind.FSTexture[Shader::tex] = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::TextureArray) + .SetWidth(width) + .SetHeight(height) + .SetLayers(numLayers) + .SetFormat(PixelFormat::RGBA8) + .SetMinFilter(TextureFilterMode::Linear) + .SetMagFilter(TextureFilterMode::Linear) + .SetMipSize(0, 0, sizeof(data)) + .SetMipContent(0, 0, data)); // build a cube mesh - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .TexCoords("in_uv", VertexFormat::Float2) + .Box(1.0f, 1.0f, 1.0f, 1) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); // ...and a pipeline object to complete the DrawState - Id shd = Gfx::CreateResource(Shader::Setup()); - auto pipSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - pipSetup.DepthStencilState.DepthWriteEnabled = true; - pipSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - pipSetup.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(pipSetup); - - // setup a projection matrix with the right aspect ratio - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); return App::OnInit(); } @@ -108,17 +112,17 @@ ArrayTextureApp::OnRunning() { auto vsParams = this->computeShaderParams(); // render texture cube - Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(); + Gfx::BeginPass(PassAction().Clear(0.2f, 0.2f, 0.3f, 1.0f)); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(this->primGroup); Gfx::EndPass(); Gfx::CommitFrame(); this->frameIndex++; return Gfx::QuitRequested() ? AppState::Cleanup : AppState::Running; } - //------------------------------------------------------------------------------ AppState::Code ArrayTextureApp::OnCleanup() { @@ -137,13 +141,14 @@ ArrayTextureApp::computeShaderParams() { vsParams.uvOffset1 = glm::vec2(-offset, offset); vsParams.uvOffset2 = glm::vec2(0.0f, 0.0f); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); const glm::vec3 cubePos(0.0f, 0.0f, -2.5f); float angleX = glm::radians(0.25f * this->frameIndex); float angleY = glm::radians(0.2f * this->frameIndex); glm::mat4 model = glm::translate(glm::mat4(), cubePos); model = glm::rotate(model, angleX, glm::vec3(1.0f, 0.0f, 0.0f)); model = glm::rotate(model, angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - vsParams.mvp = this->proj * model; + vsParams.mvp = proj * model; return vsParams; } @@ -156,9 +161,9 @@ ArrayTextureApp::notSupported() { #else const char* msg = "This demo needs array texture support\n"; #endif - uint8_t x = uint8_t((Gfx::DisplayAttrs().FramebufferWidth/16 - std::strlen(msg))/2); - uint8_t y = uint8_t(Gfx::DisplayAttrs().FramebufferHeight/16/2); - Gfx::BeginPass(PassAction::Clear(glm::vec4(0.5f, 0.0f, 0.0f, 1.0f))); + uint8_t x = uint8_t((Gfx::DisplayAttrs().Width/16 - std::strlen(msg))/2); + uint8_t y = uint8_t((Gfx::DisplayAttrs().Height/16)/2); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.0f, 0.0f, 1.0f)); Dbg::TextScale(2.0f, 2.0f); Dbg::CursorPos(x, y); Dbg::Print(msg); diff --git a/code/Samples/ArrayTexture/shaders.glsl b/code/Samples/ArrayTexture/shaders.glsl index 342fccffa..a4251a5f3 100644 --- a/code/Samples/ArrayTexture/shaders.glsl +++ b/code/Samples/ArrayTexture/shaders.glsl @@ -10,17 +10,17 @@ uniform vsParams { vec2 uvOffset2; }; -in vec4 position; -in vec2 texcoord0; +in vec4 in_pos; +in vec2 in_uv; out vec3 uv0; out vec3 uv1; out vec3 uv2; void main() { - gl_Position = mvp * position; - uv0 = vec3(texcoord0 + uvOffset0, 0.0); - uv1 = vec3(texcoord0 + uvOffset1, 1.0); - uv2 = vec3(texcoord0 + uvOffset2, 2.0); + gl_Position = mvp * in_pos; + uv0 = vec3(in_uv + uvOffset0, 0.0); + uv1 = vec3(in_uv + uvOffset1, 1.0); + uv2 = vec3(in_uv + uvOffset2, 2.0); } @end diff --git a/code/Samples/BlendTest/BlendTest.cc b/code/Samples/BlendTest/BlendTest.cc index dc47d9b29..1d3107215 100644 --- a/code/Samples/BlendTest/BlendTest.cc +++ b/code/Samples/BlendTest/BlendTest.cc @@ -4,7 +4,6 @@ #include "Pre.h" #include "Core/Main.h" #include "Gfx/Gfx.h" -#include "Assets/Gfx/MeshBuilder.h" #include "shaders.h" using namespace Oryol; @@ -15,9 +14,10 @@ class BlendTestApp : public App { AppState::Code OnInit(); AppState::Code OnCleanup(); - DrawState bgDrawState; - Id triMesh; - Id pipelines[BlendFactor::NumBlendFactors][BlendFactor::NumBlendFactors]; + Id bgPipeline; + Bindings bgBindings; + Id triVBuf; + Id pipelines[BlendFactor::Num][BlendFactor::Num]; TriShader::params params; }; OryolMain(BlendTestApp); @@ -26,49 +26,53 @@ OryolMain(BlendTestApp); AppState::Code BlendTestApp::OnInit() { // setup rendering system - auto gfxSetup = GfxSetup::Window(1024, 768, "Oryol Blend Sample"); - gfxSetup.ResourcePoolSize[GfxResourceType::Pipeline] = 512; - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(1024) + .SetHeight(768) + .SetTitle("Oryol Blend Sample") + .SetHtmlTrackElementSize(true) + .SetResourcePoolSize(GfxResourceType::Pipeline, 512)); // create pipeline object for a patterned background - auto ms = MeshSetup::FullScreenQuad(); - this->bgDrawState.Mesh[0] = Gfx::CreateResource(ms); - Id bgShd = Gfx::CreateResource(BGShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(ms.Layout, bgShd); - this->bgDrawState.Pipeline = Gfx::CreateResource(ps); + const float bgVertices[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + this->bgBindings.VertexBuffers[0] = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(bgVertices)) + .SetContent(bgVertices)); + this->bgPipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(BGShader::Desc())) + .SetLayout(0, { + { "in_pos", VertexFormat::Float2 } + }) + .SetPrimitiveType(PrimitiveType::TriangleStrip)); // setup a triangle mesh and shader - MeshBuilder meshBuilder; - meshBuilder.NumVertices = 3; - meshBuilder.IndicesType = IndexType::None; - meshBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } + float triVertices[] = { + // pos color + 0.0f, 0.05f, 0.5f, 0.7f, 0.0f, 0.0f, 0.75f, + 0.05f, -0.05f, 0.5f, 0.0f, 0.75f, 0.0f, 0.75f, + -0.05f, -0.05f, 0.5f, 0.0f, 0.0f, 0.75f, 0.75f }; - meshBuilder.PrimitiveGroups.Add(0, 3); - meshBuilder.Begin() - .Vertex(0, VertexAttr::Position, 0.0f, 0.05f, 0.5f) - .Vertex(0, VertexAttr::Color0, 0.75f, 0.0f, 0.0f, 0.75f) - .Vertex(1, VertexAttr::Position, 0.05f, -0.05f, 0.5f) - .Vertex(1, VertexAttr::Color0, 0.0f, 0.75f, 0.0f, 0.75f) - .Vertex(2, VertexAttr::Position, -0.05f, -0.05f, 0.5f) - .Vertex(2, VertexAttr::Color0, 0.0f, 0.0f, 0.75f, 0.75f); - this->triMesh = Gfx::CreateResource(meshBuilder.Build()); - Id shd = Gfx::CreateResource(TriShader::Setup()); - + this->triVBuf = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(triVertices)) + .SetContent(triVertices)); + // setup one draw state for each blend factor combination - ps = PipelineSetup::FromLayoutAndShader(meshBuilder.Layout, shd); - ps.BlendState.BlendEnabled = true; - ps.BlendColor = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); - ps.BlendState.ColorWriteMask = PixelChannel::RGB; - for (uint32_t y = 0; y < BlendFactor::NumBlendFactors; y++) { - for (uint32_t x = 0; x < BlendFactor::NumBlendFactors; x++) { - ps.BlendState.SrcFactorRGB = (BlendFactor::Code) x; - ps.BlendState.DstFactorRGB = (BlendFactor::Code) y; - this->pipelines[y][x] = Gfx::CreateResource(ps); + auto ps = PipelineDesc() + .SetShader(Gfx::CreateShader(TriShader::Desc())) + .SetLayout(0, { + { "in_pos", VertexFormat::Float3 }, + { "in_color", VertexFormat::Float4 } + }) + .SetBlendEnabled(true) + .SetBlendColor(glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)) + .SetColorWriteMask(PixelChannel::RGB); + for (uint32_t y = 0; y < BlendFactor::Num; y++) { + for (uint32_t x = 0; x < BlendFactor::Num; x++) { + ps.SetBlendSrcFactorRGB((BlendFactor::Code)x); + ps.SetBlendDstFactorRGB((BlendFactor::Code)y); + this->pipelines[y][x] = Gfx::CreatePipeline(ps); } } - return App::OnInit(); } @@ -78,21 +82,45 @@ BlendTestApp::OnRunning() { // draw checkboard background Gfx::BeginPass(); - Gfx::ApplyDrawState(this->bgDrawState); - Gfx::Draw(); + Gfx::ApplyPipeline(this->bgPipeline); + Gfx::ApplyBindings(this->bgBindings); + Gfx::Draw(0, 4); // draw blended triangles - DrawState triDrawState; - triDrawState.Mesh[0] = this->triMesh; - float d = 1.0f / BlendFactor::NumBlendFactors; - for (uint32_t y = 0; y < BlendFactor::NumBlendFactors; y++) { - for (uint32_t x = 0; x < BlendFactor::NumBlendFactors; x++) { - this->params.translate.x = ((d * x) + d*0.5f) * 2.0f - 1.0f; - this->params.translate.y = ((d * y) + d*0.5f) * 2.0f - 1.0f; - triDrawState.Pipeline = this->pipelines[y][x]; - Gfx::ApplyDrawState(triDrawState); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(); + Bindings triBind; + triBind.VertexBuffers[0] = this->triVBuf; + float d = 1.0f / BlendFactor::Num; + for (uint32_t y = 0; y < BlendFactor::Num; y++) { + for (uint32_t x = 0; x < BlendFactor::Num; x++) { + bool valid = true; + /* WebGL exceptions: + - "GL_SRC_ALPHA_SATURATE as a destination blend function is disallowed in WebGL 1" + - "constant color and constant alpha cannot be used together as source and + destination factors in the blend function" + */ + const BlendFactor::Code src = (BlendFactor::Code) x; + const BlendFactor::Code dst = (BlendFactor::Code) y; + if (dst == BlendFactor::SrcAlphaSaturated) { + valid = false; + } + else if (((src == BlendFactor::BlendColor) || (src == BlendFactor::OneMinusBlendColor)) && + ((dst == BlendFactor::BlendAlpha) || (dst == BlendFactor::OneMinusBlendAlpha))) + { + valid = false; + } + else if (((src == BlendFactor::BlendAlpha) || (src == BlendFactor::OneMinusBlendAlpha)) && + ((dst == BlendFactor::BlendColor) || (dst == BlendFactor::OneMinusBlendColor))) + { + valid = false; + } + if (valid) { + this->params.translate.x = ((d * x) + d*0.5f) * 2.0f - 1.0f; + this->params.translate.y = ((d * y) + d*0.5f) * 2.0f - 1.0f; + Gfx::ApplyPipeline(this->pipelines[y][x]); + Gfx::ApplyBindings(triBind); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(0, 3); + } } } Gfx::EndPass(); diff --git a/code/Samples/BlendTest/CMakeLists.txt b/code/Samples/BlendTest/CMakeLists.txt index 58df7bec0..b5e280d07 100644 --- a/code/Samples/BlendTest/CMakeLists.txt +++ b/code/Samples/BlendTest/CMakeLists.txt @@ -2,6 +2,6 @@ fips_begin_app(BlendTest windowed) fips_vs_warning_level(3) fips_files(BlendTest.cc) oryol_shader(shaders.glsl) - fips_deps(Gfx Assets) + fips_deps(Gfx) oryol_add_web_sample(BlendTest "Test BlendState implementation" "emscripten,android" BlendTest.jpg "BlendTest/BlendTest.cc") fips_end_app() diff --git a/code/Samples/BlendTest/shaders.glsl b/code/Samples/BlendTest/shaders.glsl index 50e4401fb..6ad84c797 100644 --- a/code/Samples/BlendTest/shaders.glsl +++ b/code/Samples/BlendTest/shaders.glsl @@ -2,13 +2,12 @@ // background shader // @vs backgroundVS -in vec4 position; -in vec2 texcoord0; +in vec2 in_pos; out vec2 uv0; void main() { - gl_Position = position; - uv0 = texcoord0; + gl_Position = vec4(in_pos*2.0-1.0, 0.5f, 1.0f); + uv0 = in_pos; } @end @@ -30,13 +29,13 @@ void main() { uniform params { vec4 translate; }; -in vec4 position; -in vec4 color0; +in vec4 in_pos; +in vec4 in_color; out vec4 color; void main() { - gl_Position = position + translate; - color = color0; + gl_Position = in_pos + translate; + color = in_color; } @end diff --git a/code/Samples/Clear/Clear.cc b/code/Samples/Clear/Clear.cc index 282168067..b6d3be945 100644 --- a/code/Samples/Clear/Clear.cc +++ b/code/Samples/Clear/Clear.cc @@ -21,7 +21,11 @@ OryolMain(ClearApp); //------------------------------------------------------------------------------ AppState::Code ClearApp::OnInit() { - Gfx::Setup(GfxSetup::Window(400, 300, "Oryol Clear Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(400) + .SetHeight(300) + .SetTitle("Oryol Clear Sample") + .SetHtmlTrackElementSize(true)); return App::OnInit(); } diff --git a/code/Samples/CoreHello/CMakeLists.txt b/code/Samples/CoreHello/CMakeLists.txt index 1cb629c67..72a8b0efb 100644 --- a/code/Samples/CoreHello/CMakeLists.txt +++ b/code/Samples/CoreHello/CMakeLists.txt @@ -2,5 +2,4 @@ fips_begin_app(CoreHello cmdline) fips_vs_warning_level(3) fips_files(CoreHello.cc) fips_deps(Core) - oryol_add_web_sample(CoreHello "Low-level hello world sample" "emscripten" none "CoreHello/CoreHello.cc") fips_end_app() diff --git a/code/Samples/DDSCubeMap/DDSCubeMap.cc b/code/Samples/DDSCubeMap/DDSCubeMap.cc index e5aae5f62..2eb9951ee 100644 --- a/code/Samples/DDSCubeMap/DDSCubeMap.cc +++ b/code/Samples/DDSCubeMap/DDSCubeMap.cc @@ -22,10 +22,10 @@ class DDSCubeMapApp : public App { glm::mat4 computeMVP(const glm::vec3& pos); - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; Shader::vsParams vsParams; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -36,24 +36,18 @@ AppState::Code DDSCubeMapApp::OnInit() { // setup IO system - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - ioSetup.Assigns.Add("tex:", ORYOL_SAMPLE_URL); - IO::Setup(ioSetup); + IO::Setup(IODesc() + .AddFileSystem("http", HTTPFileSystem::Creator()) + .AddAssign("tex:", ORYOL_SAMPLE_URL)); // setup rendering system - auto gfxSetup = GfxSetup::Window(600, 400, "Oryol DXT Cube Map Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetTitle("Oryol DXT Cube Map Sample") + .SetHtmlTrackElementSize(true)); // create resources - Id shd = Gfx::CreateResource(Shader::Setup()); - - TextureSetup texBluePrint; - texBluePrint.Sampler.MinFilter = TextureFilterMode::LinearMipmapLinear; - texBluePrint.Sampler.MagFilter = TextureFilterMode::Linear; - texBluePrint.Sampler.WrapU = TextureWrapMode::ClampToEdge; - texBluePrint.Sampler.WrapV = TextureWrapMode::ClampToEdge; StringAtom texPath; if (Gfx::QueryFeature(GfxFeature::TextureCompressionPVRTC)) { texPath = "tex:romechurch_bpp2.pvr"; @@ -61,28 +55,27 @@ DDSCubeMapApp::OnInit() { else { texPath = "tex:romechurch_dxt1.dds"; } - this->drawState.FSTexture[Shader::tex] = Gfx::LoadResource( - TextureLoader::Create(TextureSetup::FromFile(texPath, texBluePrint)) - ); - glm::mat4 rot90 = glm::rotate(glm::mat4(), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Float3 } - }; - shapeBuilder.Transform(rot90).Sphere(1.0f, 36, 20); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - this->drawState.Pipeline = Gfx::CreateResource(ps); + this->bind.FSTexture[Shader::tex] = TextureLoader::Load(TextureDesc() + .SetLocator(texPath) + .SetMinFilter(TextureFilterMode::LinearMipmapLinear) + .SetMagFilter(TextureFilterMode::Linear) + .SetWrapU(TextureWrapMode::ClampToEdge) + .SetWrapV(TextureWrapMode::ClampToEdge)); + + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Float3) + .Transform(glm::rotate(glm::mat4(), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f))) + .Sphere(1.0f, 36, 20) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual)); - // setup projection and view matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); - return App::OnInit(); } @@ -94,14 +87,12 @@ DDSCubeMapApp::OnRunning() { this->angleY += 0.02f; this->angleX += 0.01f; - Gfx::BeginPass(); - const Id& tex = this->drawState.FSTexture[Shader::tex]; - if (Gfx::QueryResourceInfo(tex).State == ResourceState::Valid) { - this->vsParams.mvp = this->computeMVP(glm::vec3(0.0f, 0.0f, 0.0f)); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); - } + Gfx::BeginPass(PassAction().Clear(0.5f, 0.5f, 0.5f, 1.0f)); + this->vsParams.mvp = this->computeMVP(glm::vec3(0.0f, 0.0f, 0.0f)); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); Gfx::EndPass(); Gfx::CommitFrame(); @@ -122,9 +113,10 @@ DDSCubeMapApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 DDSCubeMapApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/DDSCubeMap/shaders.glsl b/code/Samples/DDSCubeMap/shaders.glsl index 2bdc8a93c..9c6e22ca8 100644 --- a/code/Samples/DDSCubeMap/shaders.glsl +++ b/code/Samples/DDSCubeMap/shaders.glsl @@ -5,13 +5,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec3 normal; +in vec4 in_pos; +in vec3 in_normal; out vec3 nrm; void main() { - gl_Position = mvp * position; - nrm = normal; + gl_Position = mvp * in_pos; + nrm = in_normal; } @end diff --git a/code/Samples/DDSTextureLoading/DDSTextureLoading.cc b/code/Samples/DDSTextureLoading/DDSTextureLoading.cc index 4b802439f..6f345b73f 100644 --- a/code/Samples/DDSTextureLoading/DDSTextureLoading.cc +++ b/code/Samples/DDSTextureLoading/DDSTextureLoading.cc @@ -24,12 +24,12 @@ class DDSTextureLoadingApp : public App { glm::mat4 computeMVP(const glm::vec3& pos); float distVal = 0.0f; - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; static const int NumTextures = 16; StaticArray textures; Shader::vsParams vsParams; - glm::mat4 view; - glm::mat4 proj; }; OryolMain(DDSTextureLoadingApp); @@ -38,24 +38,18 @@ AppState::Code DDSTextureLoadingApp::OnInit() { // setup IO system - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - ioSetup.Assigns.Add("tex:", ORYOL_SAMPLE_URL); - IO::Setup(ioSetup); + IO::Setup(IODesc() + .AddFileSystem("http", HTTPFileSystem::Creator()) + .AddAssign("tex:", ORYOL_SAMPLE_URL)); // setup rendering system - auto gfxSetup = GfxSetup::Window(600, 400, "Oryol DDS Loading Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetTitle("Oryol DDS Loading Sample") + .SetHtmlTrackElementSize(true)); // setup resources - Id shd = Gfx::CreateResource(Shader::Setup()); - - TextureSetup texBluePrint; - texBluePrint.Sampler.MinFilter = TextureFilterMode::LinearMipmapLinear; - texBluePrint.Sampler.MagFilter = TextureFilterMode::Linear; - texBluePrint.Sampler.WrapU = TextureWrapMode::ClampToEdge; - texBluePrint.Sampler.WrapV = TextureWrapMode::ClampToEdge; static const char *paths[NumTextures] = { "tex:lok_dxt1.dds", "tex:lok_dxt3.dds", @@ -75,26 +69,27 @@ DDSTextureLoadingApp::OnInit() { "tex:lok_bgr565.dds", }; for (int i = 0; i < NumTextures; i++) { - this->textures[i] = Gfx::LoadResource(TextureLoader::Create(TextureSetup::FromFile(paths[i], texBluePrint))); + this->textures[i] = TextureLoader::Load(TextureDesc() + .SetLocator(paths[i]) + .SetMinFilter(TextureFilterMode::LinearMipmapLinear) + .SetMagFilter(TextureFilterMode::Linear) + .SetWrapU(TextureWrapMode::ClampToEdge) + .SetWrapV(TextureWrapMode::ClampToEdge)); } - const glm::mat4 rot90 = glm::rotate(glm::mat4(), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Transform(rot90).Plane(1.0f, 1.0f, 4); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - this->drawState.Pipeline = Gfx::CreateResource(ps); - - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .TexCoords("in_uv", VertexFormat::Float2) + .Transform(glm::rotate(glm::mat4(), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f))) + .Plane(1.0f, 1.0f, 4) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual)); return App::OnInit(); } @@ -105,7 +100,7 @@ DDSTextureLoadingApp::OnRunning() { this->distVal += 0.01f; - Gfx::BeginPass(); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.5f, 0.5f, 1.0f)); // only render when texture is loaded (until texture placeholder are implemented) static const glm::vec3 pos[NumTextures] = { @@ -131,16 +126,14 @@ DDSTextureLoadingApp::OnRunning() { glm::vec3(+1.65f, -1.1f, 0.0f), glm::vec3(+2.75f, -1.1f, 0.0f) }; + Gfx::ApplyPipeline(this->pip); for (int i = 0; i < NumTextures; i++) { - const auto resState = Gfx::QueryResourceInfo(this->textures[i]).State; - if (resState == ResourceState::Valid) { - glm::vec3 p = pos[i] + glm::vec3(0.0f, 0.0f, -20.0f + glm::sin(this->distVal) * 19.0f); - this->vsParams.mvp = this->computeMVP(p); - this->drawState.FSTexture[Shader::tex] = this->textures[i]; - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); - } + glm::vec3 p = pos[i] + glm::vec3(0.0f, 0.0f, -20.0f + glm::sin(this->distVal) * 19.0f); + this->vsParams.mvp = this->computeMVP(p); + this->bind.FSTexture[Shader::tex] = this->textures[i]; + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); } Gfx::EndPass(); Gfx::CommitFrame(); @@ -160,7 +153,8 @@ DDSTextureLoadingApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 DDSTextureLoadingApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); - return this->proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/DDSTextureLoading/shaders.glsl b/code/Samples/DDSTextureLoading/shaders.glsl index 752704c6b..aeb0f8dfa 100644 --- a/code/Samples/DDSTextureLoading/shaders.glsl +++ b/code/Samples/DDSTextureLoading/shaders.glsl @@ -6,13 +6,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec2 texcoord0; +in vec4 in_pos; +in vec2 in_uv; out vec2 uv; void main() { - gl_Position = mvp * position; - uv = texcoord0; + gl_Position = mvp * in_pos; + uv = in_uv; } @end diff --git a/code/Samples/DebugText/DebugText.cc b/code/Samples/DebugText/DebugText.cc index b00bf6ddd..2b084869f 100644 --- a/code/Samples/DebugText/DebugText.cc +++ b/code/Samples/DebugText/DebugText.cc @@ -30,16 +30,16 @@ OryolMain(DebugTextApp); //------------------------------------------------------------------------------ AppState::Code DebugTextApp::OnInit() { - auto gfxSetup = GfxSetup::Window(800, 600, "Oryol DebugText Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)); - Gfx::Setup(gfxSetup); - DbgSetup dbgSetup; - dbgSetup.TextScaleX = 2.0f; - dbgSetup.TextScaleY = 2.0f; - Dbg::Setup(dbgSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetTitle("Oryol DebugText Sample")); + Dbg::Setup(DbgDesc() + .SetTextScaleX(2.0f) + .SetTextScaleY(2.0f)); - this->width = Gfx::DisplayAttrs().FramebufferWidth / 16; - this->height = Gfx::DisplayAttrs().FramebufferHeight / 16; + this->width = Gfx::DisplayAttrs().Width / 16; + this->height = Gfx::DisplayAttrs().Height / 16; this->buffer = (uint8_t*) Memory::Alloc(this->width * this->height); Memory::Clear(this->buffer, this->width * this->height); @@ -56,7 +56,7 @@ DebugTextApp::OnRunning() { this->moveChars(); this->drawText(); - Gfx::BeginPass(); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.5f, 0.5f, 1.0f)); Dbg::DrawTextBuffer(); Gfx::EndPass(); Gfx::CommitFrame(); diff --git a/code/Samples/DrawCallPerf/DrawCallPerf.cc b/code/Samples/DrawCallPerf/DrawCallPerf.cc index 3afb9eacb..0f1c0eade 100644 --- a/code/Samples/DrawCallPerf/DrawCallPerf.cc +++ b/code/Samples/DrawCallPerf/DrawCallPerf.cc @@ -25,10 +25,9 @@ class DrawCallPerfApp : public App { void emitParticles(); void updateParticles(); - DrawState drawState; - glm::mat4 view; - glm::mat4 proj; - glm::mat4 model; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; Shader::perFrameParams perFrameParams; Shader::perParticleParams perParticleParams; bool updateEnabled = true; @@ -48,35 +47,32 @@ OryolMain(DrawCallPerfApp); AppState::Code DrawCallPerfApp::OnInit() { // setup rendering system - GfxSetup gfxSetup = GfxSetup::Window(800, 500, "Oryol DrawCallPerf Sample"); - gfxSetup.GlobalUniformBufferSize = 1024 * 1024 * 32; - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(500) + .SetTitle("Oryol DrawCallPerf Sample") + .SetGlobalUniformBufferSize(1024 * 1024 * 32) + .SetHtmlTrackElementSize(true)); Dbg::Setup(); Input::Setup(); // create resources const glm::mat4 rot90 = glm::rotate(glm::mat4(), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - ShapeBuilder shapeBuilder; - shapeBuilder.RandomColors = true; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } - }; - shapeBuilder.Transform(rot90).Sphere(0.05f, 3, 2); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.RasterizerState.CullFaceEnabled = true; - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - this->drawState.Pipeline = Gfx::CreateResource(ps); - - // setup projection and view matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::lookAt(glm::vec3(0.0f, 2.5f, 0.0f), glm::vec3(0.0f, 0.0f, -10.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - this->model = glm::mat4(); + auto shape = ShapeBuilder() + .RandomColors(true) + .Positions("in_pos", VertexFormat::Float3) + .Colors("in_color", VertexFormat::Float4) + .Transform(rot90) + .Sphere(0.05f, 3, 2) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetCullFaceEnabled(true) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual)); return App::OnInit(); } @@ -102,12 +98,13 @@ DrawCallPerfApp::OnRunning() { Gfx::BeginPass(); applyRtTime = Clock::Since(applyRtStart); TimePoint drawStart = Clock::Now(); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->perFrameParams); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->perFrameParams); for (int i = 0; i < this->curNumParticles; i++) { this->perParticleParams.translate = this->particles[i].pos; - Gfx::ApplyUniformBlock(this->perParticleParams); - Gfx::Draw(); + Gfx::ApplyUniforms(this->perParticleParams); + Gfx::Draw(this->primGroup); } drawTime = Clock::Since(drawStart); @@ -122,7 +119,7 @@ DrawCallPerfApp::OnRunning() { Duration frameTime = Clock::LapTime(this->lastFrameTimePoint); Dbg::TextColor(1.0f, 1.0f, 0.0f, 1.0f); - Dbg::PrintF("\n %d draws\n\r upd=%.3fms\n\r applyRt=%.3fms\n\r draw=%.3fms\n\r frame=%.3fms\n\r" + Dbg::PrintF("\n\n\n\n\n %d draws\n\r upd=%.3fms\n\r applyRt=%.3fms\n\r draw=%.3fms\n\r frame=%.3fms\n\r" " LMB/tap: toggle particle update", this->curNumParticles, updTime.AsMilliSeconds(), @@ -140,8 +137,9 @@ void DrawCallPerfApp::updateCamera() { float angle = this->frameCount * 0.01f; glm::vec3 pos(glm::sin(angle) * 10.0f, 2.5f, glm::cos(angle) * 10.0f); - this->view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - this->perFrameParams.mvp = this->proj * this->view * this->model; + glm::mat4 view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + this->perFrameParams.mvp = proj * view; } //------------------------------------------------------------------------------ diff --git a/code/Samples/DrawCallPerf/shaders.glsl b/code/Samples/DrawCallPerf/shaders.glsl index a9010df25..8e16c8de1 100644 --- a/code/Samples/DrawCallPerf/shaders.glsl +++ b/code/Samples/DrawCallPerf/shaders.glsl @@ -9,12 +9,12 @@ uniform perFrameParams { uniform perParticleParams { vec4 translate; }; -in vec4 position; -in vec4 color0; +in vec4 in_pos; +in vec4 in_color; out vec4 color; void main() { - gl_Position = mvp * (position + translate); - color = color0; + gl_Position = mvp * (in_pos + translate); + color = in_color; } @end diff --git a/code/Samples/FullscreenQuad/CMakeLists.txt b/code/Samples/FullscreenQuad/CMakeLists.txt index 5cd3f52e3..94e7895c3 100644 --- a/code/Samples/FullscreenQuad/CMakeLists.txt +++ b/code/Samples/FullscreenQuad/CMakeLists.txt @@ -2,6 +2,6 @@ fips_begin_app(FullscreenQuad windowed) fips_vs_warning_level(3) fips_files(FullscreenQuad.cc) oryol_shader(shaders.glsl) - fips_deps(Gfx) + fips_deps(Gfx Assets) oryol_add_web_sample(FullscreenQuad "Fullscreen quad with some distance field ray-marching" "emscripten" FullscreenQuad.jpg "FullscreenQuad/FullscreenQuad.cc") fips_end_app() diff --git a/code/Samples/FullscreenQuad/FullscreenQuad.cc b/code/Samples/FullscreenQuad/FullscreenQuad.cc index 15a9735c2..b4d8dd884 100644 --- a/code/Samples/FullscreenQuad/FullscreenQuad.cc +++ b/code/Samples/FullscreenQuad/FullscreenQuad.cc @@ -4,6 +4,7 @@ #include "Pre.h" #include "Core/Main.h" #include "Gfx/Gfx.h" +#include "Assets/Gfx/FullscreenQuadBuilder.h" #include "shaders.h" using namespace Oryol; @@ -14,7 +15,8 @@ class FullscreenQuadApp : public App { AppState::Code OnInit(); AppState::Code OnCleanup(); - DrawState drawState; + Id pip; + Bindings bind; Shader::params params; }; OryolMain(FullscreenQuadApp); @@ -22,12 +24,15 @@ OryolMain(FullscreenQuadApp); //------------------------------------------------------------------------------ AppState::Code FullscreenQuadApp::OnInit() { - Gfx::Setup(GfxSetup::Window(600, 600, "Oryol Fullscreen Quad Sample")); - auto quadSetup = MeshSetup::FullScreenQuad(); - this->drawState.Mesh[0] = Gfx::CreateResource(quadSetup); - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(quadSetup.Layout, shd); - this->drawState.Pipeline = Gfx::CreateResource(ps); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(600) + .SetTitle("Oryol Fullscreen Quad Sample") + .SetHtmlTrackElementSize(true)); + auto fsq = FullscreenQuadBuilder().Build(); + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(fsq.VertexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(fsq.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc()))); this->params.time = 0.0f; return App::OnInit(); } @@ -38,9 +43,10 @@ FullscreenQuadApp::OnRunning() { // render one frame this->params.time += 1.0f / 60.0f; Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(0, 4); Gfx::EndPass(); Gfx::CommitFrame(); diff --git a/code/Samples/GPUParticles/GPUParticles.cc b/code/Samples/GPUParticles/GPUParticles.cc index 320deb573..4017c6da6 100644 --- a/code/Samples/GPUParticles/GPUParticles.cc +++ b/code/Samples/GPUParticles/GPUParticles.cc @@ -33,13 +33,14 @@ class GPUParticlesApp : public App { Id texture; Id pass; } particleBuffer[NumParticleBuffers]; - DrawState initParticles; - DrawState updParticles; - DrawState drawParticles; - - glm::mat4 view; - glm::mat4 proj; - glm::mat4 model; + Id initPipeline; + Id updPipeline; + Id drawPipeline; + Bindings initBind; + Bindings updBind; + Bindings drawBind; + + PrimitiveGroup shapePrimGroup; int frameCount = 0; TimePoint lastFrameTimePoint; int curNumParticles = 0; @@ -54,7 +55,11 @@ OryolMain(GPUParticlesApp); AppState::Code GPUParticlesApp::OnInit() { // setup rendering system - Gfx::Setup(GfxSetup::Window(800, 500, "Oryol GPU Particles Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(500) + .SetTitle("Oryol GPU Particles Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); // check required extensions @@ -77,70 +82,72 @@ GPUParticlesApp::OnInit() { // - 1 particle-rendering draw state // the 2 ping/pong particle state textures and render passes - auto particleTextureSetup = TextureSetup::RenderTarget2D(ParticleBufferWidth, ParticleBufferHeight, PixelFormat::RGBA32F); - particleTextureSetup.Sampler.MinFilter = TextureFilterMode::Nearest; - particleTextureSetup.Sampler.MagFilter = TextureFilterMode::Nearest; for (int i = 0; i < 2; i++) { - this->particleBuffer[i].texture = Gfx::CreateResource(particleTextureSetup); - auto particlePassSetup = PassSetup::From(this->particleBuffer[i].texture); - particlePassSetup.DefaultAction.DontCareColor(0); - this->particleBuffer[i].pass = Gfx::CreateResource(particlePassSetup); + this->particleBuffer[i].texture = Gfx::CreateTexture(TextureDesc() + .SetRenderTarget(true) + .SetWidth(ParticleBufferWidth) + .SetHeight(ParticleBufferHeight) + .SetFormat(PixelFormat::RGBA32F) + .SetMinFilter(TextureFilterMode::Nearest) + .SetMagFilter(TextureFilterMode::Nearest)); + this->particleBuffer[i].pass = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, this->particleBuffer[i].texture)); } // a fullscreen mesh for the particle init- and update-shaders - auto quadSetup = MeshSetup::FullScreenQuad(Gfx::QueryFeature(GfxFeature::OriginTopLeft)); - Id quadMesh = Gfx::CreateResource(quadSetup); - this->initParticles.Mesh[0] = quadMesh; - this->updParticles.Mesh[0] = quadMesh; + const float quadVertices[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + Id quadVbuf = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(quadVertices)) + .SetContent(quadVertices)); + this->initBind.VertexBuffers[0] = quadVbuf; + this->updBind.VertexBuffers[0] = quadVbuf; // particle initialization and update resources - Id initShader = Gfx::CreateResource(InitShader::Setup()); - Id updShader = Gfx::CreateResource(UpdateShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(quadSetup.Layout, initShader); - ps.BlendState.ColorFormat = particleTextureSetup.ColorFormat; - ps.BlendState.DepthFormat = particleTextureSetup.DepthFormat; - this->initParticles.Pipeline = Gfx::CreateResource(ps); - ps.Shader = updShader; - ps.RasterizerState.ScissorTestEnabled = true; - this->updParticles.Pipeline = Gfx::CreateResource(ps); + PipelineDesc particlePipDesc = PipelineDesc() + .SetLayout(0, { { "in_pos", VertexFormat::Float2 } }) + .SetPrimitiveType(PrimitiveType::TriangleStrip) + .SetColorFormat(PixelFormat::RGBA32F) + .SetDepthFormat(PixelFormat::None); + this->initPipeline = Gfx::CreatePipeline(PipelineDesc(particlePipDesc) + .SetShader(Gfx::CreateShader(InitShader::Desc()))); + this->updPipeline = Gfx::CreatePipeline(PipelineDesc(particlePipDesc) + .SetShader(Gfx::CreateShader(UpdateShader::Desc()))); // the static geometry of a single particle is at mesh slot 0 const glm::mat4 rot90 = glm::rotate(glm::mat4(), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - ShapeBuilder shapeBuilder; - shapeBuilder.RandomColors = true; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } - }; - shapeBuilder.Transform(rot90).Sphere(0.05f, 3, 2); - this->drawParticles.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - - // a instancing vertex buffer with the particleIds at mesh slot 1 - const int particleIdSize = MaxNumParticles * sizeof(float); - float* particleIdData = (float*) Memory::Alloc(particleIdSize); - for (int i = 0; i < MaxNumParticles; i++) { - particleIdData[i] = (float) i; + auto shape = ShapeBuilder() + .RandomColors(true) + .Positions("in_pos", VertexFormat::Float3) + .Colors("in_color", VertexFormat::Float4) + .Transform(rot90) + .Sphere(0.05f, 3, 2) + .Build(); + this->shapePrimGroup = shape.PrimitiveGroups[0]; + this->drawBind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->drawBind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + + // a instancing vertex buffer with the particleIds at vertex buffer slot 1 + { + const int particleIdSize = MaxNumParticles * sizeof(float); + float* particleIdData = (float*) Memory::Alloc(particleIdSize); + for (int i = 0; i < MaxNumParticles; i++) { + particleIdData[i] = (float) i; + } + this->drawBind.VertexBuffers[1] = Gfx::CreateBuffer(BufferDesc() + .SetSize(particleIdSize) + .SetContent(particleIdData)); + Memory::Free(particleIdData); } - auto particleIdSetup = MeshSetup::FromData(Usage::Immutable); - particleIdSetup.NumVertices = MaxNumParticles; - particleIdSetup.Layout.EnableInstancing().Add(VertexAttr::Instance0, VertexFormat::Float); - this->drawParticles.Mesh[1] = Gfx::CreateResource(particleIdSetup, particleIdData, particleIdSize); - Memory::Free(particleIdData); - - // particle rendering texture blocks and draw state - Id drawShader = Gfx::CreateResource(DrawShader::Setup()); - ps = PipelineSetup::FromShader(drawShader); - ps.Layouts[0] = shapeBuilder.Layout; - ps.Layouts[1] = particleIdSetup.Layout; - ps.RasterizerState.CullFaceEnabled = true; - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::Less; - this->drawParticles.Pipeline = Gfx::CreateResource(ps); - - // the static projection matrix - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 50.0f); + + // ...and the pipeline object for instanced particle rendering + this->drawPipeline = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(DrawShader::Desc())) + .SetLayout(1, VertexLayout() + .EnableInstancing() + .Add("in_particleId", VertexFormat::Float)) + .SetCullFaceEnabled(true) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual)); // setup initial shader params const glm::vec2 bufferDims(ParticleBufferWidth, ParticleBufferHeight); @@ -150,10 +157,11 @@ GPUParticlesApp::OnInit() { // 'draw' the initial particle state (positions at origin, pseudo-random velocity) for (int i = 0; i < 2; i++) { - Gfx::BeginPass(this->particleBuffer[0].pass); - Gfx::ApplyDrawState(this->initParticles); - Gfx::ApplyUniformBlock(this->initFSParams); - Gfx::Draw(); + Gfx::BeginPass(this->particleBuffer[0].pass, PassAction().DontCare()); + Gfx::ApplyPipeline(this->initPipeline); + Gfx::ApplyBindings(this->initBind); + Gfx::ApplyUniforms(this->initFSParams); + Gfx::Draw(0, 4); Gfx::EndPass(); } @@ -184,29 +192,31 @@ GPUParticlesApp::OnRunning() { // - we use a scissor rect around the currently active particles to make this update // a bit more efficient const int scissorHeight = (this->curNumParticles / NumParticlesX) + 1; - this->updParticles.FSTexture[UpdateShader::prevState] = this->particleBuffer[readIndex].texture; + this->updBind.FSTexture[UpdateShader::prevState] = this->particleBuffer[readIndex].texture; this->updFSParams.numParticles = (float) this->curNumParticles; - Gfx::BeginPass(this->particleBuffer[drawIndex].pass); + Gfx::BeginPass(this->particleBuffer[drawIndex].pass, PassAction().DontCare()); Gfx::ApplyScissorRect(0, 0, ParticleBufferWidth, scissorHeight, Gfx::QueryFeature(GfxFeature::OriginTopLeft)); - Gfx::ApplyDrawState(this->updParticles); - Gfx::ApplyUniformBlock(this->updFSParams); - Gfx::Draw(); + Gfx::ApplyPipeline(this->updPipeline); + Gfx::ApplyBindings(this->updBind); + Gfx::ApplyUniforms(this->updFSParams); + Gfx::Draw(0, 4); Gfx::EndPass(); // now the actual particle shape rendering: // - the new particle state texture is sampled in the vertex shader to obtain particle positions // - draw 'curNumParticles' instances of the basic particle shape through hardware-instancing - this->drawParticles.VSTexture[DrawShader::particleTex] = this->particleBuffer[drawIndex].texture; + this->drawBind.VSTexture[DrawShader::particleTex] = this->particleBuffer[drawIndex].texture; Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawParticles); - Gfx::ApplyUniformBlock(this->drawVSParams); - Gfx::Draw(0, this->curNumParticles); + Gfx::ApplyPipeline(this->drawPipeline); + Gfx::ApplyBindings(this->drawBind); + Gfx::ApplyUniforms(this->drawVSParams); + Gfx::Draw(this->shapePrimGroup, this->curNumParticles); Dbg::DrawTextBuffer(); Gfx::EndPass(); Gfx::CommitFrame(); Duration frameTime = Clock::LapTime(this->lastFrameTimePoint); - Dbg::PrintF("\n %d instances\n\r frame=%.3fms", this->curNumParticles, frameTime.AsMilliSeconds()); + Dbg::PrintF("\n\n\n\n\n %d instances\n\r frame=%.3fms", this->curNumParticles, frameTime.AsMilliSeconds()); // continue running or quit? return Gfx::QuitRequested() ? AppState::Cleanup : AppState::Running; @@ -217,8 +227,9 @@ void GPUParticlesApp::updateCamera() { float angle = this->frameCount * 0.01f; glm::vec3 pos(glm::sin(angle) * 10.0f, 2.5f, glm::cos(angle) * 10.0f); - this->view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - this->drawVSParams.mvp = this->proj * this->view * this->model; + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 50.0f); + glm::mat4 view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + this->drawVSParams.mvp = proj * view; } //------------------------------------------------------------------------------ diff --git a/code/Samples/GPUParticles/shaders.glsl b/code/Samples/GPUParticles/shaders.glsl index 71ebe4c45..384b68275 100644 --- a/code/Samples/GPUParticles/shaders.glsl +++ b/code/Samples/GPUParticles/shaders.glsl @@ -128,9 +128,9 @@ float snoise(vec2 v) // A generic fullscreen-quad vertex shader. // @vs fsqVS -in vec4 position; +in vec2 in_pos; void main() { - gl_Position = position; + gl_Position = vec4(in_pos * 2.0 - 1.0, 0.5, 1.0); } @end @@ -237,17 +237,15 @@ uniform vsParams { }; uniform sampler2D particleTex; -in vec4 position; -in vec4 color0; -in float instance0; +in vec4 in_pos; +in vec4 in_color; +in float in_particleId; out vec4 color; void main() { - float particleId = instance0; - vec2 posUv = posUvFromParticleId(particleId, bufDims); + vec2 posUv = posUvFromParticleId(in_particleId, bufDims); vec4 particlePos = vec4(texture(particleTex, posUv).xyz, 0.0); - - gl_Position = mvp * (position + particlePos); - color = color0; + gl_Position = mvp * (in_pos + particlePos); + color = in_color; } @end diff --git a/code/Samples/GamepadExplorer/GamepadExplorer.cc b/code/Samples/GamepadExplorer/GamepadExplorer.cc index 1e659becc..e1ae46887 100644 --- a/code/Samples/GamepadExplorer/GamepadExplorer.cc +++ b/code/Samples/GamepadExplorer/GamepadExplorer.cc @@ -49,7 +49,11 @@ const char* axisNames[GamepadAxis::NumAxes] = { //------------------------------------------------------------------------------ AppState::Code GamepadExplorerApp::OnInit() { - Gfx::Setup(GfxSetup::Window(800, 600, "Test Gamepads")); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetTitle("Test Gamepads") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); Input::Setup(); return App::OnInit(); @@ -61,7 +65,7 @@ GamepadExplorerApp::OnRunning() { Gfx::BeginPass(); // Gamepad0 Gamepad1 Gamepad2 Gamepad3 - Dbg::Print("\n\t\t"); + Dbg::Print("\n\n\n\n\n\t\t"); for (int i = 0; i < Input::MaxNumGamepads; i++) { if (Input::GamepadAttached(i)) { Dbg::TextColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -134,4 +138,4 @@ GamepadExplorerApp::OnCleanup() { Dbg::Discard(); Gfx::Discard(); return App::OnCleanup(); -} \ No newline at end of file +} diff --git a/code/Samples/IOQueueSample/CMakeLists.txt b/code/Samples/IOQueueSample/CMakeLists.txt index 80be1e059..a072bcc9b 100644 --- a/code/Samples/IOQueueSample/CMakeLists.txt +++ b/code/Samples/IOQueueSample/CMakeLists.txt @@ -2,5 +2,4 @@ fips_begin_app(IOQueueSample windowed) fips_vs_warning_level(3) fips_files(IOQueueSample.cc) fips_deps(IO HttpFS) - oryol_add_web_sample(IOQueueSample "Asynchronous file loading with IOQueue" "emscripten" none "IOQueueSample/IOQueueSample.cc") fips_end_app() diff --git a/code/Samples/IOQueueSample/IOQueueSample.cc b/code/Samples/IOQueueSample/IOQueueSample.cc index 64c0e3eff..74075af41 100644 --- a/code/Samples/IOQueueSample/IOQueueSample.cc +++ b/code/Samples/IOQueueSample/IOQueueSample.cc @@ -27,11 +27,10 @@ IOQueueApp::OnInit() { // setup the IO module, attach a HTTP filesystem and setup // a path assign (aka path alias) - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - ioSetup.Assigns.Add("res:", ORYOL_SAMPLE_URL); - IO::Setup(ioSetup); - + IO::Setup(IODesc() + .AddFileSystem("http", HTTPFileSystem::Creator()) + .AddAssign("res:", ORYOL_SAMPLE_URL)); + // now the important part: start loading files, and define // the success-callbacks as lambdas // diff --git a/code/Samples/InfiniteSpheres/InfiniteSpheres.cc b/code/Samples/InfiniteSpheres/InfiniteSpheres.cc index 61fd30208..c4b141e59 100644 --- a/code/Samples/InfiniteSpheres/InfiniteSpheres.cc +++ b/code/Samples/InfiniteSpheres/InfiniteSpheres.cc @@ -21,17 +21,18 @@ class InfiniteSpheresApp : public App { glm::mat4 computeModel(float rotX, float rotY, const glm::vec3& pos); glm::mat4 computeMVP(const glm::mat4& proj, const glm::mat4& model); - DrawState offscreenDrawState; - DrawState displayDrawState; + PrimitiveGroup primGroup; + Id offscreenPipeline; + Id displayPipeline; + Bindings offscreenBind; + Bindings displayBind; struct { Id texture; Id pass; } passInfo[2]; Shader::vsParams vsParams; - PassAction passAction = PassAction::Clear(glm::vec4(0.25f, 0.25f, 0.25f, 1.0f)); - glm::mat4 view; + PassAction passAction = PassAction().Clear(0.25f, 0.25f, 0.25f, 1.0f); glm::mat4 offscreenProj; - glm::mat4 displayProj; float angleX = 0.0f; float angleY = 0.0f; int frameIndex = 0; @@ -42,56 +43,71 @@ OryolMain(InfiniteSpheresApp); AppState::Code InfiniteSpheresApp::OnInit() { // setup rendering system - auto gfxSetup = GfxSetup::WindowMSAA4(800, 600, "Oryol Infinite Spheres Sample"); - Gfx::Setup(gfxSetup); - - // create 2 ping-pong offscreen render targets - auto rtSetup = TextureSetup::RenderTarget2D(512, 512, PixelFormat::RGBA8, PixelFormat::DEPTH); - rtSetup.Sampler.MinFilter = TextureFilterMode::Linear; - rtSetup.Sampler.MagFilter = TextureFilterMode::Linear; - rtSetup.Sampler.WrapU = TextureWrapMode::Repeat; - rtSetup.Sampler.WrapV = TextureWrapMode::Repeat; + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetSampleCount(4) + .SetTitle("Oryol Infinite Spheres Sample") + .SetHtmlTrackElementSize(true)); + + // create 2 ping-pong offscreen render targets, only need 1 depth buffer + const PixelFormat::Code rtColorFormat = PixelFormat::RGBA8; + const PixelFormat::Code rtDepthFormat = PixelFormat::DEPTH; + const int rtWidth = 512; + const int rtHeight = 512; + Id rtDepth = Gfx::CreateTexture(TextureDesc() + .SetRenderTarget(true) + .SetWidth(rtWidth) + .SetHeight(rtHeight) + .SetFormat(rtDepthFormat)); for (int i = 0; i < 2; i++) { - Id tex = Gfx::CreateResource(rtSetup); - this->passInfo[i].texture = tex; - auto rpSetup = PassSetup::From(tex, tex); - this->passInfo[i].pass = Gfx::CreateResource(rpSetup); + this->passInfo[i].texture = Gfx::CreateTexture(TextureDesc() + .SetRenderTarget(true) + .SetWidth(rtWidth) + .SetHeight(rtHeight) + .SetFormat(rtColorFormat) + .SetMinFilter(TextureFilterMode::Linear) + .SetMagFilter(TextureFilterMode::Linear) + .SetWrapU(TextureWrapMode::Repeat) + .SetWrapV(TextureWrapMode::Repeat)); + this->passInfo[i].pass = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, this->passInfo[i].texture) + .SetDepthStencilAttachment(rtDepth)); } // create a sphere shape mesh - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Sphere(0.75f, 72, 40); - Id sphere = Gfx::CreateResource(shapeBuilder.Build()); - this->offscreenDrawState.Mesh[0] = sphere; - this->displayDrawState.Mesh[0] = sphere; + auto sphere = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Byte4N) + .TexCoords("in_uv", VertexFormat::Float2) + .Sphere(0.75f, 72, 40) + .Build(); + this->primGroup = sphere.PrimitiveGroups[0]; + Id vbuf = Gfx::CreateBuffer(sphere.VertexBufferDesc); + Id ibuf = Gfx::CreateBuffer(sphere.IndexBufferDesc); + this->offscreenBind.VertexBuffers[0] = vbuf; + this->offscreenBind.IndexBuffer = ibuf; + this->displayBind.VertexBuffers[0] = vbuf; + this->displayBind.IndexBuffer = ibuf; // create shader which is used for both offscreen- and display-rendering - Id shd = Gfx::CreateResource(Shader::Setup()); + Id shd = Gfx::CreateShader(Shader::Desc()); // create draw state for rendering into default render target - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->displayDrawState.Pipeline = Gfx::CreateResource(ps); - - // create draw state for rendering into offscreen render target - ps.BlendState.ColorFormat = rtSetup.ColorFormat; - ps.BlendState.DepthFormat = rtSetup.DepthFormat; - ps.RasterizerState.SampleCount = 1; - this->offscreenDrawState.Pipeline = Gfx::CreateResource(ps); + this->displayPipeline = Gfx::CreatePipeline(PipelineDesc(sphere.PipelineDesc) + .SetShader(shd) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->offscreenPipeline = Gfx::CreatePipeline(PipelineDesc(sphere.PipelineDesc) + .SetShader(shd) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetColorFormat(rtColorFormat) + .SetDepthFormat(rtDepthFormat)); // setup static transform matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; this->offscreenProj = glm::perspective(glm::radians(45.0f), 1.0f, 0.01f, 20.0f); - this->displayProj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 20.0f); - this->view = glm::mat4(); return App::OnInit(); } @@ -112,20 +128,22 @@ InfiniteSpheresApp::OnRunning() { glm::mat4 model = this->computeModel(this->angleX, this->angleY, glm::vec3(0.0f, 0.0f, -2.0f)); this->vsParams.mvp = this->computeMVP(this->offscreenProj, model); Gfx::BeginPass(this->passInfo[index0].pass); - this->offscreenDrawState.FSTexture[Shader::tex] = this->passInfo[index1].texture; - Gfx::ApplyDrawState(this->offscreenDrawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); + Gfx::ApplyPipeline(this->offscreenPipeline); + Gfx::ApplyBindings(this->offscreenBind.SetFSTexture(Shader::tex, this->passInfo[index1].texture)); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); Gfx::EndPass(); // ...and again to display model = this->computeModel(-this->angleX, -this->angleY, glm::vec3(0.0f, 0.0f, -2.0f)); - this->vsParams.mvp = this->computeMVP(this->displayProj, model); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 20.0f); + this->vsParams.mvp = this->computeMVP(proj, model); Gfx::BeginPass(this->passAction); - this->displayDrawState.FSTexture[Shader::tex] = this->passInfo[index0].texture; - Gfx::ApplyDrawState(this->displayDrawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); + this->displayBind.FSTexture[Shader::tex] = this->passInfo[index0].texture; + Gfx::ApplyPipeline(this->displayPipeline); + Gfx::ApplyBindings(this->displayBind.SetFSTexture(Shader::tex, this->passInfo[index0].texture)); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); Gfx::EndPass(); Gfx::CommitFrame(); @@ -153,6 +171,6 @@ InfiniteSpheresApp::computeModel(float rotX, float rotY, const glm::vec3& pos) { //------------------------------------------------------------------------------ glm::mat4 InfiniteSpheresApp::computeMVP(const glm::mat4& proj, const glm::mat4& modelTform) { - return proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/InfiniteSpheres/shaders.glsl b/code/Samples/InfiniteSpheres/shaders.glsl index 71596ebaf..e3a6b0a1d 100644 --- a/code/Samples/InfiniteSpheres/shaders.glsl +++ b/code/Samples/InfiniteSpheres/shaders.glsl @@ -7,15 +7,15 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; -in vec2 texcoord0; +in vec4 in_pos; +in vec4 in_normal; +in vec2 in_uv; out vec4 nrm; out vec2 uv; void main() { - gl_Position = mvp * position; - nrm = normal; - uv = texcoord0; + gl_Position = mvp * in_pos; + nrm = in_normal; + uv = in_uv; } @end diff --git a/code/Samples/Instancing/Instancing.cc b/code/Samples/Instancing/Instancing.cc index 30bb7acb5..1fd0f12b6 100644 --- a/code/Samples/Instancing/Instancing.cc +++ b/code/Samples/Instancing/Instancing.cc @@ -25,14 +25,9 @@ class InstancingApp : public App { void emitParticles(); void updateParticles(); - // the static geometry is at mesh slot 0, and the instance data at slot 1 - static const int geomMeshSlot = 0; - static const int instMeshSlot = 1; - - DrawState drawState; - glm::mat4 view; - glm::mat4 proj; - glm::mat4 model; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; Shader::vsParams vsParams; bool updateEnabled = true; int frameCount = 0; @@ -49,7 +44,11 @@ OryolMain(InstancingApp); AppState::Code InstancingApp::OnInit() { // setup rendering system - Gfx::Setup(GfxSetup::Window(800, 500, "Oryol Instancing Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(500) + .SetTitle("Oryol Instancing Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); Input::Setup(); @@ -60,37 +59,31 @@ InstancingApp::OnInit() { // create static mesh at mesh slot 0 const glm::mat4 rot90 = glm::rotate(glm::mat4(), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); - ShapeBuilder shapeBuilder; - shapeBuilder.RandomColors = true; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } - }; - shapeBuilder.Transform(rot90).Sphere(0.05f, 3, 2); - auto shapeBuilderResult = shapeBuilder.Build(); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilderResult); + auto shape = ShapeBuilder() + .RandomColors(true) + .Positions("in_pos", VertexFormat::Float3) + .Colors("in_color", VertexFormat::Float4) + .Transform(rot90) + .Sphere(0.05f, 3, 2) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); - // create dynamic instance data mesh at mesh slot 1 - auto instMeshSetup = MeshSetup::Empty(MaxNumParticles, Usage::Stream); - instMeshSetup.Layout - .EnableInstancing() - .Add(VertexAttr::Instance0, VertexFormat::Float4); - this->drawState.Mesh[1] = Gfx::CreateResource(instMeshSetup); + // create dynamic instance data vertex buffer on slot 1 + this->bind.VertexBuffers[1] = Gfx::CreateBuffer(BufferDesc() + .SetSize(MaxNumParticles * VertexFormat::ByteSize(VertexFormat::Float4)) + .SetUsage(Usage::Stream)); - // setup draw state for instanced rendering - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromShader(shd); - ps.Layouts[0] = shapeBuilder.Layout; - ps.Layouts[1] = instMeshSetup.Layout; - ps.RasterizerState.CullFaceEnabled = true; - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - this->drawState.Pipeline = Gfx::CreateResource(ps); - - // setup projection and view matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); + // setup pipeline state for instanced rendering + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetLayout(1, VertexLayout() + .EnableInstancing() + .Add("in_instpos", VertexFormat::Float4)) + .SetCullFaceEnabled(true) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual)); return App::OnInit(); } @@ -111,16 +104,17 @@ InstancingApp::OnRunning() { updTime = Clock::Since(updStart); TimePoint bufStart = Clock::Now(); - Gfx::UpdateVertices(this->drawState.Mesh[instMeshSlot], this->positions, this->curNumParticles * sizeof(glm::vec4)); + Gfx::UpdateBuffer(this->bind.VertexBuffers[1], this->positions, this->curNumParticles * sizeof(glm::vec4)); bufTime = Clock::Since(bufStart); } // render block TimePoint drawStart = Clock::Now(); Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(0, this->curNumParticles); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup, this->curNumParticles); drawTime = Clock::Since(drawStart); Dbg::DrawTextBuffer(); @@ -134,7 +128,7 @@ InstancingApp::OnRunning() { } Duration frameTime = Clock::LapTime(this->lastFrameTimePoint); - Dbg::PrintF("\n %d instances\n\r upd=%.3fms\n\r bufUpd=%.3fms\n\r draw=%.3fms\n\r frame=%.3fms\n\r" + Dbg::PrintF("\n\n\n\n\n %d instances\n\r upd=%.3fms\n\r bufUpd=%.3fms\n\r draw=%.3fms\n\r frame=%.3fms\n\r" " LMB/Tap: toggle particle updates", this->curNumParticles, updTime.AsMilliSeconds(), @@ -150,8 +144,9 @@ void InstancingApp::updateCamera() { float angle = this->frameCount * 0.01f; glm::vec3 pos(glm::sin(angle) * 10.0f, 2.5f, glm::cos(angle) * 10.0f); - this->view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - this->vsParams.mvp = this->proj * this->view * this->model; + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + glm::mat4 view = glm::lookAt(pos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + this->vsParams.mvp = proj * view; } //------------------------------------------------------------------------------ diff --git a/code/Samples/Instancing/shaders.glsl b/code/Samples/Instancing/shaders.glsl index d3f594f81..5347b7d69 100644 --- a/code/Samples/Instancing/shaders.glsl +++ b/code/Samples/Instancing/shaders.glsl @@ -6,13 +6,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 color0; -in vec4 instance0; +in vec4 in_pos; +in vec4 in_color; +in vec4 in_instpos; out vec4 color; void main() { - gl_Position = mvp * (position + instance0); - color = color0; + gl_Position = mvp * (in_pos + in_instpos); + color = in_color; } @end diff --git a/code/Samples/MultipleRenderTarget/MultipleRenderTarget.cc b/code/Samples/MultipleRenderTarget/MultipleRenderTarget.cc index 3455f1c4a..4b47be106 100644 --- a/code/Samples/MultipleRenderTarget/MultipleRenderTarget.cc +++ b/code/Samples/MultipleRenderTarget/MultipleRenderTarget.cc @@ -18,7 +18,7 @@ class MultipleRenderTargetApp : public App { AppState::Code OnRunning(); AppState::Code OnCleanup(); - glm::mat4 computeMVP(float angleX, float angleY, const glm::vec3& pos); + glm::mat4 computeMVP(const glm::mat4& proj, float angleX, float angleY, const glm::vec3& pos); AppState::Code notSupported(); const int DisplayWidth = 640; @@ -26,18 +26,24 @@ class MultipleRenderTargetApp : public App { const int OffscreenWidth = 200; const int OffscreenHeight = 200; + PrimitiveGroup cubePrimGroup; + PrimitiveGroup planePrimGroup; Id mrtPass; - DrawState rt0DrawState; - DrawState rt1DrawState; - DrawState rt2DrawState; - - DrawState cubeDrawState; + PassAction mrtPassAction; + Id rtPipeline; + Bindings rt0Bind; + Bindings rt1Bind; + Bindings rt2Bind; + + Id cubePipeline; + Bindings cubeBind; OffscreenShader::vsParams cubeParams; - DrawState displayDrawState; + Id displayPipeline; + Bindings displayBind; DisplayShader::vsParams displayParams; - glm::mat4 proj; + glm::mat4 offscreenProj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -46,9 +52,11 @@ OryolMain(MultipleRenderTargetApp); //------------------------------------------------------------------------------ AppState::Code MultipleRenderTargetApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(DisplayWidth, DisplayHeight, "Oryol MRT Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(DisplayWidth) + .SetHeight(DisplayHeight) + .SetSampleCount(4) + .SetTitle("Oryol MRT Sample")); Dbg::Setup(); // if rendering backend doesn't support MRT, drop out now @@ -57,86 +65,100 @@ MultipleRenderTargetApp::OnInit() { return App::OnInit(); } - // create 3 rendertarget textures, the first one with a DepthStencil - // surface, note that the render target textures use MSAA anti-aliasing - auto rtSetup = TextureSetup::RenderTarget2D(OffscreenWidth, OffscreenHeight, PixelFormat::RGBA8, PixelFormat::DEPTHSTENCIL); - rtSetup.SampleCount = 4; - rtSetup.Sampler.MinFilter = TextureFilterMode::Linear; - rtSetup.Sampler.MagFilter = TextureFilterMode::Linear; - Id rt0 = Gfx::CreateResource(rtSetup); - rtSetup.DepthFormat = PixelFormat::None; - Id rt1 = Gfx::CreateResource(rtSetup); - Id rt2 = Gfx::CreateResource(rtSetup); - - // create a render pass which uses the 3 texture we just created - // as color attachments, and the first texture as depth-stencil buffer - // FIXME: depth-stencil should be its own texture! - auto passSetup = PassSetup::From({ rt0, rt1, rt2 }, rt0); - passSetup.DefaultAction + // create 3 color rendertarget textures, and a matching + // depth-stencil render target texture + const PixelFormat::Code rtColorFormat = PixelFormat::RGBA8; + const PixelFormat::Code rtDepthFormat = PixelFormat::DEPTHSTENCIL; + const int rtSampleCount = 4; + auto rtDesc = TextureDesc() + .SetType(TextureType::Texture2D) + .SetRenderTarget(true) + .SetWidth(OffscreenWidth) + .SetHeight(OffscreenHeight) + .SetFormat(rtColorFormat) + .SetMinFilter(TextureFilterMode::Linear) + .SetMagFilter(TextureFilterMode::Linear) + .SetSampleCount(rtSampleCount); + Id rtColor0 = Gfx::CreateTexture(rtDesc); + Id rtColor1 = Gfx::CreateTexture(rtDesc); + Id rtColor2 = Gfx::CreateTexture(rtDesc); + Id rtDepth = Gfx::CreateTexture(TextureDesc(rtDesc).SetFormat(rtDepthFormat)); + + // create a render pass with the 3 color- and 1 depth-attachment + this->mrtPass = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, rtColor0) + .SetColorAttachment(1, rtColor1) + .SetColorAttachment(2, rtColor2) + .SetDepthStencilAttachment(rtDepth)); + + // a pass-action to clear the multiple-render-target + this->mrtPassAction .ClearColor(0, glm::vec4(0.25f, 0.0f, 0.0f, 1.0f)) .ClearColor(1, glm::vec4(0.0f, 0.25f, 0.0f, 1.0f)) .ClearColor(2, glm::vec4(0.0f, 0.0f, 0.25f, 1.0f)); - this->mrtPass = Gfx::CreateResource(passSetup); // create a mesh with 2 shapes, a box and a plane - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::UByte4N }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1); - shapeBuilder.Plane(1.0f, 1.0f, 1); - Id cubeMesh = Gfx::CreateResource(shapeBuilder.Build()); + auto shapes = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::UByte4N) + .TexCoords("in_uv", VertexFormat::Float2) + .Box(1.0f, 1.0f, 1.0f, 1) + .Plane(1.0f, 1.0f, 1) + .Build(); + this->cubePrimGroup = shapes.PrimitiveGroups[0]; + this->planePrimGroup = shapes.PrimitiveGroups[1]; + Id shapesVertexBuffer = Gfx::CreateBuffer(shapes.VertexBufferDesc); + Id shapesIndexBuffer = Gfx::CreateBuffer(shapes.IndexBufferDesc); // create a draw state to render a cube into the // offscreen render targets (this is where the MRT rendering happens) - Id shd = Gfx::CreateResource(OffscreenShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.CullFaceEnabled = true; - ps.RasterizerState.SampleCount = rtSetup.SampleCount; - ps.BlendState.ColorFormat = rtSetup.ColorFormat; - ps.BlendState.MRTCount = 3; - this->cubeDrawState.Pipeline = Gfx::CreateResource(ps); - this->cubeDrawState.Mesh[0] = cubeMesh; - - // create a quad-mesh for displaying the 3 render target - // textures on screen - auto quadMeshSetup = MeshSetup::FullScreenQuad(); - Id quadMesh = Gfx::CreateResource(quadMeshSetup); - Id quadShd = Gfx::CreateResource(QuadShader::Setup()); - ps = PipelineSetup::FromLayoutAndShader(quadMeshSetup.Layout, quadShd); - ps.DepthStencilState.DepthWriteEnabled = false; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::Always; - ps.RasterizerState.CullFaceEnabled = false; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->rt0DrawState.Pipeline = Gfx::CreateResource(ps); - this->rt0DrawState.Mesh[0] = quadMesh; - this->rt0DrawState.FSTexture[QuadShader::tex] = rt0; - this->rt1DrawState.Pipeline = this->rt0DrawState.Pipeline; - this->rt1DrawState.Mesh[0] = quadMesh; - this->rt1DrawState.FSTexture[QuadShader::tex] = rt1; - this->rt2DrawState.Pipeline = this->rt0DrawState.Pipeline; - this->rt2DrawState.Mesh[0] = quadMesh; - this->rt2DrawState.FSTexture[QuadShader::tex] = rt2; + this->cubePipeline = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(OffscreenShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(true) + .SetColorFormat(rtColorFormat) + .SetDepthFormat(rtDepthFormat) + .SetSampleCount(rtSampleCount) + .SetMRTCount(3)); + this->cubeBind.VertexBuffers[0] = shapesVertexBuffer; + this->cubeBind.IndexBuffer = shapesIndexBuffer; + + // create a quad-mesh for displaying the 3 render target textures on screen + const float quadVertices[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + Id quadVertexBuffer = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(quadVertices)) + .SetContent(quadVertices)); + this->rtPipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(QuadShader::Desc())) + .SetLayout(0, { { "in_pos", VertexFormat::Float2 } }) + .SetPrimitiveType(PrimitiveType::TriangleStrip) + .SetDepthWriteEnabled(false) + .SetDepthCmpFunc(CompareFunc::Always) + .SetCullFaceEnabled(false) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->rt0Bind.VertexBuffers[0] = quadVertexBuffer; + this->rt0Bind.FSTexture[QuadShader::tex] = rtColor0; + this->rt1Bind.VertexBuffers[0] = quadVertexBuffer; + this->rt1Bind.FSTexture[QuadShader::tex] = rtColor1; + this->rt2Bind.VertexBuffers[0] = quadVertexBuffer; + this->rt2Bind.FSTexture[QuadShader::tex] = rtColor2; // and finally create a draw state to render a plane to the // main display which samples the 3 offscreen render targets - Id displayShd = Gfx::CreateResource(DisplayShader::Setup()); - ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, displayShd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.CullFaceEnabled = false; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->displayDrawState.Pipeline = Gfx::CreateResource(ps); - this->displayDrawState.Mesh[0] = cubeMesh; - this->displayDrawState.FSTexture[DisplayShader::redTex] = rt0; - this->displayDrawState.FSTexture[DisplayShader::greenTex] = rt1; - this->displayDrawState.FSTexture[DisplayShader::blueTex] = rt2; - - this->proj = glm::perspectiveFov(glm::radians(45.0f), float(OffscreenWidth), float(OffscreenHeight), 0.01f, 100.0f); + this->displayPipeline = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(DisplayShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(false) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->displayBind.VertexBuffers[0] = shapesVertexBuffer; + this->displayBind.IndexBuffer = shapesIndexBuffer; + this->displayBind.FSTexture[DisplayShader::redTex] = rtColor0; + this->displayBind.FSTexture[DisplayShader::greenTex] = rtColor1; + this->displayBind.FSTexture[DisplayShader::blueTex] = rtColor2; + + this->offscreenProj = glm::perspectiveFov(glm::radians(45.0f), float(OffscreenWidth), float(OffscreenHeight), 0.01f, 100.0f); return App::OnInit(); } @@ -153,36 +175,40 @@ MultipleRenderTargetApp::OnRunning() { // compute all the vertex shader uniforms this->angleY += 0.01f; this->angleX += 0.02f; - this->cubeParams.mvp = this->computeMVP(this->angleX, this->angleY, glm::vec3(0, 0, -3)); - this->displayParams.mvp = this->computeMVP(this->angleX * 0.5f, this->angleY * 0.5f, glm::vec3(0.0f, 0.55f, -3.0f)); + this->cubeParams.mvp = this->computeMVP(this->offscreenProj, this->angleX, this->angleY, glm::vec3(0, 0, -3)); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + this->displayParams.mvp = this->computeMVP(proj, this->angleX * 0.5f, this->angleY * 0.5f, glm::vec3(0.0f, 0.55f, -3.0f)); this->displayParams.offsets = glm::sin(glm::vec2(this->angleX, this->angleY)) * 0.1f; // render the cube into the 3 MRT render targets using a single draw call, // the fragment shader writes 3 colors, one for each color attachment - Gfx::BeginPass(this->mrtPass); - Gfx::ApplyDrawState(this->cubeDrawState); - Gfx::ApplyUniformBlock(this->cubeParams); - Gfx::Draw(0); + Gfx::BeginPass(this->mrtPass, this->mrtPassAction); + Gfx::ApplyPipeline(this->cubePipeline); + Gfx::ApplyBindings(this->cubeBind); + Gfx::ApplyUniforms(this->cubeParams); + Gfx::Draw(this->cubePrimGroup); Gfx::EndPass(); // debug-visualize the 3 offscreen render targets at the bottom of the screen - Gfx::BeginPass(); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.5f, 0.5f, 1.0f)); + Gfx::ApplyPipeline(this->rtPipeline); Gfx::ApplyViewPort(0, 0, 200, 200); - Gfx::ApplyDrawState(this->rt0DrawState); - Gfx::Draw(); + Gfx::ApplyBindings(this->rt0Bind); + Gfx::Draw(0, 4); Gfx::ApplyViewPort(200, 0, 200, 200); - Gfx::ApplyDrawState(this->rt1DrawState); - Gfx::Draw(); + Gfx::ApplyBindings(this->rt1Bind); + Gfx::Draw(0, 4); Gfx::ApplyViewPort(400, 0, 200, 200); - Gfx::ApplyDrawState(this->rt2DrawState); - Gfx::Draw(); + Gfx::ApplyBindings(this->rt2Bind); + Gfx::Draw(0, 4); // render the final plane which samples from all 3 offscreen rendertarget textures - const auto& rpAttrs = Gfx::PassAttrs(); - Gfx::ApplyViewPort(0, 0, rpAttrs.FramebufferWidth, rpAttrs.FramebufferHeight); - Gfx::ApplyDrawState(this->displayDrawState); - Gfx::ApplyUniformBlock(this->displayParams); - Gfx::Draw(1); + const auto& rpAttrs = Gfx::DisplayAttrs(); + Gfx::ApplyViewPort(0, 0, rpAttrs.Width, rpAttrs.Height); + Gfx::ApplyPipeline(this->displayPipeline); + Gfx::ApplyBindings(this->displayBind); + Gfx::ApplyUniforms(this->displayParams); + Gfx::Draw(this->planePrimGroup); Gfx::EndPass(); Gfx::CommitFrame(); @@ -199,11 +225,11 @@ MultipleRenderTargetApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 -MultipleRenderTargetApp::computeMVP(float ax, float ay, const glm::vec3& pos) { +MultipleRenderTargetApp::computeMVP(const glm::mat4& proj, float ax, float ay, const glm::vec3& pos) { glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, ax, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, ay, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * modelTform; + return proj * modelTform; } //------------------------------------------------------------------------------ @@ -214,9 +240,9 @@ MultipleRenderTargetApp::notSupported() { #else const char* msg = "This demo needs MultipleRenderTarget\n"; #endif - uint8_t x = uint8_t((Gfx::DisplayAttrs().FramebufferWidth/16 - std::strlen(msg))/2); - uint8_t y = uint8_t(Gfx::DisplayAttrs().FramebufferHeight/16/2); - Gfx::BeginPass(PassAction::Clear(glm::vec4(0.5f, 0.0f, 0.0f, 1.0f))); + uint8_t x = uint8_t((Gfx::DisplayAttrs().Width/16 - std::strlen(msg))/2); + uint8_t y = uint8_t((Gfx::DisplayAttrs().Height/16)/2); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.0f, 0.0f, 1.0f)); Dbg::TextScale(2.0f, 2.0f); Dbg::CursorPos(x, y); Dbg::Print(msg); diff --git a/code/Samples/MultipleRenderTarget/shaders.glsl b/code/Samples/MultipleRenderTarget/shaders.glsl index a9cfb0bb7..aae8d7414 100644 --- a/code/Samples/MultipleRenderTarget/shaders.glsl +++ b/code/Samples/MultipleRenderTarget/shaders.glsl @@ -4,13 +4,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; +in vec4 in_pos; +in vec4 in_normal; out vec4 nrm; void main() { - gl_Position = mvp * position; - nrm = normal; + gl_Position = mvp * in_pos; + nrm = in_normal; } @end @@ -31,12 +31,11 @@ void main() { //------------------------------------------------------------------------------ @vs quadVS -in vec4 position; -in vec2 texcoord0; +in vec2 in_pos; out vec2 uv; void main() { - gl_Position = position; - uv = texcoord0; + gl_Position = vec4(in_pos*2.0-1.0, 0.5f, 1.0f); + uv = in_pos; } @end @@ -58,18 +57,18 @@ uniform vsParams { vec2 offsets; }; -in vec4 position; -in vec4 normal; -in vec2 texcoord0; +in vec4 in_pos; +in vec4 in_normal; +in vec2 in_uv; out vec2 uvRed; out vec2 uvGreen; out vec2 uvBlue; void main() { - gl_Position = mvp * position; - uvRed = texcoord0 + vec2(offsets.x, 0.0); - uvGreen = texcoord0 + vec2(0.0, offsets.y); - uvBlue = texcoord0; + gl_Position = mvp * in_pos; + uvRed = in_uv + vec2(offsets.x, 0.0); + uvGreen = in_uv + vec2(0.0, offsets.y); + uvBlue = in_uv; } @end diff --git a/code/Samples/NativeTexture/NativeTexture.cc b/code/Samples/NativeTexture/NativeTexture.cc index 93b514de1..bc79604b7 100644 --- a/code/Samples/NativeTexture/NativeTexture.cc +++ b/code/Samples/NativeTexture/NativeTexture.cc @@ -13,7 +13,7 @@ // need to access GL API directly #if ORYOL_OPENGL -#include "Gfx/private/gl/gl_impl.h" +#include "Gfx/private/gl/gl.h" #endif using namespace Oryol; @@ -27,11 +27,11 @@ class NativeTextureApp : public App { AppState::Code notSupported(); // render a 'not supported' message glm::mat4 computeMVP(const glm::vec3& pos); - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; ResourceLabel texLabel; Shader::vsParams params; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; uint8_t counter = 0; @@ -48,25 +48,28 @@ OryolMain(NativeTextureApp); AppState::Code NativeTextureApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(600, 400, "Oryol NativeTexture Sample"); - Gfx::Setup(gfxSetup); - Dbg::Setup(); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetSampleCount(4) + .SetTitle("Oryol NativeTexture Sample") + .SetHtmlTrackElementSize(true)); + Dbg::Setup(DbgDesc().SetSampleCount(4)); - // native texture handles are currently only supported on GL, on - // other APIs, just display a warning - if (!Gfx::QueryFeature(GfxFeature::NativeTexture)) { - return App::OnInit(); - } + // FIXME: D3D and Metal + #if !ORYOL_OPENGL + return App::OnInit(); + #endif - ShapeBuilder shapeBuilder; - shapeBuilder.RandomColors = true; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 4); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); + auto shape = ShapeBuilder() + .RandomColors(true) + .Positions("in_pos", VertexFormat::Float3) + .TexCoords("in_uv", VertexFormat::Float2) + .Box(1.0f, 1.0f, 1.0f, 4) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); #if ORYOL_OPENGL // the interesting part, create 2 GL textures and hand them to the @@ -88,29 +91,26 @@ NativeTextureApp::OnInit() { // make sure that the texture creation parameters here match the OpenGL // creation parameters (size, texture type, pixel format etc...), - auto texSetup = TextureSetup::FromNativeTexture(TexWidth, TexHeight, 1, - TextureType::Texture2D, - PixelFormat::RGBA8, - Usage::Stream, - this->glTextures[0], - this->glTextures[1]); // push a new resource label and keep it for later since we'll have // to cleanup the resource ourselves Gfx::PushResourceLabel(); - this->drawState.FSTexture[0] = Gfx::CreateResource(texSetup); + this->bind.FSTexture[0] = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::Texture2D) + .SetWidth(TexWidth) + .SetHeight(TexHeight) + .SetFormat(PixelFormat::RGBA8) + .SetUsage(Usage::Stream) + .SetNativeTexture(0, this->glTextures[0]) + .SetNativeTexture(1, this->glTextures[1])); this->texLabel = Gfx::PopResourceLabel(); #endif - - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(ps); - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); + // ...and finally the pipeline object + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); return App::OnInit(); } @@ -119,9 +119,9 @@ NativeTextureApp::OnInit() { AppState::Code NativeTextureApp::OnRunning() { - if (!Gfx::QueryFeature(GfxFeature::NativeTexture)) { - return notSupported(); - } + #if !ORYOL_OPENGL + return notSupported(); + #endif this->angleY += 0.01f; this->angleX += 0.02f; @@ -137,18 +137,17 @@ NativeTextureApp::OnRunning() { } } this->counter++; - ImageDataAttrs updAttrs; - updAttrs.NumFaces = 1; - updAttrs.NumMipMaps = 1; - updAttrs.Offsets[0][0] = 0; - updAttrs.Sizes[0][0] = sizeof(this->Buffer); - Gfx::UpdateTexture(this->drawState.FSTexture[0], this->Buffer, updAttrs); + ImageContent imgContent; + imgContent.Pointer[0][0] = this->Buffer; + imgContent.Size[0][0] = sizeof(this->Buffer); + Gfx::UpdateTexture(this->bind.FSTexture[0], imgContent); Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); this->params.mvp = this->computeMVP(glm::vec3(0.0f, 0.0f, -3.0f)); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(this->primGroup); Gfx::EndPass(); Gfx::CommitFrame(); @@ -172,19 +171,20 @@ NativeTextureApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 NativeTextureApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * modelTform; } //------------------------------------------------------------------------------ AppState::Code NativeTextureApp::notSupported() { const char* msg = "This demo needs GL\n"; - int x = (Gfx::DisplayAttrs().FramebufferWidth/16 - int(std::strlen(msg)))/2; - int y = Gfx::DisplayAttrs().FramebufferHeight/16/2; - Gfx::BeginPass(PassAction::Clear(glm::vec4(0.5f, 0.0f, 0.0f, 1.0f))); + int x = (Gfx::DisplayAttrs().Width/16 - int(std::strlen(msg)))/2; + int y = (Gfx::DisplayAttrs().Height/16)/2; + Gfx::BeginPass(PassAction().Clear(0.5f, 0.0f, 0.0f, 1.0f)); Dbg::TextScale(2.0f, 2.0f); Dbg::CursorPos(uint8_t(x), uint8_t(y)); Dbg::Print(msg); diff --git a/code/Samples/NativeTexture/shaders.glsl b/code/Samples/NativeTexture/shaders.glsl index 15176d4eb..51c082fb8 100644 --- a/code/Samples/NativeTexture/shaders.glsl +++ b/code/Samples/NativeTexture/shaders.glsl @@ -2,13 +2,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec2 texcoord0; +in vec4 in_pos; +in vec2 in_uv; out vec2 uv; void main() { - gl_Position = mvp * position; - uv = texcoord0; + gl_Position = mvp * in_pos; + uv = in_uv; } @end diff --git a/code/Samples/PackedNormals/PackedNormals.cc b/code/Samples/PackedNormals/PackedNormals.cc index c65ed60c8..5d6982564 100644 --- a/code/Samples/PackedNormals/PackedNormals.cc +++ b/code/Samples/PackedNormals/PackedNormals.cc @@ -16,13 +16,12 @@ class PackedNormalsApp : public App { AppState::Code OnRunning(); AppState::Code OnInit(); AppState::Code OnCleanup(); - glm::mat4 computeMVP(const glm::vec3& pos); - DrawState drawState; + Array primGroups; + Id pip; + Bindings bind; Shader::params params; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -31,32 +30,32 @@ OryolMain(PackedNormalsApp); //------------------------------------------------------------------------------ AppState::Code PackedNormalsApp::OnInit() { - Gfx::Setup(GfxSetup::WindowMSAA4(600, 400, "Oryol Packed Normals Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetSampleCount(4) + .SetTitle("Oryol Packed Normals Sample") + .SetHtmlTrackElementSize(true)); - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 4) + auto shapes = ShapeBuilder() + .Positions("position", VertexFormat::Float3) + .Normals("normal", VertexFormat::Byte4N) + .Box(1.0f, 1.0f, 1.0f, 4) .Sphere(0.75f, 36, 20) .Cylinder(0.5f, 1.5f, 36, 10) .Torus(0.3f, 0.5f, 20, 36) - .Plane(1.5f, 1.5f, 10); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.CullFaceEnabled = true; - ps.RasterizerState.SampleCount = 4; - this->drawState.Pipeline = Gfx::CreateResource(ps); + .Plane(1.5f, 1.5f, 10) + .Build(); + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shapes.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shapes.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(true) + .SetSampleCount(4)); + this->primGroups = std::move(shapes.PrimitiveGroups); - float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); - return App::OnInit(); } @@ -68,7 +67,8 @@ PackedNormalsApp::OnRunning() { this->angleX += 0.02f; Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); static const glm::vec3 positions[] = { glm::vec3(-1.0, 1.0f, -6.0f), glm::vec3(1.0f, 1.0f, -6.0f), @@ -79,8 +79,8 @@ PackedNormalsApp::OnRunning() { int primGroupIndex = 0; for (const auto& pos : positions) { this->params.mvp = this->computeMVP(pos); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(primGroupIndex++); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(this->primGroups[primGroupIndex++]); } Gfx::EndPass(); Gfx::CommitFrame(); @@ -98,8 +98,9 @@ PackedNormalsApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 PackedNormalsApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/PrimitiveTypes/PrimitiveTypes.cc b/code/Samples/PrimitiveTypes/PrimitiveTypes.cc index 9d02c4ffe..5940f5ea8 100644 --- a/code/Samples/PrimitiveTypes/PrimitiveTypes.cc +++ b/code/Samples/PrimitiveTypes/PrimitiveTypes.cc @@ -27,10 +27,10 @@ class PrimitiveTypesApp : public App { static const int NumTriangleIndices = (NumX-1) * (NumY-1) * 3; static const int NumTriStripIndices = (NumX-1)*(NumY-1)*2 + (NumY-1)*2; - StaticArray drawStates; + StaticArray pipelines; + StaticArray indexBuffers; + Id vertexBuffer; int curPrimType = 0; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; Shader::params params; @@ -39,74 +39,78 @@ OryolMain(PrimitiveTypesApp); //------------------------------------------------------------------------------ Id -createIndexMesh(int numIndices, const void* data, int dataSize) { - auto setup = MeshSetup::FromData(Usage::InvalidUsage, Usage::Immutable); - setup.NumVertices = 0; - setup.NumIndices = numIndices; - setup.IndicesType = IndexType::Index16; - setup.VertexDataOffset = InvalidIndex; - setup.IndexDataOffset = 0; - return Gfx::CreateResource(setup, data, dataSize); +createIndexBuffer(const uint16_t* data, int dataSize) { + return Gfx::CreateBuffer(BufferDesc() + .SetSize(dataSize) + .SetContent(data) + .SetType(BufferType::IndexBuffer) + .SetUsage(Usage::Immutable)); } //------------------------------------------------------------------------------ Id -createPipeline(PrimitiveType::Code primType, int layoutSlot, const VertexLayout& layout, Id shd, int sampleCount) { - auto pipSetup = PipelineSetup::FromShader(shd); - pipSetup.DepthStencilState.DepthWriteEnabled = true; - pipSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - pipSetup.RasterizerState.SampleCount = sampleCount; - pipSetup.Layouts[layoutSlot] = layout; - pipSetup.PrimType = primType; - return Gfx::CreateResource(pipSetup); +createPipeline(PrimitiveType::Code primType, IndexType::Code indexType, const VertexLayout& layout, Id shd, int sampleCount) { + return Gfx::CreatePipeline(PipelineDesc() + .SetShader(shd) + .SetLayout(0, layout) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(sampleCount) + .SetIndexType(indexType) + .SetPrimitiveType(primType)); } //------------------------------------------------------------------------------ AppState::Code PrimitiveTypesApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(640, 480, "Oryol PrimitiveTypes Test"); - Gfx::Setup(gfxSetup); - Dbg::Setup(); + Gfx::Setup(GfxDesc() + .SetWidth(640) + .SetHeight(480) + .SetSampleCount(4) + .SetTitle("Oryol PrimitiveTypes Test") + .SetHtmlTrackElementSize(true)); + Dbg::Setup(DbgDesc().SetSampleCount(4)); Input::Setup(); // create a 2D vertex grid mesh, the same vertex data is combined // with different index buffers - MeshBuilder meshBuilder; - meshBuilder.NumVertices = NumVertices; - meshBuilder.IndicesType = IndexType::None; - meshBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::UByte4N } - }; - meshBuilder.Begin(); - const float dx = 1.0f / NumX; - const float dy = 1.0f / NumY; - const float xOffset = -dx * (NumX/2); - const float yOffset = -dy * (NumY/2); - for (int y = 0, vi=0; y < NumY; y++) { - for (int x = 0; x < NumX; x++, vi++) { - meshBuilder.Vertex(vi, VertexAttr::Position, x*dx+xOffset, y*dy+yOffset, 0.0f); - switch (vi % 3) { - case 0: meshBuilder.Vertex(vi, VertexAttr::Color0, 1.0f, 0.0f, 0.0f, 1.0f); break; - case 1: meshBuilder.Vertex(vi, VertexAttr::Color0, 0.0f, 1.0f, 0.0f, 1.0f); break; - default: meshBuilder.Vertex(vi, VertexAttr::Color0, 1.0f, 1.0f, 0.0f, 1.0f); break; + MeshBuilder::Result mesh = MeshBuilder() + .NumVertices(NumVertices) + .IndexType(IndexType::None) + .Layout({ + { "position", VertexFormat::Float3 }, + { "color0", VertexFormat::UByte4N } + }) + .Build([](MeshBuilder& mb) { + const float dx = 1.0f / NumX; + const float dy = 1.0f / NumY; + const float xOffset = -dx * (NumX/2); + const float yOffset = -dy * (NumY/2); + for (int y = 0, vi=0; y < NumY; y++) { + for (int x = 0; x < NumX; x++, vi++) { + mb.Vertex(vi, 0, x*dx+xOffset, y*dy+yOffset, 0.0f); + switch (vi % 3) { + case 0: mb.Vertex(vi, 1, 1.0f, 0.0f, 0.0f, 1.0f); break; + case 1: mb.Vertex(vi, 1, 0.0f, 1.0f, 0.0f, 1.0f); break; + default: mb.Vertex(vi, 1, 1.0f, 1.0f, 0.0f, 1.0f); break; + } + } } - } - } - Id vertexMesh = Gfx::CreateResource(meshBuilder.Build()); + }); + this->vertexBuffer = Gfx::CreateBuffer(mesh.VertexBufferDesc); // a single shader used by all pipeline objects - Id shd = Gfx::CreateResource(Shader::Setup()); + Id shd = Gfx::CreateShader(Shader::Desc()); // now setup a complete draw state (pipeline + index mesh + shared vertex mesh) for // each primitive type (points, lines, linestrip, triangles, trianglestrip) // point list (only need a pipeline object, no index buffer) - { - auto& ds = this->drawStates[PrimitiveType::Points]; - ds.Pipeline = createPipeline(PrimitiveType::Points, 0, meshBuilder.Layout, shd, gfxSetup.SampleCount); - ds.Mesh[0] = vertexMesh; - } + this->pipelines[PrimitiveType::Points] = createPipeline(PrimitiveType::Points, + IndexType::None, + mesh.Layout, + shd, + Gfx::Desc().SampleCount); // line list index buffer mesh and pipeline state { @@ -123,10 +127,12 @@ PrimitiveTypesApp::OnInit() { } } o_assert_dbg(i == numIndices); - auto& ds = this->drawStates[PrimitiveType::Lines]; - ds.Pipeline = createPipeline(PrimitiveType::Lines, 1, meshBuilder.Layout, shd, gfxSetup.SampleCount); - ds.Mesh[0] = createIndexMesh(numIndices, &indices[0], numIndices*2); - ds.Mesh[1] = vertexMesh; + this->pipelines[PrimitiveType::Lines] = createPipeline(PrimitiveType::Lines, + IndexType::UInt16, + mesh.Layout, + shd, + Gfx::Desc().SampleCount); + this->indexBuffers[PrimitiveType::Lines] = createIndexBuffer(&indices[0], indices.Size()*sizeof(uint16_t)); } // line-strip index buffer mesh and pipeline state @@ -142,10 +148,12 @@ PrimitiveTypesApp::OnInit() { } } o_assert_dbg(i == numIndices); - auto& ds = this->drawStates[PrimitiveType::LineStrip]; - ds.Pipeline = createPipeline(PrimitiveType::LineStrip, 1, meshBuilder.Layout, shd, gfxSetup.SampleCount); - ds.Mesh[0] = createIndexMesh(numIndices, &indices[0], numIndices*2); - ds.Mesh[1] = vertexMesh; + this->pipelines[PrimitiveType::LineStrip] = createPipeline(PrimitiveType::LineStrip, + IndexType::UInt16, + mesh.Layout, + shd, + Gfx::Desc().SampleCount); + this->indexBuffers[PrimitiveType::LineStrip] = createIndexBuffer(&indices[0], indices.Size()*sizeof(uint16_t)); } // triangle-list index buffer and pipeline state @@ -165,10 +173,12 @@ PrimitiveTypesApp::OnInit() { } } o_assert_dbg(i == numIndices); - auto& ds = this->drawStates[PrimitiveType::Triangles]; - ds.Pipeline = createPipeline(PrimitiveType::Triangles, 1, meshBuilder.Layout, shd, gfxSetup.SampleCount); - ds.Mesh[0] = createIndexMesh(numIndices, &indices[0], numIndices*2); - ds.Mesh[1] = vertexMesh; + this->pipelines[PrimitiveType::Triangles] = createPipeline(PrimitiveType::Triangles, + IndexType::UInt16, + mesh.Layout, + shd, + Gfx::Desc().SampleCount); + this->indexBuffers[PrimitiveType::Triangles] = createIndexBuffer(&indices[0], indices.Size()*sizeof(uint16_t)); } // triangle-strip index buffer and pipeline state @@ -190,21 +200,30 @@ PrimitiveTypesApp::OnInit() { indices[i++] = (y+1)*NumX; } o_assert_dbg(i == numIndices); - auto& ds = this->drawStates[PrimitiveType::TriangleStrip]; - ds.Pipeline = createPipeline(PrimitiveType::TriangleStrip, 1, meshBuilder.Layout, shd, gfxSetup.SampleCount); - ds.Mesh[0] = createIndexMesh(numIndices, &indices[0], numIndices*2); - ds.Mesh[1] = vertexMesh; + this->pipelines[PrimitiveType::TriangleStrip] = createPipeline(PrimitiveType::TriangleStrip, + IndexType::UInt16, + mesh.Layout, + shd, + Gfx::Desc().SampleCount); + this->indexBuffers[PrimitiveType::TriangleStrip] = createIndexBuffer(&indices[0], indices.Size()*sizeof(uint16_t)); } - - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); this->params.psize = 4.0f; return App::OnInit(); } +//------------------------------------------------------------------------------ +static const char* primTypeToString(PrimitiveType::Code t) { + switch (t) { + case PrimitiveType::Points: return "Points"; + case PrimitiveType::Lines: return "Lines"; + case PrimitiveType::LineStrip: return "LineStrip"; + case PrimitiveType::Triangles: return "Triangles"; + case PrimitiveType::TriangleStrip: return "TriangleString"; + default: return "Unknown"; + } +} + //------------------------------------------------------------------------------ AppState::Code PrimitiveTypesApp::OnRunning() { @@ -215,7 +234,8 @@ PrimitiveTypesApp::OnRunning() { glm::mat4 model = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -1.5f)); model = glm::rotate(model, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); model = glm::rotate(model, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - this->params.mvp = this->proj * this->view * model; + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + this->params.mvp = proj * model; // render the currently selected drawstate Gfx::BeginPass(); @@ -229,9 +249,12 @@ PrimitiveTypesApp::OnRunning() { default: break; } if (num > 0) { - Gfx::ApplyDrawState(this->drawStates[this->curPrimType]); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(PrimitiveGroup(0, num)); + Gfx::ApplyPipeline(this->pipelines[this->curPrimType]); + Gfx::ApplyBindings(Bindings() + .SetVertexBuffer(0, this->vertexBuffer) + .SetIndexBuffer(this->indexBuffers[this->curPrimType])); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(0, num); } // handle input @@ -260,27 +283,27 @@ PrimitiveTypesApp::OnRunning() { } if (Input::MouseAttached()) { if (Input::MouseButtonDown(MouseButton::Left)) { - this->curPrimType = (this->curPrimType + 1) % PrimitiveType::NumPrimitiveTypes; + this->curPrimType = (this->curPrimType + 1) % PrimitiveType::Num; } } if (Input::TouchpadAttached()) { if (Input::TouchTapped()) { - this->curPrimType = (this->curPrimType + 1) % PrimitiveType::NumPrimitiveTypes; + this->curPrimType = (this->curPrimType + 1) % PrimitiveType::Num; } } // print help- and status-text Dbg::TextColor(0.0f, 1.0f, 0.0f, 1.0f); - Dbg::PrintF("\n Point Size (left/right key to change): %d\n\r", int(this->params.psize)); + Dbg::PrintF("\n\n\n\n\n Point Size (left/right key to change): %d\n\r", int(this->params.psize)); Dbg::Print(" Keys 1..5, left mouse button, or touch-tap to change primitive type\n\n\r"); - for (int i = 0; i < int(PrimitiveType::NumPrimitiveTypes); i++) { + for (int i = 0; i < int(PrimitiveType::Num); i++) { if (i == this->curPrimType) { Dbg::TextColor(1.0f, 0.0f, 0.0f, 1.0f); } else { Dbg::TextColor(1.0f, 1.0f, 0.0f, 1.0f); } - Dbg::PrintF("\n\r %d: %s", i+1, PrimitiveType::ToString(PrimitiveType::Code(i))); + Dbg::PrintF("\n\r %d: %s", i+1, primTypeToString(PrimitiveType::Code(i))); } Dbg::DrawTextBuffer(); diff --git a/code/Samples/Quad/Quad.cc b/code/Samples/Quad/Quad.cc index 8fdbff8fd..23bad0ca3 100644 --- a/code/Samples/Quad/Quad.cc +++ b/code/Samples/Quad/Quad.cc @@ -14,47 +14,49 @@ class QuadApp : public App { AppState::Code OnInit(); AppState::Code OnCleanup(); - DrawState drawState; + Id pip; + Bindings bind; }; OryolMain(QuadApp); //------------------------------------------------------------------------------ AppState::Code QuadApp::OnInit() { - Gfx::Setup(GfxSetup::Window(400, 400, "Oryol Quad Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(400) + .SetHeight(400) + .SetTitle("Oryol Quad Sample") + .SetHtmlTrackElementSize(true)); - // quad mesh with vertices followed by index data - static struct data_t { - const float vertices[4 * 7] = { - // positions colors - -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, - }; - const uint16_t indices[2 * 3] = { - 0, 1, 2, // first triangle - 0, 2, 3, // second triangle - }; - } data; - - auto meshSetup = MeshSetup::FromData(); - meshSetup.NumVertices = 4; - meshSetup.NumIndices = 6; - meshSetup.IndicesType = IndexType::Index16; - meshSetup.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } + // create vertex and index buffers + const float vertices[4 * 7] = { + // positions colors + -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, + }; + const uint16_t indices[2 * 3] = { + 0, 1, 2, // first triangle + 0, 2, 3, // second triangle }; - meshSetup.AddPrimitiveGroup({0, 6}); - meshSetup.VertexDataOffset = 0; - meshSetup.IndexDataOffset = offsetof(data_t, indices); - this->drawState.Mesh[0] = Gfx::CreateResource(meshSetup, &data, sizeof(data)); + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(BufferDesc() + .SetType(BufferType::VertexBuffer) + .SetSize(sizeof(vertices)) + .SetContent(vertices)); + this->bind.IndexBuffer = Gfx::CreateBuffer(BufferDesc() + .SetType(BufferType::IndexBuffer) + .SetSize(sizeof(indices)) + .SetContent(indices)); // create shader and pipeline-state-object - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(meshSetup.Layout, shd); - this->drawState.Pipeline = Gfx::CreateResource(ps); + this->pip = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetLayout(0, { + { "in_pos", VertexFormat::Float3 }, + { "in_color", VertexFormat::Float4 } + }) + .SetIndexType(IndexType::UInt16)); return App::OnInit(); } @@ -65,8 +67,9 @@ AppState::Code QuadApp::OnRunning() { Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::Draw(); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::Draw(0, 6); Gfx::EndPass(); Gfx::CommitFrame(); diff --git a/code/Samples/Quad/shaders.glsl b/code/Samples/Quad/shaders.glsl index 1cf7d8b4d..f5913109a 100644 --- a/code/Samples/Quad/shaders.glsl +++ b/code/Samples/Quad/shaders.glsl @@ -1,10 +1,10 @@ @vs vs -in vec4 position; -in vec4 color0; +in vec4 in_pos; +in vec4 in_color; out vec4 color; void main() { - gl_Position = position; - color = color0; + gl_Position = in_pos; + color = in_color; } @end diff --git a/code/Samples/RenderToCubeMap/RenderToCubeMap.cc b/code/Samples/RenderToCubeMap/RenderToCubeMap.cc index f32fdc81e..f0f652846 100644 --- a/code/Samples/RenderToCubeMap/RenderToCubeMap.cc +++ b/code/Samples/RenderToCubeMap/RenderToCubeMap.cc @@ -34,16 +34,18 @@ class RenderToCubeMapApp : public App { const glm::vec4 ClearColor = glm::vec4(0.5f, 0.5f, 0.7f, 1.0f); const glm::vec3 LightDir = glm::normalize(glm::vec3(-0.75, 1.0, 0.0)); + Array primGroups; Id cubeMap; Id passes[NumFaces]; - Id shapesMesh; + Id shapesVertexBuffer; + Id shapesIndexBuffer; Id displayShapesPipeline; Id offscreenShapesPipeline; - DrawState sphereDrawState; + Id spherePipeline; + Bindings sphereBind; - glm::mat4 displayProj; glm::mat4 offscreenProj; glm::vec2 polar; float distance = 20.0f; @@ -64,64 +66,75 @@ OryolMain(RenderToCubeMapApp); //------------------------------------------------------------------------------ AppState::Code RenderToCubeMapApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(800, 600, "Render To CubeMap"); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetSampleCount(4) + .SetTitle("Render To CubeMap") + .SetHtmlTrackElementSize(true)); Input::Setup(); // create a cubemap which will serve as render target - auto cubeMapSetup = TextureSetup::RenderTargetCube(1024, 1024, PixelFormat::RGBA8, PixelFormat::DEPTH); - cubeMapSetup.Sampler.MinFilter = TextureFilterMode::Linear; - cubeMapSetup.Sampler.MagFilter = TextureFilterMode::Linear; - this->cubeMap = Gfx::CreateResource(cubeMapSetup); + const PixelFormat::Code rtColorFormat = PixelFormat::RGBA8; + const PixelFormat::Code rtDepthFormat = PixelFormat::DEPTH; + this->cubeMap = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::TextureCube) + .SetRenderTarget(true) + .SetWidth(1024) + .SetHeight(1024) + .SetFormat(rtColorFormat) + .SetMinFilter(TextureFilterMode::Linear) + .SetMagFilter(TextureFilterMode::Linear)); + + // ...and a matching 2D depth buffer render target + Id rtDepth = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::Texture2D) + .SetRenderTarget(true) + .SetWidth(1024) + .SetHeight(1024) + .SetFormat(rtDepthFormat)); // create 6 render passes, one per cubemap face - auto rpSetup = PassSetup::From(this->cubeMap, this->cubeMap); - rpSetup.DefaultAction = PassAction::Clear(ClearColor); for (int faceIndex = 0; faceIndex < NumFaces; faceIndex++) { - rpSetup.ColorAttachments[0].Slice = faceIndex; - this->passes[faceIndex] = Gfx::CreateResource(rpSetup); + this->passes[faceIndex] = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, this->cubeMap, 0, faceIndex) + .SetDepthStencilAttachment(rtDepth)); } // mesh, shaders and pipelines to render color shapes - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Float3 } - }; - shapeBuilder + auto shapes = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Float3) .Box(1.0f, 1.0f, 1.0f, 1, true) .Cylinder(0.5f, 1.0f, 36, 1, true) .Torus(0.25f, 0.5f, 8, 36, true) - .Sphere(0.5f, 18, 12, true); - this->shapesMesh = Gfx::CreateResource(shapeBuilder.Build()); - Id envShd = Gfx::CreateResource(ShapeShader::Setup()); - auto pipSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, envShd); - pipSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - pipSetup.DepthStencilState.DepthWriteEnabled = true; - pipSetup.RasterizerState.SampleCount = gfxSetup.SampleCount; - pipSetup.BlendState.ColorFormat = gfxSetup.ColorFormat; - pipSetup.BlendState.DepthFormat = gfxSetup.DepthFormat; - this->displayShapesPipeline = Gfx::CreateResource(pipSetup); - pipSetup.Shader = Gfx::CreateResource(ShapeShaderWithGamma::Setup()); - pipSetup.RasterizerState.SampleCount = cubeMapSetup.SampleCount; - pipSetup.BlendState.ColorFormat = cubeMapSetup.ColorFormat; - pipSetup.BlendState.DepthFormat = cubeMapSetup.DepthFormat; - this->offscreenShapesPipeline = Gfx::CreateResource(pipSetup); + .Sphere(0.5f, 18, 12, true) + .Sphere(3.5f, 72, 48, true) // this is the big center sphere + .Build(); + this->primGroups = std::move(shapes.PrimitiveGroups); + this->shapesVertexBuffer = Gfx::CreateBuffer(shapes.VertexBufferDesc); + this->shapesIndexBuffer = Gfx::CreateBuffer(shapes.IndexBufferDesc); + this->displayShapesPipeline = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(ShapeShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->offscreenShapesPipeline = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(ShapeShaderWithGamma::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetColorFormat(rtColorFormat) + .SetDepthFormat(rtDepthFormat)); // create a sphere where the env-shapes reflect and refract in - this->sphereDrawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Sphere(3.5f, 72, 48).Build()); - Id sphereShd = Gfx::CreateResource(SphereShader::Setup()); - pipSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, sphereShd); - pipSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - pipSetup.DepthStencilState.DepthWriteEnabled = true; - pipSetup.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->sphereDrawState.Pipeline = Gfx::CreateResource(pipSetup); - this->sphereDrawState.FSTexture[SphereShader::tex] = this->cubeMap; - - // setup projection matrix for main view - float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->displayProj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); + this->sphereBind.VertexBuffers[0] = this->shapesVertexBuffer; + this->sphereBind.IndexBuffer = this->shapesIndexBuffer; + this->sphereBind.FSTexture[SphereShader::tex] = this->cubeMap; + this->spherePipeline = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(SphereShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); // setup projection matrix for cubemap rendering this->offscreenProj = glm::perspective(glm::radians(90.0f), 1.0f, 0.01f, 100.0f); @@ -162,29 +175,31 @@ RenderToCubeMapApp::OnRunning() { { glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f) }, }; for (int i = 0; i < NumFaces; i++) { - Gfx::BeginPass(this->passes[i]); + Gfx::BeginPass(this->passes[i], PassAction().Clear(ClearColor)); const glm::mat4 view = glm::lookAt(glm::vec3(0.0f), centerAndUp[i][0], centerAndUp[i][1]); this->drawEnvShapes(this->offscreenShapesPipeline, glm::vec3(0.0f), view, this->offscreenProj); Gfx::EndPass(); } // render the main view - Gfx::BeginPass(PassAction::Clear(ClearColor)); + Gfx::BeginPass(PassAction().Clear(ClearColor)); // draw the environment shapes const glm::vec3 eyePos = glm::euclidean(this->polar) * distance; const glm::mat4 view = glm::lookAt(eyePos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - this->drawEnvShapes(this->displayShapesPipeline, eyePos, view, this->displayProj); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + this->drawEnvShapes(this->displayShapesPipeline, eyePos, view, proj); // draw the sphere with reflection/refraction from cubemap - Gfx::ApplyDrawState(this->sphereDrawState); + Gfx::ApplyPipeline(this->spherePipeline); + Gfx::ApplyBindings(this->sphereBind); SphereShader::vsParams vsParams; - vsParams.mvp = this->displayProj * view; + vsParams.mvp = proj * view; vsParams.model = glm::mat4(); vsParams.lightDir = LightDir; vsParams.eyePos = eyePos; - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(); + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(this->primGroups[4]); Gfx::EndPass(); Gfx::CommitFrame(); @@ -228,12 +243,11 @@ RenderToCubeMapApp::updateShapes() { //------------------------------------------------------------------------------ void RenderToCubeMapApp::drawEnvShapes(Id pipeline, const glm::vec3& eyePos, const glm::mat4& view, const glm::mat4& proj) { - + Gfx::ApplyPipeline(pipeline); + Gfx::ApplyBindings(Bindings() + .SetVertexBuffer(0, this->shapesVertexBuffer) + .SetIndexBuffer(this->shapesIndexBuffer)); const glm::mat4 viewProj = proj * view; - DrawState drawState; - drawState.Pipeline = pipeline; - drawState.Mesh[0] = this->shapesMesh; - Gfx::ApplyDrawState(drawState); ShapeShader::vsParams vsParams; for (int i = 0; i < NumShapes; i++) { const auto& shape = this->Shapes[i]; @@ -242,8 +256,8 @@ RenderToCubeMapApp::drawEnvShapes(Id pipeline, const glm::vec3& eyePos, const gl vsParams.shapeColor = shape.color; vsParams.lightDir = LightDir; vsParams.eyePos = eyePos; - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(shape.shapeIndex); + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(this->primGroups[shape.shapeIndex]); } } diff --git a/code/Samples/RenderToCubeMap/shaders.glsl b/code/Samples/RenderToCubeMap/shaders.glsl index b1c4a638a..0069a5b23 100644 --- a/code/Samples/RenderToCubeMap/shaders.glsl +++ b/code/Samples/RenderToCubeMap/shaders.glsl @@ -40,8 +40,8 @@ uniform vsParams { vec3 eyePos; }; -in vec4 position; -in vec3 normal; +in vec4 in_pos; +in vec3 in_normal; out vec3 worldPosition; out vec3 worldNormal; out vec3 worldEyePos; @@ -49,9 +49,9 @@ out vec3 worldLightDir; out vec4 color; void main() { - gl_Position = mvp * position; - worldPosition = vec4(model * position).xyz; - worldNormal = vec4(model * vec4(normal, 0.0)).xyz; + gl_Position = mvp * in_pos; + worldPosition = vec4(model * in_pos).xyz; + worldNormal = vec4(model * vec4(in_normal, 0.0)).xyz; worldEyePos = eyePos; worldLightDir = lightDir; color = shapeColor; @@ -105,17 +105,17 @@ uniform vsParams { vec3 eyePos; }; -in vec4 position; -in vec3 normal; +in vec4 in_pos; +in vec3 in_normal; out vec3 worldPosition; out vec3 worldNormal; out vec3 worldEyePos; out vec3 worldLightDir; void main() { - gl_Position = mvp * position; - worldPosition = vec4(model * position).xyz; - worldNormal = vec4(model * vec4(normal, 0.0)).xyz; + gl_Position = mvp * in_pos; + worldPosition = vec4(model * in_pos).xyz; + worldNormal = vec4(model * vec4(in_normal, 0.0)).xyz; worldEyePos = eyePos; worldLightDir = lightDir; } diff --git a/code/Samples/ResourceStress/CMakeLists.txt b/code/Samples/ResourceStress/CMakeLists.txt index a743d74e3..787a24ef9 100644 --- a/code/Samples/ResourceStress/CMakeLists.txt +++ b/code/Samples/ResourceStress/CMakeLists.txt @@ -2,6 +2,6 @@ fips_begin_app(ResourceStress windowed) fips_vs_warning_level(3) fips_files(ResourceStress.cc) oryol_shader(shaders.glsl) - fips_deps(Gfx Assets HttpFS Dbg) + fips_deps(Gfx Assets HttpFS) oryol_add_web_sample(ResourceStress "Resource loading stresstest" "emscripten" none "ResourceStress/ResourceStress.cc") fips_end_app() diff --git a/code/Samples/ResourceStress/ResourceStress.cc b/code/Samples/ResourceStress/ResourceStress.cc index 533c9c4ec..0b783c2b2 100644 --- a/code/Samples/ResourceStress/ResourceStress.cc +++ b/code/Samples/ResourceStress/ResourceStress.cc @@ -5,7 +5,6 @@ #include "Core/Main.h" #include "IO/IO.h" #include "Gfx/Gfx.h" -#include "Dbg/Dbg.h" #include "HttpFS/HTTPFileSystem.h" #include "Assets/Gfx/ShapeBuilder.h" #include "Assets/Gfx/TextureLoader.h" @@ -24,10 +23,12 @@ class ResourceStressApp : public App { void createObjects(); void updateObjects(); - void showInfo(); struct Object { - DrawState drawState; + uint32_t createdFrame = 0; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; ResourceLabel label; glm::mat4 modelTransform; }; @@ -37,9 +38,6 @@ class ResourceStressApp : public App { uint32_t frameCount = 0; Id shader; Array objects; - glm::mat4 view; - glm::mat4 proj; - TextureSetup texBlueprint; }; OryolMain(ResourceStressApp); @@ -47,36 +45,23 @@ OryolMain(ResourceStressApp); AppState::Code ResourceStressApp::OnInit() { // setup IO system - IOSetup ioSetup; - ioSetup.FileSystems.Add("http", HTTPFileSystem::Creator()); - ioSetup.Assigns.Add("tex:", ORYOL_SAMPLE_URL); - IO::Setup(ioSetup); + IO::Setup(IODesc() + .AddFileSystem("http", HTTPFileSystem::Creator()) + .AddAssign("tex:", ORYOL_SAMPLE_URL)); // setup Gfx system - auto gfxSetup = GfxSetup::Window(600, 400, "Oryol Resource Stress Test"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.5f, 0.5f, 0.5f, 1.0f)); - gfxSetup.ResourcePoolSize[GfxResourceType::Mesh] = MaxNumObjects + 32; - gfxSetup.ResourcePoolSize[GfxResourceType::Texture] = MaxNumObjects + 32; - gfxSetup.ResourcePoolSize[GfxResourceType::Pipeline] = MaxNumObjects + 32; - gfxSetup.ResourcePoolSize[GfxResourceType::Shader] = 4; - Gfx::Setup(gfxSetup); - - // setup debug text rendering - Dbg::Setup(); - - // setup the shader that is used by all objects - this->shader = Gfx::CreateResource(Shader::Setup()); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetTitle("Oryol Resource Stress Test") + .SetResourcePoolSize(GfxResourceType::Buffer, 2 * (MaxNumObjects + 32)) + .SetResourcePoolSize(GfxResourceType::Texture, MaxNumObjects + 32) + .SetResourcePoolSize(GfxResourceType::Pipeline, MaxNumObjects + 32) + .SetResourcePoolSize(GfxResourceType::Shader, 4) + .SetHtmlTrackElementSize(true)); - // setup matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); - - this->texBlueprint.Sampler.MinFilter = TextureFilterMode::LinearMipmapLinear; - this->texBlueprint.Sampler.MagFilter = TextureFilterMode::Linear; - this->texBlueprint.Sampler.WrapU = TextureWrapMode::ClampToEdge; - this->texBlueprint.Sampler.WrapV = TextureWrapMode::ClampToEdge; + // setup the shader that is used by all objects + this->shader = Gfx::CreateShader(Shader::Desc()); return App::OnInit(); } @@ -89,21 +74,24 @@ ResourceStressApp::OnRunning() { this->frameCount++; this->updateObjects(); this->createObjects(); - this->showInfo(); - Gfx::BeginPass(); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.5f, 0.5f, 1.0f)); for (const auto& obj : this->objects) { - // only render objects that have successfully loaded - const Id& tex = obj.drawState.FSTexture[Shader::tex]; - if (Gfx::QueryResourceInfo(tex).State == ResourceState::Valid) { - Gfx::ApplyDrawState(obj.drawState); + // only render objects that have successfully loaded (technically + // the check is not necessary since rendering for non-valid resources + // will be skipped anyway, but this way we have test coverage for + // Gfx::QueryResourceState() + const Id& tex = obj.bind.FSTexture[Shader::tex]; + if (Gfx::QueryResourceState(tex) == ResourceState::Valid) { + Gfx::ApplyPipeline(obj.pip); + Gfx::ApplyBindings(obj.bind); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); Shader::vsParams vsParams; - vsParams.mvp = this->proj * this->view * obj.modelTransform; - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(); + vsParams.mvp = proj * obj.modelTransform; + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(obj.primGroup); } } - Dbg::DrawTextBuffer(); Gfx::EndPass(); Gfx::CommitFrame(); @@ -114,7 +102,6 @@ ResourceStressApp::OnRunning() { //------------------------------------------------------------------------------ AppState::Code ResourceStressApp::OnCleanup() { - Dbg::Discard(); Gfx::Discard(); IO::Discard(); return App::OnCleanup(); @@ -127,29 +114,32 @@ ResourceStressApp::createObjects() { if (this->objects.Size() >= MaxNumObjects) { return; } - if (Gfx::QueryFreeResourceSlots(GfxResourceType::Mesh) == 0) { - return; - } - if (Gfx::QueryFreeResourceSlots(GfxResourceType::Texture) == 0) { - return; - } // create a cube object // NOTE: we're deliberatly not sharing resources to actually // put some stress on the resource system Object obj; obj.label = Gfx::PushResourceLabel(); - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Box(0.1f, 0.1f, 0.1f, 1); - obj.drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, this->shader); - obj.drawState.Pipeline = Gfx::CreateResource(ps); - obj.drawState.FSTexture[Shader::tex] = Gfx::LoadResource(TextureLoader::Create( - TextureSetup::FromFile(Locator::NonShared("tex:lok_dxt1.dds"), this->texBlueprint))); + obj.createdFrame = this->frameCount; + auto shape = ShapeBuilder() + .Positions("position", VertexFormat::Float3) + .TexCoords("texcoord0", VertexFormat::Float2) + .Box(0.1f, 0.1f, 0.1f, 1) + .Build(); + obj.primGroup = shape.PrimitiveGroups[0]; + obj.bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + obj.bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + obj.bind.FSTexture[Shader::tex] = TextureLoader::Load(TextureDesc() + .SetLocator(Locator::NonShared("tex:lok_dxt1.dds")) + .SetMinFilter(TextureFilterMode::LinearMipmapLinear) + .SetMagFilter(TextureFilterMode::Linear) + .SetWrapU(TextureWrapMode::ClampToEdge) + .SetWrapV(TextureWrapMode::ClampToEdge)); + obj.pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(this->shader) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(true)); glm::vec3 pos = glm::ballRand(2.0f) + glm::vec3(0.0f, 0.0f, -6.0f); obj.modelTransform = glm::translate(glm::mat4(), pos); this->objects.Add(obj); @@ -161,54 +151,18 @@ void ResourceStressApp::updateObjects() { for (int i = this->objects.Size() - 1; i >= 0; i--) { Object& obj = this->objects[i]; + int age = this->frameCount - obj.createdFrame; // check if object should be destroyed (it will be // destroyed after the texture object had been valid for // at least 3 seconds, or if it failed to load) - const Id& tex = obj.drawState.FSTexture[Shader::tex]; - const auto info = Gfx::QueryResourceInfo(tex); - if ((info.State == ResourceState::Failed) || - ((info.State == ResourceState::Valid) && (info.StateAge > (20 * 60)))) { - + const Id& tex = obj.bind.FSTexture[Shader::tex]; + ResourceState::Code state = Gfx::QueryResourceState(tex); + if ((state == ResourceState::Failed) || + ((state == ResourceState::Valid) && (age > (20 * 60)))) { Gfx::DestroyResources(obj.label); this->objects.Erase(i); } } } -//------------------------------------------------------------------------------ -void -ResourceStressApp::showInfo() { - ResourcePoolInfo texPoolInfo = Gfx::QueryResourcePoolInfo(GfxResourceType::Texture); - ResourcePoolInfo mshPoolInfo = Gfx::QueryResourcePoolInfo(GfxResourceType::Mesh); - - Dbg::PrintF("texture pool\r\n" - " num slots: %d, free: %d, used: %d\r\n" - " by state:\r\n" - " initial: %d\r\n" - " setup: %d\r\n" - " pending: %d\r\n" - " valid: %d\r\n" - " failed: %d\r\n\n", - texPoolInfo.NumSlots, texPoolInfo.NumFreeSlots, texPoolInfo.NumUsedSlots, - texPoolInfo.NumSlotsByState[ResourceState::Initial], - texPoolInfo.NumSlotsByState[ResourceState::Setup], - texPoolInfo.NumSlotsByState[ResourceState::Pending], - texPoolInfo.NumSlotsByState[ResourceState::Valid], - texPoolInfo.NumSlotsByState[ResourceState::Failed]); - - Dbg::PrintF("mesh pool\r\n" - " num slots: %d, free: %d, used: %d\r\n" - " by state:\r\n" - " initial: %d\r\n" - " setup: %d\r\n" - " pending: %d\r\n" - " valid: %d\r\n" - " failed: %d", - mshPoolInfo.NumSlots, mshPoolInfo.NumFreeSlots, mshPoolInfo.NumUsedSlots, - mshPoolInfo.NumSlotsByState[ResourceState::Initial], - mshPoolInfo.NumSlotsByState[ResourceState::Setup], - mshPoolInfo.NumSlotsByState[ResourceState::Pending], - mshPoolInfo.NumSlotsByState[ResourceState::Valid], - mshPoolInfo.NumSlotsByState[ResourceState::Failed]); -} diff --git a/code/Samples/Sensors/Sensors.cc b/code/Samples/Sensors/Sensors.cc index 428a37a81..c3370d2fd 100644 --- a/code/Samples/Sensors/Sensors.cc +++ b/code/Samples/Sensors/Sensors.cc @@ -23,9 +23,10 @@ class SensorsApp : public App { glm::mat4 computeMVP(); - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; Shader::vsParams vsParams; - glm::mat4 proj; TimePoint lastFrameTimePoint; }; OryolMain(SensorsApp); @@ -33,42 +34,41 @@ OryolMain(SensorsApp); //------------------------------------------------------------------------------ AppState::Code SensorsApp::OnInit() { - Gfx::Setup(GfxSetup::Window(800, 400, "Oryol Device Sensor Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(400) + .SetTitle("Oryol Device Sensor Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); Input::Setup(); // create a 3D cube - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N } - }; - shapeBuilder.Box(2.0, 2.0, 2.0, 1); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.CullFaceEnabled = true; - this->drawState.Pipeline = Gfx::CreateResource(ps); - - // setup transform matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.1f, 100.0f); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Byte4N) + .Box(2.0, 2.0, 2.0, 1) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(true)); return App::OnInit(); } //------------------------------------------------------------------------------ glm::mat4 SensorsApp::computeMVP() { - glm::mat4 model = glm::mat4(); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.1f, 100.0f); const glm::vec3& ypr = Input::SensorYawPitchRoll(); glm::mat4 att = glm::yawPitchRoll(ypr.y, -ypr.z, 0.0f); glm::vec3 eye = glm::vec3(att[2]) * 6.0f; glm::vec3 up = glm::vec3(att[1]); glm::mat4 view = glm::lookAt(eye, glm::vec3(0.0f), up); - return this->proj * view * model; + return proj * view; } //------------------------------------------------------------------------------ @@ -76,10 +76,12 @@ AppState::Code SensorsApp::OnRunning() { Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); this->vsParams.mvp = this->computeMVP(); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); + Dbg::Print("\n\n\n\n"); if (!Input::SensorsAttached()) { Dbg::Print("\n Please run on mobile device!\n\r"); } diff --git a/code/Samples/Sensors/shaders.glsl b/code/Samples/Sensors/shaders.glsl index 4b73a2935..73de1d02a 100644 --- a/code/Samples/Sensors/shaders.glsl +++ b/code/Samples/Sensors/shaders.glsl @@ -3,13 +3,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; +in vec4 in_pos; +in vec4 in_normal; out vec4 nrm; void main() { - gl_Position = mvp * position; - nrm = normal; + gl_Position = mvp * in_pos; + nrm = in_normal; } @end diff --git a/code/Samples/SeparateBuffers/SeparateBuffers.cc b/code/Samples/SeparateBuffers/SeparateBuffers.cc index f9f25b997..fa6620ec0 100644 --- a/code/Samples/SeparateBuffers/SeparateBuffers.cc +++ b/code/Samples/SeparateBuffers/SeparateBuffers.cc @@ -27,12 +27,12 @@ class SeparateBuffersApp : public App { AppState::Code OnCleanup(); glm::mat4 computeMVP(const glm::vec3& pos); - static const int NumColorMeshes = 3; - StaticArray colorMesh; - DrawState drawState; + static const int NumColorBuffer = 3; + PrimitiveGroup cubePrimGroup; + StaticArray colorBuffers; + Id pip; + Bindings bind; Shader::params params; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -42,50 +42,45 @@ OryolMain(SeparateBuffersApp); AppState::Code SeparateBuffersApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(600, 400, "Separate Buffers"); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetSampleCount(4) + .SetTitle("Separate Buffers") + .SetHtmlTrackElementSize(true)); // create a cube mesh with positions only, this will be placed // into the first vertex buffer bind slot - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - }; - auto cubeSetupAndData = shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1).Build(); - this->drawState.Mesh[0] = Gfx::CreateResource(cubeSetupAndData); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Box(1.0f, 1.0f, 1.0f, 1) + .Build(); + this->cubePrimGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); // create 3 meshes with only color data - auto colorSetup = MeshSetup::FromData(); - colorSetup.Layout = { - { VertexAttr::Color0, VertexFormat::Float3 } - }; static const int NumVertices = 24; - o_assert(cubeSetupAndData.Setup.NumVertices == NumVertices); - colorSetup.NumVertices = NumVertices; - for (int i = 0; i < NumColorMeshes; i++) { - float colorVertices[NumVertices][NumColorMeshes] = { }; + static const int NumColorChannels = 3; + float colorVertices[NumVertices][NumColorChannels]= { }; + for (int i = 0; i < NumColorChannels; i++) { for (int vi = 0; vi < NumVertices; vi++) { colorVertices[vi][i] = glm::linearRand(0.5f, 1.0f); } - this->colorMesh[i] = Gfx::CreateResource(colorSetup, colorVertices, sizeof(colorVertices)); + this->colorBuffers[i] = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(colorVertices)) + .SetContent(colorVertices)); } // create shader and pipeline, the position data vertex Layout - // goes into the first slot, and the color data vertex layout into the second slot - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromShader(shd); - ps.Layouts[0] = cubeSetupAndData.Setup.Layout; - ps.Layouts[1] = colorSetup.Layout; - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(ps); + // goes into the first layout slot, and the color data vertex layout into the second slot + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetLayout(1, { { "in_color", VertexFormat::Float3 } }) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); - return App::OnInit(); } @@ -97,6 +92,7 @@ SeparateBuffersApp::OnRunning() { this->angleX += 0.02f; Gfx::BeginPass(); + Gfx::ApplyPipeline(pip); static const glm::vec3 positions[] = { glm::vec3(-2.0, 0.0f, -6.0f), glm::vec3(0.0f, 0.0f, -6.0f), @@ -105,11 +101,11 @@ SeparateBuffersApp::OnRunning() { for (int i = 0; i < 3; i++) { // switch to the next color data buffer, but keep the // same position data buffer - this->drawState.Mesh[1] = this->colorMesh[i]; - Gfx::ApplyDrawState(this->drawState); + this->bind.VertexBuffers[1] = this->colorBuffers[i]; + Gfx::ApplyBindings(this->bind); this->params.mvp = this->computeMVP(positions[i]); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(this->cubePrimGroup); } Gfx::EndPass(); Gfx::CommitFrame(); @@ -127,9 +123,10 @@ SeparateBuffersApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 SeparateBuffersApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/SeparateBuffers/shaders.glsl b/code/Samples/SeparateBuffers/shaders.glsl index c687e4610..ffe85825a 100644 --- a/code/Samples/SeparateBuffers/shaders.glsl +++ b/code/Samples/SeparateBuffers/shaders.glsl @@ -3,13 +3,13 @@ uniform params { mat4 mvp; }; -in vec4 position; -in vec3 color0; +in vec4 in_pos; +in vec3 in_color; out vec3 color; void main() { - gl_Position = mvp * position; - color = color0; + gl_Position = mvp * in_pos; + color = in_color; } @end diff --git a/code/Samples/Shapes/Shapes.cc b/code/Samples/Shapes/Shapes.cc index c806559f5..32bffc263 100644 --- a/code/Samples/Shapes/Shapes.cc +++ b/code/Samples/Shapes/Shapes.cc @@ -16,12 +16,12 @@ class ShapeApp : public App { AppState::Code OnRunning(); AppState::Code OnInit(); AppState::Code OnCleanup(); - glm::mat4 computeMVP(const glm::vec3& pos); - DrawState drawState; + + Id pip; + Bindings bind; + Array primGroups; Shader::params params; - glm::mat4 view; - glm::mat4 proj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -30,35 +30,31 @@ OryolMain(ShapeApp); //------------------------------------------------------------------------------ AppState::Code ShapeApp::OnInit() { - - auto gfxSetup = GfxSetup::WindowMSAA4(600, 400, "Oryol Shapes Sample"); - Gfx::Setup(gfxSetup); - - ShapeBuilder shapeBuilder; - shapeBuilder.RandomColors = true; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::UByte4N } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 4) + Gfx::Setup(GfxDesc() + .SetWidth(600) + .SetHeight(400) + .SetSampleCount(4) + .SetTitle("Oryol Shapes Sample") + .SetHtmlTrackElementSize(true)); + auto shapes = ShapeBuilder() + .RandomColors(true) + .Positions("position", VertexFormat::Float3) + .Colors("color0", VertexFormat::UByte4N) + .Box(1.0f, 1.0f, 1.0f, 4) .Sphere(0.75f, 36, 20) .Cylinder(0.5f, 1.5f, 36, 10) .Torus(0.3f, 0.5f, 20, 36) - .Plane(1.5f, 1.5f, 10); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); - - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(ps); + .Plane(1.5f, 1.5f, 10) + .Build(); + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shapes.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shapes.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shapes.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->primGroups = std::move(shapes.PrimitiveGroups); - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); - return App::OnInit(); } @@ -70,7 +66,8 @@ ShapeApp::OnRunning() { this->angleX += 0.02f; Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); static const glm::vec3 positions[] = { glm::vec3(-1.0, 1.0f, -6.0f), glm::vec3(1.0f, 1.0f, -6.0f), @@ -81,8 +78,8 @@ ShapeApp::OnRunning() { int primGroupIndex = 0; for (const auto& pos : positions) { this->params.mvp = this->computeMVP(pos); - Gfx::ApplyUniformBlock(this->params); - Gfx::Draw(primGroupIndex++); + Gfx::ApplyUniforms(this->params); + Gfx::Draw(this->primGroups[primGroupIndex++]); } Gfx::EndPass(); Gfx::CommitFrame(); @@ -100,9 +97,10 @@ ShapeApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 ShapeApp::computeMVP(const glm::vec3& pos) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, this->angleX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, this->angleY, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/SimpleRenderTarget/SimpleRenderTarget.cc b/code/Samples/SimpleRenderTarget/SimpleRenderTarget.cc index 39840c570..31077c262 100644 --- a/code/Samples/SimpleRenderTarget/SimpleRenderTarget.cc +++ b/code/Samples/SimpleRenderTarget/SimpleRenderTarget.cc @@ -20,14 +20,16 @@ class SimpleRenderTargetApp : public App { glm::mat4 computeMVP(const glm::mat4& proj, float rotX, float rotY, const glm::vec3& pos); + PrimitiveGroup donutPrimGroup; + PrimitiveGroup spherePrimGroup; Id renderPass; - DrawState offscreenDrawState; - DrawState displayDrawState; + Id offscreenPipeline; + Id displayPipeline; + Bindings offscreenBindings; + Bindings displayBindings; OffscreenShader::vsParams offscreenParams; DisplayShader::vsParams displayVSParams; - glm::mat4 view; glm::mat4 offscreenProj; - glm::mat4 displayProj; float angleX = 0.0f; float angleY = 0.0f; }; @@ -36,71 +38,71 @@ OryolMain(SimpleRenderTargetApp); //------------------------------------------------------------------------------ AppState::Code SimpleRenderTargetApp::OnInit() { - - auto gfxSetup = GfxSetup::WindowMSAA4(800, 600, "Oryol Simple Render Target Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.25f, 0.45f, 0.65f, 1.0f)); - Gfx::Setup(gfxSetup); - - // create an offscreen render pass object with a single color attachment - // texture, we explicitly want repeat texture wrap mode and linear blending... - auto rtSetup = TextureSetup::RenderTarget2D(128, 128, PixelFormat::RGBA8, PixelFormat::DEPTH); - rtSetup.Sampler.WrapU = TextureWrapMode::Repeat; - rtSetup.Sampler.WrapV = TextureWrapMode::Repeat; - rtSetup.Sampler.MagFilter = TextureFilterMode::Linear; - rtSetup.Sampler.MinFilter = TextureFilterMode::Linear; - // if supported, use an anti-aliased offscreen render target - if (Gfx::QueryFeature(GfxFeature::MSAARenderTargets)) { - rtSetup.SampleCount = 4; - Log::Info("Using MSAA4 render target\n"); - } - Id rtTexture = Gfx::CreateResource(rtSetup); - auto rpSetup = PassSetup::From(rtTexture, rtTexture); - rpSetup.DefaultAction = PassAction::Clear(glm::vec4(0.25f, 0.25f, 0.25f, 1.0f)); - this->renderPass = Gfx::CreateResource(rpSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetSampleCount(4) + .SetTitle("Oryol Simple Render Target Sample") + .SetHtmlTrackElementSize(true)); + + // create a color render target texture and compatible depth render target + // texture for offscreen rendering + const PixelFormat::Code rtColorFormat = PixelFormat::RGBA8; + const PixelFormat::Code rtDepthFormat = PixelFormat::DEPTH; + const int rtSampleCount = Gfx::QueryFeature(GfxFeature::MSAARenderTargets) ? 4 : 1; + auto rtCommon = TextureDesc() + .SetType(TextureType::Texture2D) + .SetRenderTarget(true) + .SetWidth(128) + .SetHeight(128) + .SetWrapU(TextureWrapMode::Repeat) + .SetWrapV(TextureWrapMode::Repeat) + .SetMagFilter(TextureFilterMode::Linear) + .SetMinFilter(TextureFilterMode::Linear) + .SetSampleCount(rtSampleCount); + Id rtColorTexture = Gfx::CreateTexture(TextureDesc(rtCommon).SetFormat(rtColorFormat)); + Id rtDepthTexture = Gfx::CreateTexture(TextureDesc(rtCommon).SetFormat(rtDepthFormat)); + this->renderPass = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, rtColorTexture) + .SetDepthStencilAttachment(rtDepthTexture)); // create a donut mesh, shader and pipeline object // (this will be rendered into the offscreen render target) - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N } - }; - shapeBuilder.Torus(0.3f, 0.5f, 20, 36); - this->offscreenDrawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - - // create shader and pipeline-state-object for offscreen rendering - Id offScreenShader = Gfx::CreateResource(OffscreenShader::Setup()); - auto offpsSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, offScreenShader); - offpsSetup.DepthStencilState.DepthWriteEnabled = true; - offpsSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - offpsSetup.BlendState.ColorFormat = rtSetup.ColorFormat; - offpsSetup.BlendState.DepthFormat = rtSetup.DepthFormat; - offpsSetup.RasterizerState.SampleCount = rtSetup.SampleCount; - this->offscreenDrawState.Pipeline = Gfx::CreateResource(offpsSetup); + auto donut = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Byte4N) + .Torus(0.3f, 0.5f, 20, 36) + .Build(); + this->donutPrimGroup = donut.PrimitiveGroups[0]; + this->offscreenBindings.VertexBuffers[0] = Gfx::CreateBuffer(donut.VertexBufferDesc); + this->offscreenBindings.IndexBuffer = Gfx::CreateBuffer(donut.IndexBufferDesc); + this->offscreenPipeline = Gfx::CreatePipeline(PipelineDesc(donut.PipelineDesc) + .SetShader(Gfx::CreateShader(OffscreenShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetColorFormat(rtColorFormat) + .SetDepthFormat(rtDepthFormat) + .SetSampleCount(rtSampleCount)); // create a sphere mesh, shader and pipeline object for rendering to display - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Sphere(0.5f, 72, 40); - this->displayDrawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - - Id dispShader = Gfx::CreateResource(DisplayShader::Setup()); - auto disppsSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, dispShader); - disppsSetup.DepthStencilState.DepthWriteEnabled = true; - disppsSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - disppsSetup.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->displayDrawState.Pipeline = Gfx::CreateResource(disppsSetup); - this->displayDrawState.FSTexture[DisplayShader::tex] = rtTexture; + auto sphere = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Byte4N) + .TexCoords("in_uv", VertexFormat::Float2) + .Sphere(0.5f, 72, 40) + .Build(); + this->spherePrimGroup = sphere.PrimitiveGroups[0]; + this->displayBindings.VertexBuffers[0] = Gfx::CreateBuffer(sphere.VertexBufferDesc); + this->displayBindings.IndexBuffer = Gfx::CreateBuffer(sphere.IndexBufferDesc); + this->displayPipeline = Gfx::CreatePipeline(PipelineDesc(sphere.PipelineDesc) + .SetShader(Gfx::CreateShader(DisplayShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); + this->displayBindings.FSTexture[DisplayShader::tex] = rtColorTexture; // setup static transform matrices - float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; this->offscreenProj = glm::perspective(glm::radians(45.0f), 1.0f, 0.01f, 20.0f); - this->displayProj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); - this->view = glm::mat4(); return App::OnInit(); } @@ -114,19 +116,22 @@ SimpleRenderTargetApp::OnRunning() { this->angleX += 0.02f; // render donut to offscreen render target - Gfx::BeginPass(this->renderPass); - Gfx::ApplyDrawState(this->offscreenDrawState); + Gfx::BeginPass(this->renderPass, PassAction().Clear(0.25f, 0.25f, 0.25f, 1.0f)); + Gfx::ApplyPipeline(this->offscreenPipeline); + Gfx::ApplyBindings(this->offscreenBindings); this->offscreenParams.mvp = this->computeMVP(this->offscreenProj, this->angleX, this->angleY, glm::vec3(0.0f, 0.0f, -3.0f)); - Gfx::ApplyUniformBlock(this->offscreenParams); - Gfx::Draw(); + Gfx::ApplyUniforms(this->offscreenParams); + Gfx::Draw(this->donutPrimGroup); Gfx::EndPass(); // render sphere to display, with offscreen render target as texture - Gfx::BeginPass(); - Gfx::ApplyDrawState(this->displayDrawState); - this->displayVSParams.mvp = this->computeMVP(this->displayProj, -this->angleX * 0.25f, this->angleY * 0.25f, glm::vec3(0.0f, 0.0f, -1.5f)); - Gfx::ApplyUniformBlock(this->displayVSParams); - Gfx::Draw(); + Gfx::BeginPass(PassAction().Clear(0.25f, 0.45f, 0.65f, 1.0f)); + Gfx::ApplyPipeline(this->displayPipeline); + Gfx::ApplyBindings(this->displayBindings); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); + this->displayVSParams.mvp = this->computeMVP(proj, -this->angleX * 0.25f, this->angleY * 0.25f, glm::vec3(0.0f, 0.0f, -1.5f)); + Gfx::ApplyUniforms(this->displayVSParams); + Gfx::Draw(this->spherePrimGroup); Gfx::EndPass(); Gfx::CommitFrame(); @@ -148,5 +153,5 @@ SimpleRenderTargetApp::computeMVP(const glm::mat4& proj, float rotX, float rotY, glm::mat4 modelTform = glm::translate(glm::mat4(), pos); modelTform = glm::rotate(modelTform, rotX, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, rotY, glm::vec3(0.0f, 1.0f, 0.0f)); - return proj * this->view * modelTform; + return proj * modelTform; } diff --git a/code/Samples/SimpleRenderTarget/shaders.glsl b/code/Samples/SimpleRenderTarget/shaders.glsl index 313e2ecbd..56fc8c3f7 100644 --- a/code/Samples/SimpleRenderTarget/shaders.glsl +++ b/code/Samples/SimpleRenderTarget/shaders.glsl @@ -6,13 +6,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; +in vec4 in_pos; +in vec4 in_normal; out vec4 nrm; void main() { - gl_Position = mvp * position; - nrm = normal; + gl_Position = mvp * in_pos; + nrm = in_normal; } @end @@ -34,16 +34,16 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; -in vec2 texcoord0; +in vec4 in_pos; +in vec4 in_normal; +in vec2 in_uv; out vec2 uv; out vec4 nrm; void main() { - gl_Position = mvp * position; - uv = texcoord0; - nrm = normalize(mvp * normal); + gl_Position = mvp * in_pos; + uv = in_uv; + nrm = normalize(mvp * in_normal); } @end diff --git a/code/Samples/TestInput/TestInput.cc b/code/Samples/TestInput/TestInput.cc index 60d16e3a9..61a177a1b 100644 --- a/code/Samples/TestInput/TestInput.cc +++ b/code/Samples/TestInput/TestInput.cc @@ -47,14 +47,15 @@ class TestInputApp : public App { float minDist; float maxDist; - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; glm::vec2 startPolar; glm::vec2 polar; float distance = 6.0f; float startDistance = 6.0f; glm::vec2 startMousePos; glm::vec3 pointOfInterest; - glm::mat4 proj; glm::mat4 view; glm::mat4 invView; bool pointerLock = false; @@ -66,11 +67,14 @@ OryolMain(TestInputApp); //------------------------------------------------------------------------------ AppState::Code TestInputApp::OnInit() { - auto gfxSetup = GfxSetup::Window(800, 400, "Oryol Input Test Sample"); - gfxSetup.HighDPI = true; - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(400) + .SetHighDPI(true) + .SetTitle("Oryol Input Test Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); - if (Gfx::DisplayAttrs().WindowWidth > 800) { + if (Gfx::DisplayAttrs().Width > 800) { Dbg::TextScale(2.0f, 2.0f); } @@ -90,23 +94,20 @@ TestInputApp::OnInit() { }); // create a 3D cube - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Normal, VertexFormat::Byte4N } - }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - ps.DepthStencilState.DepthWriteEnabled = true; - ps.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - ps.RasterizerState.CullFaceEnabled = true; - this->drawState.Pipeline = Gfx::CreateResource(ps); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Normals("in_normal", VertexFormat::Byte4N) + .Box(1.0f, 1.0f, 1.0f, 1) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetCullFaceEnabled(true)); - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); this->polar = glm::vec2(glm::radians(45.0f), glm::radians(45.0f)); this->distance = 6.0f; this->minLatitude = glm::radians(-85.0f); @@ -468,17 +469,20 @@ void TestInputApp::handleGamepadInput(int gamepadIndex) { //------------------------------------------------------------------------------ void TestInputApp::drawCube() { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); Shader::vsParams vsParams; - vsParams.mvp = this->proj * this->view; - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(vsParams); - Gfx::Draw(); + vsParams.mvp = proj * this->view; + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(vsParams); + Gfx::Draw(this->primGroup); } //------------------------------------------------------------------------------ AppState::Code TestInputApp::OnRunning() { // print input device status as debug text + Dbg::Print("\n\n"); this->printMouseState(); this->printKeyboardState(); this->printTouchpadState(); @@ -491,7 +495,7 @@ TestInputApp::OnRunning() { this->updateView(); // draw frame - Gfx::BeginPass(PassAction::Clear(this->getClearColor())); + Gfx::BeginPass(PassAction().Clear(this->getClearColor())); this->drawCube(); Dbg::DrawTextBuffer(); Gfx::EndPass(); diff --git a/code/Samples/TestInput/shaders.glsl b/code/Samples/TestInput/shaders.glsl index 3c5694043..3a2484bad 100644 --- a/code/Samples/TestInput/shaders.glsl +++ b/code/Samples/TestInput/shaders.glsl @@ -2,13 +2,13 @@ uniform vsParams { mat4 mvp; }; -in vec4 position; -in vec4 normal; +in vec4 in_pos; +in vec4 in_normal; out vec4 nrm; void main() { - gl_Position = mvp * position; - nrm = normal; + gl_Position = mvp * in_pos; + nrm = in_normal; } @end diff --git a/code/Samples/TextureFloat/TextureFloat.cc b/code/Samples/TextureFloat/TextureFloat.cc index e5f54638d..48b584cb3 100644 --- a/code/Samples/TextureFloat/TextureFloat.cc +++ b/code/Samples/TextureFloat/TextureFloat.cc @@ -19,10 +19,11 @@ class TextureFloatApp : public App { AppState::Code OnCleanup(); Id renderPass; - DrawState offscreenDrawState; - DrawState copyDrawState; - glm::mat4 view; - glm::mat4 proj; + PassAction renderPassAction; + Id offscreenPipeline; + Bindings offscreenBind; + Id copyPipeline; + Bindings copyBind; OffscreenShader::fsParams offscreenFSParams; TimePoint lastFrameTimePoint; }; @@ -32,8 +33,11 @@ OryolMain(TextureFloatApp); AppState::Code TextureFloatApp::OnInit() { // setup rendering system - auto gfxSetup = GfxSetup::Window(512, 512, "Oryol Float Texture Sample"); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(512) + .SetHeight(512) + .SetTitle("Oryol Float Texture Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); // check required extensions @@ -43,39 +47,40 @@ TextureFloatApp::OnInit() { // create an offscreen float render target, same size as display, // configure texture sampler with point-filtering - auto rtSetup = TextureSetup::RenderTarget2D(gfxSetup.Width, gfxSetup.Height, PixelFormat::RGBA32F); - rtSetup.Sampler.MagFilter = TextureFilterMode::Nearest; - rtSetup.Sampler.MinFilter = TextureFilterMode::Nearest; - Id rt = Gfx::CreateResource(rtSetup); - auto passSetup = PassSetup::From(rt); - passSetup.DefaultAction.DontCareColor(0); - this->renderPass = Gfx::CreateResource(passSetup); + const PixelFormat::Code rtColorFormat = PixelFormat::RGBA32F; + Id rt = Gfx::CreateTexture(TextureDesc() + .SetRenderTarget(true) + .SetWidth(Gfx::Desc().Width) + .SetHeight(Gfx::Desc().Height) + .SetFormat(rtColorFormat) + .SetMinFilter(TextureFilterMode::Nearest) + .SetMagFilter(TextureFilterMode::Nearest)); + this->renderPass = Gfx::CreatePass(PassDesc() + .SetColorAttachment(0, rt)); + this->renderPassAction.DontCareColor(0); // fullscreen mesh, we'll reuse this several times - auto quadSetup = MeshSetup::FullScreenQuad(); - Id quadMesh = Gfx::CreateResource(quadSetup); - this->offscreenDrawState.Mesh[0] = quadMesh; - this->copyDrawState.Mesh[0] = quadMesh; + const float quadVertices[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + this->offscreenBind.VertexBuffers[0] = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(quadVertices)) + .SetContent(quadVertices)); + this->copyBind.VertexBuffers[0] = this->offscreenBind.VertexBuffers[0]; // setup draw state for offscreen rendering to float render target - Id offscreenShader = Gfx::CreateResource(OffscreenShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(quadSetup.Layout, offscreenShader); - ps.BlendState.ColorFormat = rtSetup.ColorFormat; - ps.BlendState.DepthFormat = rtSetup.DepthFormat; - this->offscreenDrawState.Pipeline = Gfx::CreateResource(ps); + this->offscreenPipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(OffscreenShader::Desc())) + .SetLayout(0, {{"in_pos", VertexFormat::Float2}}) + .SetPrimitiveType(PrimitiveType::TriangleStrip) + .SetColorFormat(rtColorFormat) + .SetDepthFormat(PixelFormat::None)); this->offscreenFSParams.time = 0.0f; // fullscreen-copy resources - Id copyShader = Gfx::CreateResource(CopyShader::Setup()); - ps = PipelineSetup::FromLayoutAndShader(quadSetup.Layout, copyShader); - this->copyDrawState.Pipeline = Gfx::CreateResource(ps); - this->copyDrawState.FSTexture[CopyShader::tex] = rt; - - // setup static transform matrices - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 5.0f); - this->view = glm::mat4(); + this->copyPipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(CopyShader::Desc())) + .SetLayout(0, {{"in_pos", VertexFormat::Float2}}) + .SetPrimitiveType(PrimitiveType::TriangleStrip)); + this->copyBind.FSTexture[CopyShader::tex] = rt; return App::OnInit(); } @@ -87,22 +92,24 @@ TextureFloatApp::OnRunning() { this->offscreenFSParams.time += 1.0f / 60.0f; // render plasma to offscreen render target, do not clear - Gfx::BeginPass(this->renderPass); - Gfx::ApplyDrawState(this->offscreenDrawState); - Gfx::ApplyUniformBlock(this->offscreenFSParams); - Gfx::Draw(); + Gfx::BeginPass(this->renderPass, this->renderPassAction); + Gfx::ApplyPipeline(this->offscreenPipeline); + Gfx::ApplyBindings(this->offscreenBind); + Gfx::ApplyUniforms(this->offscreenFSParams); + Gfx::Draw(0, 4); Gfx::EndPass(); // copy fullscreen quad Gfx::BeginPass(); - Gfx::ApplyDrawState(this->copyDrawState); - Gfx::Draw(); + Gfx::ApplyPipeline(this->copyPipeline); + Gfx::ApplyBindings(this->copyBind); + Gfx::Draw(0, 4); Dbg::DrawTextBuffer(); Gfx::EndPass(); Gfx::CommitFrame(); Duration frameTime = Clock::LapTime(this->lastFrameTimePoint); - Dbg::PrintF("%.3fms", frameTime.AsMilliSeconds()); + Dbg::PrintF("\n\n\n\n\n %.3fms", frameTime.AsMilliSeconds()); // continue running or quit? return Gfx::QuitRequested() ? AppState::Cleanup : AppState::Running; diff --git a/code/Samples/TextureFloat/shaders.glsl b/code/Samples/TextureFloat/shaders.glsl index 2a8ce8eb5..fe0a33ff7 100644 --- a/code/Samples/TextureFloat/shaders.glsl +++ b/code/Samples/TextureFloat/shaders.glsl @@ -77,12 +77,11 @@ float snoise(vec2 v) //------------------------------------------------------------------------------ @vs offscreenVS -in vec4 position; -in vec2 texcoord0; -out vec2 uv; +in vec2 in_pos; +out vec2 uv; void main() { - gl_Position = position; - uv = texcoord0; + gl_Position = vec4(in_pos*2.0-1.0, 0.5f, 1.0f); + uv = in_pos; } @end @@ -125,12 +124,11 @@ void main() { // Copy offscreen render target to back buffer // @vs copyVS -in vec4 position; -in vec2 texcoord0; +in vec2 in_pos; out vec2 uv; void main() { - gl_Position = position; - uv = texcoord0; + gl_Position = vec4(in_pos*2.0-1.0, 0.5f, 1.0f); + uv = in_pos; } @end diff --git a/code/Samples/Triangle/Triangle.cc b/code/Samples/Triangle/Triangle.cc index a7b7562c8..0481278e1 100644 --- a/code/Samples/Triangle/Triangle.cc +++ b/code/Samples/Triangle/Triangle.cc @@ -14,7 +14,8 @@ class TriangleApp : public App { AppState::Code OnInit(); AppState::Code OnCleanup(); - DrawState drawState; + Id pip; + Bindings bind; }; OryolMain(TriangleApp); @@ -22,7 +23,11 @@ OryolMain(TriangleApp); AppState::Code TriangleApp::OnInit() { // setup rendering system - Gfx::Setup(GfxSetup::Window(400, 400, "Oryol Triangle Sample")); + Gfx::Setup(GfxDesc() + .SetWidth(400) + .SetHeight(400) + .SetTitle("Oryol Triangle Sample") + .SetHtmlTrackElementSize(true)); // create a mesh with vertex data from memory const float vertices[] = { @@ -31,19 +36,17 @@ TriangleApp::OnInit() { 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f , 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, }; - auto meshSetup = MeshSetup::FromData(); - meshSetup.NumVertices = 3; - meshSetup.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::Color0, VertexFormat::Float4 } - }; - meshSetup.AddPrimitiveGroup({0, 3}); - this->drawState.Mesh[0] = Gfx::CreateResource(meshSetup, vertices, sizeof(vertices)); + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(vertices)) + .SetContent(vertices)); // create shader and pipeline-state-object - Id shd = Gfx::CreateResource(Shader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(meshSetup.Layout, shd); - this->drawState.Pipeline = Gfx::CreateResource(ps); + this->pip = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetLayout(0, { + { "position", VertexFormat::Float3 }, + { "color0", VertexFormat::Float4 } + })); return App::OnInit(); } @@ -53,8 +56,9 @@ AppState::Code TriangleApp::OnRunning() { Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::Draw(); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::Draw(0, 3); Gfx::EndPass(); Gfx::CommitFrame(); diff --git a/code/Samples/VertexTexture/VertexTexture.cc b/code/Samples/VertexTexture/VertexTexture.cc index 4de542de5..b2e0c09fc 100644 --- a/code/Samples/VertexTexture/VertexTexture.cc +++ b/code/Samples/VertexTexture/VertexTexture.cc @@ -22,11 +22,12 @@ class VertexTextureApp : public App { glm::mat4 computeMVP(const glm::vec2& angles); Id plasmaRenderPass; - DrawState plasmaDrawState; - DrawState planeDrawState; + Id plasmaPipeline; + Bindings plasmaBind; + PrimitiveGroup planePrimGroup; + Id planePipeline; + Bindings planeBind; - glm::mat4 view; - glm::mat4 proj; PlaneShader::vsParams planeVSParams; PlasmaShader::fsParams plasmaFSParams; TimePoint lastFrameTimePoint; @@ -37,49 +38,54 @@ OryolMain(VertexTextureApp); AppState::Code VertexTextureApp::OnInit() { // setup rendering system - Gfx::Setup(GfxSetup::WindowMSAA4(800, 600, "Oryol Vertex Texture Sample")); - Dbg::Setup(); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetSampleCount(4) + .SetTitle("Oryol Vertex Texture Sample") + .SetHtmlTrackElementSize(true)); + Dbg::Setup(DbgDesc().SetSampleCount(4)); // FIXME: need a way to check number of vertex texture units // create RGBA offscreen render pass which holds the plasma - auto rtSetup = TextureSetup::RenderTarget2D(256, 256, PixelFormat::RGBA8); - rtSetup.Sampler.MinFilter = TextureFilterMode::Nearest; - rtSetup.Sampler.MagFilter = TextureFilterMode::Nearest; - Id plasmaTex = Gfx::CreateResource(rtSetup); - auto passSetup = PassSetup::From(plasmaTex); - passSetup.DefaultAction.DontCareColor(0); - this->plasmaRenderPass = Gfx::CreateResource(passSetup); + Id plasmaTex = Gfx::CreateTexture(TextureDesc() + .SetRenderTarget(true) + .SetWidth(256) + .SetHeight(256) + .SetFormat(PixelFormat::RGBA8) + .SetMinFilter(TextureFilterMode::Nearest) + .SetMagFilter(TextureFilterMode::Nearest)); + this->plasmaRenderPass = Gfx::CreatePass(PassDesc().SetColorAttachment(0, plasmaTex)); // setup draw state for offscreen rendering to float render target - auto quadSetup = MeshSetup::FullScreenQuad(); - this->plasmaDrawState.Mesh[0] = Gfx::CreateResource(quadSetup); - Id plasmaShader = Gfx::CreateResource(PlasmaShader::Setup()); - auto ps = PipelineSetup::FromLayoutAndShader(quadSetup.Layout, plasmaShader); - ps.BlendState.ColorFormat = rtSetup.ColorFormat; - ps.BlendState.DepthFormat = rtSetup.DepthFormat; - this->plasmaDrawState.Pipeline = Gfx::CreateResource(ps); + const float quadVertices[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; + this->plasmaBind.VertexBuffers[0] = Gfx::CreateBuffer(BufferDesc() + .SetSize(sizeof(quadVertices)) + .SetContent(quadVertices)); + this->plasmaPipeline = Gfx::CreatePipeline(PipelineDesc() + .SetShader(Gfx::CreateShader(PlasmaShader::Desc())) + .SetLayout(0, { { "in_pos", VertexFormat::Float2 } }) + .SetPrimitiveType(PrimitiveType::TriangleStrip) + .SetColorFormat(PixelFormat::RGBA8) + .SetDepthFormat(PixelFormat::None)); // draw state for a 256x256 plane - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { - { VertexAttr::Position, VertexFormat::Float3 }, - { VertexAttr::TexCoord0, VertexFormat::Float2 } - }; - shapeBuilder.Plane(3.0f, 3.0f, 255); - this->planeDrawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - Id planeShader = Gfx::CreateResource(PlaneShader::Setup()); - auto psPlane = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, planeShader); - psPlane.DepthStencilState.DepthWriteEnabled = true; - psPlane.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - psPlane.RasterizerState.SampleCount = 4; - this->planeDrawState.Pipeline = Gfx::CreateResource(psPlane); - this->planeDrawState.VSTexture[PlaneShader::tex] = plasmaTex; + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .TexCoords("in_uv", VertexFormat::Float2) + .Plane(3.0f, 3.0f, 255) + .Build(); + this->planePrimGroup = shape.PrimitiveGroups[0]; + this->planeBind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->planeBind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->planeBind.VSTexture[PlaneShader::tex] = plasmaTex; + this->planePipeline = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(PlaneShader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(4)); - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 10.0f); - this->view = glm::lookAt(glm::vec3(0.0f, 1.5f, 0.0f), glm::vec3(0.0f, 0.0f, -3.0f), glm::vec3(0.0f, 1.0f, 0.0f)); this->plasmaFSParams.time = 0.0f; return App::OnInit(); @@ -93,23 +99,25 @@ VertexTextureApp::OnRunning() { this->planeVSParams.mvp = this->computeMVP(glm::vec2(0.0f, 0.0f)); // render plasma to offscreen render target - Gfx::BeginPass(this->plasmaRenderPass); - Gfx::ApplyDrawState(this->plasmaDrawState); - Gfx::ApplyUniformBlock(this->plasmaFSParams); - Gfx::Draw(); + Gfx::BeginPass(this->plasmaRenderPass, PassAction().DontCare()); + Gfx::ApplyPipeline(this->plasmaPipeline); + Gfx::ApplyBindings(this->plasmaBind); + Gfx::ApplyUniforms(this->plasmaFSParams); + Gfx::Draw(0, 4); Gfx::EndPass(); // render displacement mapped plane shape Gfx::BeginPass(); - Gfx::ApplyDrawState(this->planeDrawState); - Gfx::ApplyUniformBlock(this->planeVSParams); - Gfx::Draw(); + Gfx::ApplyPipeline(this->planePipeline); + Gfx::ApplyBindings(this->planeBind); + Gfx::ApplyUniforms(this->planeVSParams); + Gfx::Draw(this->planePrimGroup); Dbg::DrawTextBuffer(); Gfx::EndPass(); Gfx::CommitFrame(); Duration frameTime = Clock::LapTime(this->lastFrameTimePoint); - Dbg::PrintF("%.3fms", frameTime.AsMilliSeconds()); + Dbg::PrintF("\n\n\n\n\n %.3fms", frameTime.AsMilliSeconds()); // continue running or quit? return Gfx::QuitRequested() ? AppState::Cleanup : AppState::Running; @@ -126,9 +134,11 @@ VertexTextureApp::OnCleanup() { //------------------------------------------------------------------------------ glm::mat4 VertexTextureApp::computeMVP(const glm::vec2& angles) { + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 10.0f); + glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 1.5f, 0.0f), glm::vec3(0.0f, 0.0f, -3.0f), glm::vec3(0.0f, 1.0f, 0.0f)); glm::mat4 modelTform = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -3.0f)); modelTform = glm::rotate(modelTform, angles.x, glm::vec3(1.0f, 0.0f, 0.0f)); modelTform = glm::rotate(modelTform, angles.y, glm::vec3(0.0f, 1.0f, 0.0f)); - return this->proj * this->view * modelTform; + return proj * view * modelTform; } diff --git a/code/Samples/VertexTexture/shaders.glsl b/code/Samples/VertexTexture/shaders.glsl index 5c2ce91be..a969b03df 100644 --- a/code/Samples/VertexTexture/shaders.glsl +++ b/code/Samples/VertexTexture/shaders.glsl @@ -76,12 +76,11 @@ float snoise(vec2 v) //------------------------------------------------------------------------------ @vs plasmaVS -in vec4 position; -in vec2 texcoord0; +in vec2 in_pos; out vec2 uv; void main() { - gl_Position = position; - uv = texcoord0; + gl_Position = vec4(in_pos * 2.0 - 1.0, 0.5, 1.0); + uv = in_pos; } @end @@ -130,13 +129,13 @@ uniform vsParams { mat4 mvp; }; uniform sampler2D tex; -in vec4 position; -in vec2 texcoord0; +in vec4 in_pos; +in vec2 in_uv; out vec4 color; void main() { - color = texture(tex, texcoord0); - vec4 pos = position; + color = texture(tex, in_uv); + vec4 pos = in_pos; pos.y = color.w; color.w = 1.0; gl_Position = mvp * pos; diff --git a/code/Samples/VolumeTexture/VolumeTexture.cc b/code/Samples/VolumeTexture/VolumeTexture.cc index e0ba250b3..da59b948d 100644 --- a/code/Samples/VolumeTexture/VolumeTexture.cc +++ b/code/Samples/VolumeTexture/VolumeTexture.cc @@ -22,19 +22,23 @@ class VolumeTextureApp : public App { AppState::Code notSupported(); // render a warning if 3D textures not supported by platform void computeShaderParams(); - DrawState drawState; + PrimitiveGroup primGroup; + Id pip; + Bindings bind; Shader::vsParams vsParams; int frameIndex = 0; - glm::mat4 proj; }; OryolMain(VolumeTextureApp); //------------------------------------------------------------------------------ AppState::Code VolumeTextureApp::OnInit() { - auto gfxSetup = GfxSetup::WindowMSAA4(800, 600, "3D Texture Sample"); - gfxSetup.DefaultPassAction = PassAction::Clear(glm::vec4(0.25f, 0.25f, 0.25f, 1.0f)); - Gfx::Setup(gfxSetup); + Gfx::Setup(GfxDesc() + .SetWidth(800) + .SetHeight(600) + .SetSampleCount(4) + .SetTitle("3D Texture Sample") + .SetHtmlTrackElementSize(true)); Dbg::Setup(); // if 3D textures not supported show a warning later during rendering @@ -61,30 +65,30 @@ VolumeTextureApp::OnInit() { } p.z += 1.0f / dim; } - auto texSetup = TextureSetup::FromPixelData3D(dim, dim, dim, 1, PixelFormat::RGBA8); - texSetup.Sampler.MinFilter = TextureFilterMode::Linear; - texSetup.Sampler.MagFilter = TextureFilterMode::Linear; - texSetup.ImageData.Sizes[0][0] = sizeof(data); - this->drawState.FSTexture[Shader::tex] = Gfx::CreateResource(texSetup, data, sizeof(data)); + this->bind.FSTexture[Shader::tex] = Gfx::CreateTexture(TextureDesc() + .SetType(TextureType::Texture3D) + .SetWidth(dim) + .SetHeight(dim) + .SetDepth(dim) + .SetFormat(PixelFormat::RGBA8) + .SetMinFilter(TextureFilterMode::Linear) + .SetMagFilter(TextureFilterMode::Linear) + .SetMipSize(0, 0, sizeof(data)) + .SetMipContent(0, 0, data)); // create a cube which will be the hull geometry for raycasting through the 3D texture - ShapeBuilder shapeBuilder; - shapeBuilder.Layout = { { VertexAttr::Position, VertexFormat::Float3 } }; - shapeBuilder.Box(1.0f, 1.0f, 1.0f, 1); - this->drawState.Mesh[0] = Gfx::CreateResource(shapeBuilder.Build()); - - // pipeline state for rendering the 3D-textures cube - Id shd = Gfx::CreateResource(Shader::Setup()); - auto pipSetup = PipelineSetup::FromLayoutAndShader(shapeBuilder.Layout, shd); - pipSetup.DepthStencilState.DepthWriteEnabled = true; - pipSetup.DepthStencilState.DepthCmpFunc = CompareFunc::LessEqual; - pipSetup.RasterizerState.SampleCount = gfxSetup.SampleCount; - this->drawState.Pipeline = Gfx::CreateResource(pipSetup); - - // setup a projection matrix with the right aspect ratio - const float fbWidth = (const float) Gfx::DisplayAttrs().FramebufferWidth; - const float fbHeight = (const float) Gfx::DisplayAttrs().FramebufferHeight; - this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 100.0f); + auto shape = ShapeBuilder() + .Positions("in_pos", VertexFormat::Float3) + .Box(1.0f, 1.0f, 1.0f, 1) + .Build(); + this->primGroup = shape.PrimitiveGroups[0]; + this->bind.VertexBuffers[0] = Gfx::CreateBuffer(shape.VertexBufferDesc); + this->bind.IndexBuffer = Gfx::CreateBuffer(shape.IndexBufferDesc); + this->pip = Gfx::CreatePipeline(PipelineDesc(shape.PipelineDesc) + .SetShader(Gfx::CreateShader(Shader::Desc())) + .SetDepthWriteEnabled(true) + .SetDepthCmpFunc(CompareFunc::LessEqual) + .SetSampleCount(Gfx::Desc().SampleCount)); return App::OnInit(); } @@ -101,10 +105,11 @@ VolumeTextureApp::OnRunning() { this->computeShaderParams(); // render the rotating cube - Gfx::BeginPass(); - Gfx::ApplyDrawState(this->drawState); - Gfx::ApplyUniformBlock(this->vsParams); - Gfx::Draw(); + Gfx::BeginPass(PassAction().Clear(0.25f, 0.25f, 0.25f, 1.0f)); + Gfx::ApplyPipeline(this->pip); + Gfx::ApplyBindings(this->bind); + Gfx::ApplyUniforms(this->vsParams); + Gfx::Draw(this->primGroup); Gfx::EndPass(); Gfx::CommitFrame(); this->frameIndex++; @@ -125,13 +130,14 @@ VolumeTextureApp::computeShaderParams() { const glm::vec4 eyePos(0.0f, 0.0f, 0.0f, 1.0f); const glm::vec3 pos(0.0f, 0.0f, -2.0f); + glm::mat4 proj = glm::perspectiveFov(glm::radians(45.0f), float(Gfx::Width()), float(Gfx::Height()), 0.01f, 100.0f); float angleX = glm::radians(0.2f * this->frameIndex); float angleY = glm::radians(0.1f * this->frameIndex); glm::mat4 model = glm::translate(glm::mat4(), pos); model = glm::rotate(model, angleX, glm::vec3(1.0f, 0.0f, 0.0f)); model = glm::rotate(model, angleY, glm::vec3(0.0f, 1.0f, 0.0f)); glm::mat4 invModel = glm::inverse(model); - this->vsParams.mvp = this->proj * model; + this->vsParams.mvp = proj * model; this->vsParams.modelEyePos = invModel * eyePos; } @@ -143,9 +149,9 @@ VolumeTextureApp::notSupported() { #else const char* msg = "This demo needs 3D texture support\n"; #endif - uint8_t x = uint8_t((Gfx::DisplayAttrs().FramebufferWidth/16 - std::strlen(msg))/2); - uint8_t y = uint8_t(Gfx::DisplayAttrs().FramebufferHeight/16/2); - Gfx::BeginPass(PassAction::Clear(glm::vec4(0.5f, 0.0f, 0.0f, 1.0f))); + uint8_t x = uint8_t((Gfx::DisplayAttrs().Width/16 - std::strlen(msg))/2); + uint8_t y = uint8_t((Gfx::DisplayAttrs().Height/16)/2); + Gfx::BeginPass(PassAction().Clear(0.5f, 0.0f, 0.0f, 1.0f)); Dbg::TextScale(2.0f, 2.0f); Dbg::CursorPos(x, y); Dbg::Print(msg); diff --git a/code/Samples/VolumeTexture/shaders.glsl b/code/Samples/VolumeTexture/shaders.glsl index 9a9c7dd29..58184437c 100644 --- a/code/Samples/VolumeTexture/shaders.glsl +++ b/code/Samples/VolumeTexture/shaders.glsl @@ -6,13 +6,13 @@ uniform vsParams { mat4 mvp; vec4 modelEyePos; }; -in vec4 position; +in vec4 in_pos; out vec3 eyePos; out vec3 surfPos; void main() { - gl_Position = mvp * position; + gl_Position = mvp * in_pos; eyePos = modelEyePos.xyz + vec3(0.5, 0.5, 0.5); - surfPos = position.xyz + vec3(0.5, 0.5, 0.5); + surfPos = in_pos.xyz + vec3(0.5, 0.5, 0.5); } @end diff --git a/doc/BUILD.md b/doc/BUILD.md index 3c3e2f2e8..92ceb4e8b 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -66,7 +66,7 @@ can be easily selected via fips build configs: ##### D3D11: Building with D3D11 support requires at least Windows7 and a recent -Visual Studio version (VS2013 or VS2015). Select any of the following build +Visual Studio version (at least VS2015). Select any of the following build configs: - d3d11-win64-vstudio-debug diff --git a/fips-files/configs/webgl2-emsc-ninja-release.yml b/fips-files/configs/webgl2-emsc-ninja-release.yml index df3d5a9ac..833d48bac 100644 --- a/fips-files/configs/webgl2-emsc-ninja-release.yml +++ b/fips-files/configs/webgl2-emsc-ninja-release.yml @@ -8,4 +8,4 @@ defines: FIPS_NO_ASSERTS_IN_RELEASE: ON FIPS_EMSCRIPTEN_USE_WEBGL2: ON FIPS_EMSCRIPTEN_USE_CWRAP: ON - + FIPS_EMSCRIPTEN_MEM_INIT_METHOD: 0 diff --git a/fips-files/generators/Shader.py b/fips-files/generators/Shader.py index 7824137f8..22eac8348 100644 --- a/fips-files/generators/Shader.py +++ b/fips-files/generators/Shader.py @@ -2,13 +2,12 @@ Code generator for shader libraries. ''' -Version = 49 +Version = 64 import os, platform, json import genutil as util from util import glslcompiler, shdc from mod import log -import zlib # only for crc32 if platform.system() == 'Windows' : from util import hlslcompiler @@ -40,11 +39,6 @@ def isHLSL(sl): def isMetal(sl): return sl == 'metal' -validVsInNames = [ - 'position', 'normal', 'texcoord0', 'texcoord1', 'texcoord2', 'texcoord3', - 'tangent', 'binormal', 'weights', 'indices', 'color0', 'color1', - 'instance0', 'instance1', 'instance2', 'instance3' -] validInOutTypes = [ 'float', 'vec2', 'vec3', 'vec4' ] validUniformTypes = [ 'mat4', 'mat2', 'vec4', 'vec3', 'vec2', 'float' ] @@ -73,29 +67,10 @@ def isMetal(sl): } attrOryolType = { - 'float': 'Oryol::VertexFormat::Float', - 'vec2': 'Oryol::VertexFormat::Float2', - 'vec3': 'Oryol::VertexFormat::Float3', - 'vec4': 'Oryol::VertexFormat::Float4' -} - -attrOryolName = { - 'position': 'Oryol::VertexAttr::Position', - 'normal': 'Oryol::VertexAttr::Normal', - 'texcoord0': 'Oryol::VertexAttr::TexCoord0', - 'texcoord1': 'Oryol::VertexAttr::TexCoord1', - 'texcoord2': 'Oryol::VertexAttr::TexCoord2', - 'texcoord3': 'Oryol::VertexAttr::TexCoord3', - 'tangent': 'Oryol::VertexAttr::Tangent', - 'binormal': 'Oryol::VertexAttr::Binormal', - 'weights': 'Oryol::VertexAttr::Weights', - 'indices': 'Oryol::VertexAttr::Indices', - 'color0': 'Oryol::VertexAttr::Color0', - 'color1': 'Oryol::VertexAttr::Color1', - 'instance0': 'Oryol::VertexAttr::Instance0', - 'instance1': 'Oryol::VertexAttr::Instance1', - 'instance2': 'Oryol::VertexAttr::Instance2', - 'instance3': 'Oryol::VertexAttr::Instance3' + 'float': 'VertexFormat::Float', + 'vec2': 'VertexFormat::Float2', + 'vec3': 'VertexFormat::Float3', + 'vec4': 'VertexFormat::Float4', } validTextureTypes = [ @@ -384,8 +359,6 @@ def validate(self, slangs) : util.setErrorLocation(vs.lines[0].path, vs.lines[0].lineNumber) vs_inputs = refl['inputs'] for vs_input in vs_inputs: - if vs_input['name'] not in validVsInNames: - util.fmtError("invalid vertex shader input name '{}', must be ({})".format(vs_input['name'], ','.join(validVsInNames))) if vs_input['type'] not in validInOutTypes: util.fmtError("invalid vertex shader input type '{}', must be ({})".format(vs_input['type'], ','.join(validInOutTypes))) for ub in refl['uniform_blocks']: @@ -482,14 +455,6 @@ def writeHeaderTop(f, shdLib) : def writeHeaderBottom(f, shdLib) : f.write('\n') -#------------------------------------------------------------------------------- -def getUniformBlockTypeHash(ub_refl): - hashString = '' - for member in ub_refl['members']: - hashString += member['type'] - hashString += str(member['num']) - return zlib.crc32(hashString.encode('ascii')) & 0xFFFFFFFF - #------------------------------------------------------------------------------- def roundup(val, round_to): return (val + (round_to - 1)) & ~(round_to - 1) @@ -506,7 +471,6 @@ def writeProgramHeader(f, shdLib, prog, slang) : f.write(' struct {} {{\n'.format(ub['type'])) f.write(' static const int _bindSlotIndex = {};\n'.format(ub['slot'])) f.write(' static const Oryol::ShaderStage::Code _bindShaderStage = Oryol::ShaderStage::{};\n'.format(stage)) - f.write(' static const uint32_t _layoutHash = {};\n'.format(getUniformBlockTypeHash(ub))) for m in ub['members']: next_offset = m['offset'] if next_offset > cur_offset: @@ -526,7 +490,7 @@ def writeProgramHeader(f, shdLib, prog, slang) : f.write(' #pragma pack(pop)\n') for tex in refl['textures']: f.write(' static const int {} = {};\n'.format(tex['name'], tex['slot'])) - f.write(' extern Oryol::ShaderSetup Setup();\n') + f.write(' extern Oryol::ShaderDesc Desc();\n') f.write('}\n') #------------------------------------------------------------------------------- @@ -546,6 +510,7 @@ def writeSourceTop(f, absSourcePath, shdLib, slang) : f.write('// #version:{}# machine generated, do not edit!\n'.format(Version)) f.write('//-----------------------------------------------------------------------------\n') f.write('#include "Pre.h"\n') + f.write('#include "Gfx/Gfx.h"\n') f.write('#include "' + hdrFile + '.h"\n') f.write('\n') if slang == 'hlsl': @@ -597,27 +562,14 @@ def writeShaderSource(f, absPath, shdLib, shd, slVersion) : else : util.fmtError("Invalid shader language id") -#------------------------------------------------------------------------------- -def writeInputVertexLayout(f, vs, slang) : - # writes a C++ VertexLayout definition into the generated source - # code, this is used to match mesh vertex layouts with - # vertex shader input signatures (e.g. required in D3D11), - # return the C++ name of the vertex layout - layoutName = '{}_input'.format(vs.name) - f.write(' Oryol::VertexLayout {};\n'.format(layoutName)) - for inp in vs.slReflection[slang]['inputs'] : - f.write(' {}.Add({}, {});\n'.format(layoutName, attrOryolName[inp['name']], attrOryolType[inp['type']])) - return layoutName - #------------------------------------------------------------------------------- def writeProgramSource(f, shdLib, prog, slangs) : - # write the Setup() function - f.write('Oryol::ShaderSetup ' + prog.name + '::Setup() {\n') - f.write(' Oryol::ShaderSetup setup("' + prog.name + '");\n') + # write the Desc() function + f.write('Oryol::ShaderDesc ' + prog.name + '::Desc() {\n') + f.write(' Oryol::ShaderDesc desc;\n') + f.write(' desc.SetLocator("' + prog.name + '");\n') vs = shdLib.vertexShaders[prog.vs] fs = shdLib.fragmentShaders[prog.fs] - vsInputLayout = writeInputVertexLayout(f, vs, slangs[0]) - f.write(' setup.SetInputLayout({});\n'.format(vsInputLayout)) vsName = vs.name fsName = fs.name for slang in slangs: @@ -625,20 +577,35 @@ def writeProgramSource(f, shdLib, prog, slangs) : vsSource = '{}_{}_src'.format(vsName, slang) fsSource = '{}_{}_src'.format(fsName, slang) if isGLSL(slang): - f.write(' setup.SetProgramFromSources({}, {}, {});\n'.format( - slangType, vsSource, fsSource)); + f.write(' if (Oryol::Gfx::QueryShaderLang() == {}) {{\n'.format(slangType)) + f.write(' desc.SetSource(Oryol::ShaderStage::VS, {});\n'.format(vsSource)) + f.write(' desc.SetSource(Oryol::ShaderStage::FS, {});\n'.format(fsSource)) + f.write(' }\n') elif isHLSL(slang): vs_c_name = '{}_vs_hlsl5'.format(vs.name) fs_c_name = '{}_fs_hlsl5'.format(fs.name) - f.write(' setup.SetProgramFromByteCode({}, {}, sizeof({}), {}, sizeof({}));\n'.format( - slangType, vs_c_name, vs_c_name, fs_c_name, fs_c_name)) + f.write(' desc.SetByteCode(Oryol::ShaderStage::VS, {}, sizeof({}));\n'.format(vs_c_name, vs_c_name)) + f.write(' desc.SetByteCode(Oryol::ShaderStage::FS, {}, sizeof({}));\n'.format(fs_c_name, fs_c_name)) elif isMetal(slang): vs_c_name = '{}_vs_metallib'.format(vs.name) fs_c_name = '{}_fs_metallib'.format(fs.name) - f.write(' setup.SetProgramFromByteCode({}, {}, sizeof({}), {}, sizeof({}), "main0", "main0");\n'.format( - slangType, vs_c_name, vs_c_name, fs_c_name, fs_c_name)) - - # add uniform layouts to setup object + f.write(' desc.SetByteCode(Oryol::ShaderStage::VS, {}, sizeof({}));\n'.format(vs_c_name, vs_c_name)) + f.write(' desc.SetByteCode(Oryol::ShaderStage::FS, {}, sizeof({}));\n'.format(fs_c_name, fs_c_name)) + f.write(' desc.SetEntry(Oryol::ShaderStage::VS, "main0");\n') + f.write(' desc.SetEntry(Oryol::ShaderStage::FS, "main0");\n') + slang = slangs[0] + + # add vertex shader input layout + vs_inputs = shdLib.vertexShaders[prog.vs].slReflection[slang]['inputs'] + # get an order inputs array by slot + ord_vs_inputs = [None]*len(vs_inputs) + for attr in vs_inputs: + ord_vs_inputs[attr['slot']] = attr + vs_inputs = ord_vs_inputs + for attr in vs_inputs: + f.write(' desc.SetAttr("{}", Oryol::{});\n'.format(attr['name'], attrOryolType[attr['type']])) + + # add uniform block layouts to desc object for stage in ['VS', 'FS']: shd = shdLib.vertexShaders[prog.vs] if stage == 'VS' else shdLib.fragmentShaders[prog.fs] refl = shd.slReflection[slang] @@ -647,12 +614,13 @@ def writeProgramSource(f, shdLib, prog, slangs) : ub_size = ub['size'] if 'glsl' in slang: ub_size = roundup(ub_size, 16) - f.write(' setup.AddUniformBlock("{}", "{}", {}, {}, {}::_bindShaderStage, {}::_bindSlotIndex);\n'.format( - ub['type'], ub['name'], getUniformBlockTypeHash(ub), ub_size, ub['type'], ub['type'])) - # add textures layouts to setup objects + f.write(' desc.SetUniformBlock(Oryol::ShaderStage::{}, {}, "{}", "{}", {});\n'.format( + stage, ub['slot'], ub['name'], ub['type'], ub_size)) + # add textures layouts to desc objects for tex in refl['textures']: - f.write(' setup.AddTexture("{}", {}, Oryol::ShaderStage::{}, {});\n'.format(tex['name'], texOryolType[tex['type']], stage, tex['slot'])) - f.write(' return setup;\n') + f.write(' desc.SetTexture(Oryol::ShaderStage::{}, {}, "{}", {});\n'.format( + stage, tex['slot'], tex['name'], texOryolType[tex['type']])) + f.write(' return desc;\n') f.write('}\n') #------------------------------------------------------------------------------- diff --git a/fips-files/verbs/webpage.py b/fips-files/verbs/webpage.py index 5a4045129..d3c5560d6 100644 --- a/fips-files/verbs/webpage.py +++ b/fips-files/verbs/webpage.py @@ -18,16 +18,6 @@ # webpage template arguments GitHubSamplesURL = 'https://github.com/floooh/oryol/tree/master/code/Samples/' -DocTitle = 'Oryol Core Samples' -Title = 'Oryol' -Subtitle = 'core samples' -# Separator = 'rainbow-separator' -# GameSeparator = 'game-rainbow-separator' -# BackgroundColor = '#19A3FF' # this is the original bright blue - -Separator = 'simple-separator' -GameSeparator = 'simple-separator' -BackgroundColor = '#42A5F5' # build configuration EmscConfig = 'webgl2-emsc-ninja-release' @@ -52,7 +42,7 @@ def deploy_webpage(fips_dir, proj_dir, webpage_dir) : content = '' if ExtensionSamples : content = '
\n' - content += '
To Extension Samples...
\n' + content += '
Ext Samples...
\n' content += '
\n' content += '
\n' @@ -85,19 +75,19 @@ def deploy_webpage(fips_dir, proj_dir, webpage_dir) : # populate the html template, and write to the build directory with open(proj_dir + '/web/index.html', 'r') as f : templ = Template(f.read()) - html = templ.safe_substitute(doctitle=DocTitle, title=Title, subtitle=Subtitle, samples=content, separator=Separator) + html = templ.safe_substitute(samples=content) with open(webpage_dir + '/index.html', 'w') as f : f.write(html) # and the same with the CSS template with open(proj_dir + '/web/style.css', 'r') as f : templ = Template(f.read()) - css = templ.safe_substitute(background=BackgroundColor) + css = templ.safe_substitute() with open(webpage_dir +'/style.css', 'w') as f : f.write(css) # copy other required files - for name in ['dummy.jpg', 'emsc.js', 'wasm.js', 'about.html', 'favicon.png', 'ext_samples.jpg'] : + for name in ['dummy.jpg', 'emsc.js', 'favicon.png', 'ext_samples.jpg'] : log.info('> copy file: {}'.format(name)) shutil.copy(proj_dir + '/web/' + name, webpage_dir + '/' + name) @@ -108,14 +98,14 @@ def deploy_webpage(fips_dir, proj_dir, webpage_dir) : name = sample['name'] if name != '__end__' and 'emscripten' in sample['type'] : log.info('> generate emscripten HTML page: {}'.format(name)) - for ext in ['js', 'html.mem'] : + for ext in ['js'] : src_path = '{}/{}.{}'.format(emsc_deploy_dir, name, ext) if os.path.isfile(src_path) : shutil.copy(src_path, '{}/asmjs/'.format(webpage_dir)) with open(proj_dir + '/web/emsc.html', 'r') as f : templ = Template(f.read()) src_url = GitHubSamplesURL + sample['src']; - html = templ.safe_substitute(name=name, source=src_url, separator=GameSeparator) + html = templ.safe_substitute(name=name, source=src_url) with open('{}/asmjs/{}.html'.format(webpage_dir, name, name), 'w') as f : f.write(html) @@ -130,14 +120,14 @@ def deploy_webpage(fips_dir, proj_dir, webpage_dir) : src_path = '{}/{}.{}'.format(wasm_deploy_dir, name, ext) if os.path.isfile(src_path) : shutil.copy(src_path, '{}/wasm/'.format(webpage_dir)) - for ext in ['html.mem', 'wasm'] : + for ext in ['wasm'] : src_path = '{}/{}.{}'.format(wasm_deploy_dir, name, ext) if os.path.isfile(src_path) : shutil.copy(src_path, '{}/wasm/{}.{}.txt'.format(webpage_dir, name, ext)) with open(proj_dir + '/web/wasm.html', 'r') as f : templ = Template(f.read()) src_url = GitHubSamplesURL + sample['src']; - html = templ.safe_substitute(name=name, source=src_url, separator=GameSeparator) + html = templ.safe_substitute(name=name, source=src_url) with open('{}/wasm/{}.html'.format(webpage_dir, name), 'w') as f : f.write(html) diff --git a/tools/linux/oryol-shdc b/tools/linux/oryol-shdc index 36ef42240..3a206672b 100755 Binary files a/tools/linux/oryol-shdc and b/tools/linux/oryol-shdc differ diff --git a/tools/osx/oryol-shdc b/tools/osx/oryol-shdc index b808cfb33..6f2552b69 100755 Binary files a/tools/osx/oryol-shdc and b/tools/osx/oryol-shdc differ diff --git a/tools/win32/oryol-shdc.exe b/tools/win32/oryol-shdc.exe index 9aec5a0bb..f83f53ed2 100644 Binary files a/tools/win32/oryol-shdc.exe and b/tools/win32/oryol-shdc.exe differ diff --git a/web/about.html b/web/about.html deleted file mode 100644 index 0f2d058ec..000000000 --- a/web/about.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -Oryol About - - - - - -
ORYOL 2000©
-
- -
-Oryol is a very experimental, multiplatform, MIT-licensed, 3D engine, written in C++11. -

-The focus is on web and mobile platforms, and on providing a very lean general purpose engine -for realtime 3D apps. -

-A word of warning: The samples on this page use bleeding-edge browser features and thus -may accordingly require the bleeding-edge versions of browsers (like -Firefox Nightly or -Chrome Canary). -

-Enjoy! -
-
- - diff --git a/web/emsc.html b/web/emsc.html index 0b29b639d..22e91444c 100644 --- a/web/emsc.html +++ b/web/emsc.html @@ -3,31 +3,16 @@ ${name} - - -
${name}asm.js
-
- -