../dali/dali-test-suite-utils/test-graphics-command-buffer.cpp
../dali/dali-test-suite-utils/test-graphics-controller.cpp
../dali/dali-test-suite-utils/test-graphics-pipeline.cpp
+ ../dali/dali-test-suite-utils/test-graphics-program.cpp
../dali/dali-test-suite-utils/test-graphics-reflection.cpp
- ../dali/dali-test-suite-utils/test-graphics-texture.cpp
../dali/dali-test-suite-utils/test-graphics-sampler.cpp
+ ../dali/dali-test-suite-utils/test-graphics-shader.cpp
+ ../dali/dali-test-suite-utils/test-graphics-texture.cpp
../dali/dali-test-suite-utils/test-native-image.cpp
../dali/dali-test-suite-utils/test-platform-abstraction.cpp
../dali/dali-test-suite-utils/test-render-controller.cpp
dali-test-suite-utils/test-graphics-command-buffer.cpp
dali-test-suite-utils/test-graphics-controller.cpp
dali-test-suite-utils/test-graphics-pipeline.cpp
+ dali-test-suite-utils/test-graphics-program.cpp
dali-test-suite-utils/test-graphics-reflection.cpp
+ dali-test-suite-utils/test-graphics-shader.cpp
dali-test-suite-utils/test-graphics-texture.cpp
dali-test-suite-utils/test-graphics-sampler.cpp
dali-test-suite-utils/test-native-image.cpp
return geometry;
}
+Property::Map CreateModelVertexFormat()
+{
+ Property::Map modelVF;
+ modelVF["aPosition"] = Property::VECTOR3;
+ modelVF["aNormal"] = Property::VECTOR3;
+ modelVF["aTexCoord1"] = Property::VECTOR3;
+ modelVF["aTexCoord2"] = Property::VECTOR3;
+ modelVF["aBoneIndex[0]"] = Property::INTEGER;
+ modelVF["aBoneIndex[1]"] = Property::INTEGER;
+ modelVF["aBoneIndex[2]"] = Property::INTEGER;
+ modelVF["aBoneIndex[3]"] = Property::INTEGER;
+ modelVF["aBoneWeights[0]"] = Property::FLOAT;
+ modelVF["aBoneWeights[1]"] = Property::FLOAT;
+ modelVF["aBoneWeights[2]"] = Property::FLOAT;
+ modelVF["aBoneWeights[3]"] = Property::FLOAT;
+ return modelVF;
+}
+
+Geometry CreateModelGeometry(Property::Map& vf)
+{
+ VertexBuffer vertexData = VertexBuffer::New(vf);
+
+ struct Vertex
+ {
+ Vector3 position;
+ Vector3 diffuseTexCoords;
+ Vector3 metalRoughTexCoords;
+ int boneIndices[4];
+ float boneWeights[4];
+ };
+
+ Vertex verts[30];
+ vertexData.SetData(verts, 30);
+ unsigned short indexData[40];
+
+ Geometry geometry = Geometry::New();
+ geometry.AddVertexBuffer(vertexData);
+ geometry.SetIndexBuffer(indexData, sizeof(indexData) / sizeof(indexData[0]));
+
+ return geometry;
+}
+
} // namespace Dali
#define MESH_BUILDER_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace Dali
{
-Shader CreateShader();
-TextureSet CreateTextureSet();
-TextureSet CreateTextureSet(Texture texture);
-Geometry CreateQuadGeometry();
-VertexBuffer CreateVertexBuffer();
+Shader CreateShader();
+TextureSet CreateTextureSet();
+TextureSet CreateTextureSet(Texture texture);
+Geometry CreateQuadGeometry();
+Geometry CreateModelGeometry(Property::Map& vf);
+VertexBuffer CreateVertexBuffer();
+Property::Map CreateModelVertexFormat();
} // namespace Dali
#include "test-graphics-command-buffer.h"
#include "test-graphics-reflection.h"
#include "test-graphics-sampler.h"
+#include "test-graphics-shader.h"
#include "test-graphics-texture.h"
#include <dali/integration-api/gl-defines.h>
namedParams["submitInfo"] << "cmdBuffer[" << submitInfo.cmdBuffer.size()
<< "], flags:" << std::hex << submitInfo.flags;
- mCallStack.PushCall("Controller::SubmitCommandBuffers", "", namedParams);
+ mCallStack.PushCall("SubmitCommandBuffers", "", namedParams);
+
+ mSubmitStack.emplace_back(submitInfo);
for(auto& graphicsCommandBuffer : submitInfo.cmdBuffer)
{
{
TraceCallStack::NamedParams namedParams;
namedParams["renderTarget"] << std::hex << renderTarget;
- mCallStack.PushCall("Controller::PresentRenderTarget", "", namedParams);
+ mCallStack.PushCall("PresentRenderTarget", "", namedParams);
}
/**
*/
void TestGraphicsController::WaitIdle()
{
- mCallStack.PushCall("Controller::WaitIdle", "");
+ mCallStack.PushCall("WaitIdle", "");
}
/**
*/
void TestGraphicsController::Pause()
{
- mCallStack.PushCall("Controller::Pause", "");
+ mCallStack.PushCall("Pause", "");
}
/**
*/
void TestGraphicsController::Resume()
{
- mCallStack.PushCall("Controller::Resume", "");
+ mCallStack.PushCall("Resume", "");
}
void TestGraphicsController::UpdateTextures(const std::vector<Graphics::TextureUpdateInfo>& updateInfoList,
namedParams["updateInfoList"] << "[" << updateInfoList.size() << "]:";
namedParams["sourceList"] << "[" << sourceList.size() << "]:";
- mCallStack.PushCall("Controller::UpdateTextures", "", namedParams);
+ mCallStack.PushCall("UpdateTextures", "", namedParams);
// Call either TexImage2D or TexSubImage2D
for(unsigned int i = 0; i < updateInfoList.size(); ++i)
TraceCallStack::NamedParams namedParams;
namedParams["enableDepth"] << (enableDepth ? "T" : "F");
namedParams["enableStencil"] << (enableStencil ? "T" : "F");
- mCallStack.PushCall("Controller::EnableDepthStencilBuffer", "", namedParams);
+ mCallStack.PushCall("EnableDepthStencilBuffer", "", namedParams);
return false;
}
{
TraceCallStack::NamedParams namedParams;
namedParams["numberOfDiscardedRenderers"] << numberOfDiscardedRenderers;
- mCallStack.PushCall("Controller::RunGarbageCollector", "", namedParams);
+ mCallStack.PushCall("RunGarbageCollector", "", namedParams);
}
void TestGraphicsController::DiscardUnusedResources()
{
- mCallStack.PushCall("Controller::DiscardUnusedResources", "");
+ mCallStack.PushCall("DiscardUnusedResources", "");
}
bool TestGraphicsController::IsDiscardQueueEmpty()
{
- mCallStack.PushCall("Controller::IsDiscardQueueEmpty", "");
+ mCallStack.PushCall("IsDiscardQueueEmpty", "");
return isDiscardQueueEmptyResult;
}
*/
bool TestGraphicsController::IsDrawOnResumeRequired()
{
- mCallStack.PushCall("Controller::IsDrawOnResumeRequired", "");
+ mCallStack.PushCall("IsDrawOnResumeRequired", "");
return isDrawOnResumeRequiredResult;
}
{
std::ostringstream oss;
oss << "bufferCreateInfo:" << createInfo;
- mCallStack.PushCall("Controller::CreateBuffer", oss.str());
+ mCallStack.PushCall("CreateBuffer", oss.str());
return Graphics::MakeUnique<TestGraphicsBuffer>(mCallStack, mGl, createInfo.size, createInfo.usage);
}
{
std::ostringstream oss;
oss << "commandBufferCreateInfo:" << commandBufferCreateInfo;
- mCallStack.PushCall("Controller::CreateCommandBuffer", oss.str());
+ mCallStack.PushCall("CreateCommandBuffer", oss.str());
return Graphics::MakeUnique<TestGraphicsCommandBuffer>(mCommandBufferCallStack, mGl);
}
Graphics::UniquePtr<Graphics::RenderPass> TestGraphicsController::CreateRenderPass(const Graphics::RenderPassCreateInfo& renderPassCreateInfo, Graphics::UniquePtr<Graphics::RenderPass>&& oldRenderPass)
{
- mCallStack.PushCall("Controller::CreateRenderPass", "");
+ mCallStack.PushCall("CreateRenderPass", "");
return nullptr;
}
{
TraceCallStack::NamedParams namedParams;
namedParams["textureCreateInfo"] << textureCreateInfo;
- mCallStack.PushCall("Controller::CreateTexture", namedParams.str(), namedParams);
+ mCallStack.PushCall("CreateTexture", namedParams.str(), namedParams);
return Graphics::MakeUnique<TestGraphicsTexture>(mGl, textureCreateInfo);
}
Graphics::UniquePtr<Graphics::Framebuffer> TestGraphicsController::CreateFramebuffer(const Graphics::FramebufferCreateInfo& framebufferCreateInfo, Graphics::UniquePtr<Graphics::Framebuffer>&& oldFramebuffer)
{
- mCallStack.PushCall("Controller::CreateFramebuffer", "");
+ mCallStack.PushCall("CreateFramebuffer", "");
return nullptr;
}
Graphics::UniquePtr<Graphics::Pipeline> TestGraphicsController::CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
{
- mCallStack.PushCall("Controller::CreatePipeline", "");
+ mCallStack.PushCall("CreatePipeline", "");
return std::make_unique<TestGraphicsPipeline>(mGl, pipelineCreateInfo);
}
+Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram)
+{
+ mCallStack.PushCall("CreateProgram", "");
+ return Graphics::MakeUnique<TestGraphicsProgram>(mGl, programCreateInfo, mVertexFormats);
+}
+
Graphics::UniquePtr<Graphics::Shader> TestGraphicsController::CreateShader(const Graphics::ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader)
{
- mCallStack.PushCall("Controller::CreateShader", "");
- return nullptr;
+ mCallStack.PushCall("CreateShader", "");
+ return Graphics::MakeUnique<TestGraphicsShader>(mGl, shaderCreateInfo);
}
Graphics::UniquePtr<Graphics::Sampler> TestGraphicsController::CreateSampler(const Graphics::SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr<Graphics::Sampler>&& oldSampler)
{
TraceCallStack::NamedParams namedParams;
namedParams["samplerCreateInfo"] << samplerCreateInfo;
- mCallStack.PushCall("Controller::CreateSampler", namedParams.str(), namedParams);
+ mCallStack.PushCall("CreateSampler", namedParams.str(), namedParams);
return Graphics::MakeUnique<TestGraphicsSampler>(mGl, samplerCreateInfo);
}
Graphics::UniquePtr<Graphics::RenderTarget> TestGraphicsController::CreateRenderTarget(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo, Graphics::UniquePtr<Graphics::RenderTarget>&& oldRenderTarget)
{
- mCallStack.PushCall("Controller::CreateRenderTarget", "");
+ mCallStack.PushCall("CreateRenderTarget", "");
return nullptr;
}
Graphics::UniquePtr<Graphics::Memory> TestGraphicsController::MapBufferRange(const Graphics::MapBufferInfo& mapInfo)
{
- mCallStack.PushCall("Controller::MapBufferRange", "");
+ mCallStack.PushCall("MapBufferRange", "");
auto buffer = static_cast<TestGraphicsBuffer*>(mapInfo.buffer);
buffer->memory.resize(mapInfo.offset + mapInfo.size); // For initial testing, allow writes past capacity
Graphics::UniquePtr<Graphics::Memory> TestGraphicsController::MapTextureRange(const Graphics::MapTextureInfo& mapInfo)
{
- mCallStack.PushCall("Controller::MapTextureRange", "");
+ mCallStack.PushCall("MapTextureRange", "");
return nullptr;
}
void TestGraphicsController::UnmapMemory(Graphics::UniquePtr<Graphics::Memory> memory)
{
- mCallStack.PushCall("Controller::UnmapMemory", "");
+ mCallStack.PushCall("UnmapMemory", "");
}
Graphics::MemoryRequirements TestGraphicsController::GetTextureMemoryRequirements(Graphics::Texture& texture) const
{
- mCallStack.PushCall("Controller::GetTextureMemoryRequirements", "");
+ mCallStack.PushCall("GetTextureMemoryRequirements", "");
return Graphics::MemoryRequirements{};
}
Graphics::MemoryRequirements TestGraphicsController::GetBufferMemoryRequirements(Graphics::Buffer& buffer) const
{
- mCallStack.PushCall("Controller::GetBufferMemoryRequirements", "");
+ mCallStack.PushCall("GetBufferMemoryRequirements", "");
return Graphics::MemoryRequirements{};
}
const Graphics::TextureProperties& TestGraphicsController::GetTextureProperties(const Graphics::Texture& texture)
{
static Graphics::TextureProperties textureProperties{};
- mCallStack.PushCall("Controller::GetTextureProperties", "");
+ mCallStack.PushCall("GetTextureProperties", "");
return textureProperties;
}
-const Graphics::Reflection& TestGraphicsController::GetPipelineReflection(const Graphics::Pipeline& pipeline)
+const Graphics::Reflection& TestGraphicsController::GetProgramReflection(const Graphics::Program& program)
{
- static TestGraphicsReflection reflection(mGl);
- mCallStack.PushCall("Controller::GetPipelineReflection", "");
+ mCallStack.PushCall("GetProgramReflection", "");
- return reflection;
+ return static_cast<const TestGraphicsProgram*>(&program)->GetReflection();
}
bool TestGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const
{
- mCallStack.PushCall("Controller::PipelineEquals", "");
+ mCallStack.PushCall("PipelineEquals", "");
return false;
}
Graphics::UniquePtr<Graphics::Pipeline> CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline) override;
/**
+ * @brief Creates new Program object
+ *
+ * @param[in] programCreateInfo The valid ProgramCreateInfo structure
+ * @return pointer to the Program object
+ */
+ Graphics::UniquePtr<Graphics::Program> CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram) override;
+
+ /**
* @brief Creates new Shader object
*
* @param[in] shaderCreateInfo The valid ShaderCreateInfo structure
const Graphics::TextureProperties& GetTextureProperties(const Graphics::Texture& texture) override;
/**
- * @brief Returns the reflection of the given pipeline
+ * @brief Returns the reflection of the given program
*
- * @param[in] pipeline The pipeline
- * @return The reflection of the pipeline
+ * @param[in] program The program
+ * @return The reflection of the program
*/
- const Graphics::Reflection& GetPipelineReflection(const Graphics::Pipeline& pipeline) override;
+ const Graphics::Reflection& GetProgramReflection(const Graphics::Program& program) override;
/**
* @brief Tests whether two Pipelines are the same.
*/
bool PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const override;
+public: // Test Functions
+ void SetVertexFormats(Property::Array& vfs)
+ {
+ mVertexFormats = vfs;
+ }
+
+ void ClearSubmitStack()
+ {
+ mSubmitStack.clear();
+ }
+
public:
- mutable TraceCallStack mCallStack;
- mutable TraceCallStack mCommandBufferCallStack;
+ mutable TraceCallStack mCallStack;
+ mutable TraceCallStack mCommandBufferCallStack;
+ mutable std::vector<Graphics::SubmitInfo> mSubmitStack;
TestGlAbstraction mGl;
TestGlSyncAbstraction mGlSyncAbstraction;
bool isDiscardQueueEmptyResult{true};
bool isDrawOnResumeRequiredResult{true};
+
+ Property::Array mVertexFormats;
};
} // namespace Dali
if(createInfo.colorBlendState)
colorBlendState = *createInfo.colorBlendState;
- if(createInfo.shaderState)
- shaderState = *createInfo.shaderState;
+ if(createInfo.programState)
+ programState = *createInfo.programState;
if(createInfo.viewportState)
viewportState = *createInfo.viewportState;
#include <dali/graphics-api/graphics-pipeline-create-info.h>
#include <dali/graphics-api/graphics-pipeline.h>
#include "test-gl-abstraction.h"
+#include "test-graphics-program.h"
+#include "test-graphics-reflection.h"
namespace Dali
{
+class TestGraphicsReflection;
+
+template<typename T>
+T* Uncast(const Graphics::Program* object)
+{
+ return const_cast<T*>(static_cast<const T*>(object));
+}
+
class TestGraphicsPipeline : public Graphics::Pipeline
{
public:
TestGraphicsPipeline(TestGlAbstraction& gl, const Graphics::PipelineCreateInfo& createInfo);
+ const TestGraphicsReflection& GetReflection() const
+ {
+ return Uncast<TestGraphicsProgram>(programState.program)->GetReflection();
+ }
+
+public:
TestGlAbstraction& mGl;
Graphics::ColorBlendState colorBlendState;
- std::vector<Graphics::ShaderState> shaderState;
+ Graphics::ProgramState programState;
Graphics::ViewportState viewportState;
Graphics::FramebufferState framebufferState;
Graphics::Pipeline basePipeline;
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test-graphics-program.h"
+
+namespace Dali
+{
+TestGraphicsProgram::TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats)
+: mGl(gl),
+ mCreateInfo(createInfo),
+ mReflection(gl, vertexFormats)
+{
+}
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TEST_GRAPHICS_PROGRAM_H
+#define DALI_TEST_GRAPHICS_PROGRAM_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+#include "test-gl-abstraction.h"
+#include "test-graphics-reflection.h"
+
+namespace Dali
+{
+class TestGraphicsProgram : public Graphics::Program
+{
+public:
+ TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats);
+
+ // For API
+ const TestGraphicsReflection& GetReflection() const
+ {
+ // Build a reflection
+ return mReflection;
+ }
+
+ // For tests
+ TestGraphicsReflection& GetProgamReflection()
+ {
+ return mReflection;
+ }
+
+public:
+ TestGlAbstraction& mGl;
+ Graphics::ProgramCreateInfo mCreateInfo;
+ TestGraphicsReflection mReflection;
+};
+
+} // namespace Dali
+
+#endif //DALI_TEST_GRAPHICS_PROGRAM_H
*/
#include "test-graphics-reflection.h"
+#include <dali/public-api/object/property-map.h>
namespace Dali
{
-TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl)
+TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, Property::Array& vfs)
: mGl(gl)
{
+ for(Property::Array::SizeType i = 0; i < vfs.Count(); ++i)
+ {
+ Property::Map* vertexFormat = vfs[i].GetMap();
+ if(vertexFormat)
+ {
+ for(Property::Map::SizeType j = 0; j < vertexFormat->Count(); ++j)
+ {
+ auto key = vertexFormat->GetKeyAt(j);
+ if(key.type == Property::Key::STRING)
+ {
+ mAttributes.push_back(key.stringKey);
+ }
+ }
+ }
+ }
}
uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& name) const
{
+ // Automatically assign locations to named attributes when requested
+ auto iter = std::find(mAttributes.begin(), mAttributes.end(), name);
+ if(iter != mAttributes.end())
+ {
+ return iter - mAttributes.begin();
+ }
+ else
+ {
+ uint32_t location = mAttributes.size();
+ mAttributes.push_back(name);
+ return location;
+ }
return 0u;
}
std::vector<uint32_t> TestGraphicsReflection::GetVertexAttributeLocations() const
{
- return std::vector<uint32_t>{};
+ std::vector<uint32_t> locs;
+ for(uint32_t i = 0; i < mAttributes.size(); ++i)
+ {
+ locs.push_back(i);
+ }
+ return locs;
}
uint32_t TestGraphicsReflection::GetUniformBlockCount() const
class TestGraphicsReflection : public Graphics::Reflection
{
public:
- TestGraphicsReflection(TestGlAbstraction& gl);
+ TestGraphicsReflection(TestGlAbstraction& gl, Property::Array& vertexFormats);
uint32_t GetVertexAttributeLocation(const std::string& name) const override;
Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
std::vector<Dali::Graphics::UniformInfo> GetSamplers() const override;
Graphics::ShaderLanguage GetLanguage() const override;
- TestGlAbstraction& mGl;
+public: // Test methods
+ void SetAttributes(std::vector<std::string> locations)
+ {
+ mAttributes.clear();
+ mAttributes.resize(locations.size());
+ for(auto& location : locations)
+ {
+ mAttributes.push_back(location);
+ }
+ }
+
+ TestGlAbstraction& mGl;
+ mutable std::vector<std::string> mAttributes;
};
} // namespace Dali
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test-graphics-shader.h"
+
+namespace Dali
+{
+TestGraphicsShader::TestGraphicsShader(TestGlAbstraction& gl, const Graphics::ShaderCreateInfo& createInfo)
+: mGl(gl),
+ mCreateInfo(createInfo)
+{
+}
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TEST_GRAPHICS_SHADER_H
+#define DALI_TEST_GRAPHICS_SHADER_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/graphics-api/graphics-shader-create-info.h>
+#include <dali/graphics-api/graphics-shader.h>
+#include "test-gl-abstraction.h"
+
+namespace Dali
+{
+class TestGraphicsShader : public Graphics::Shader
+{
+public:
+ TestGraphicsShader(TestGlAbstraction& gl, const Graphics::ShaderCreateInfo& createInfo);
+
+public:
+ TestGlAbstraction& mGl;
+ Graphics::ShaderCreateInfo mCreateInfo;
+};
+
+} // namespace Dali
+
+#endif //DALI_TEST_GRAPHICS_SHADER_H
#include <dali-test-suite-utils.h>
#include <mesh-builder.h>
#include <test-trace-call-stack.h>
+#include "test-graphics-command-buffer.h"
using namespace Dali;
DALI_TEST_CHECK(cmdBufCallstack.FindMethod("BindTextures"));
END_TEST;
}
+
+int UtcDaliRendererPreparePipeline(void)
+{
+ TestApplication application;
+
+ tet_infoline("Test that rendering an actor binds the attributes locs from the reflection");
+
+ Property::Map vf = CreateModelVertexFormat();
+ Geometry modelGeometry = CreateModelGeometry(vf);
+ Shader shader = Shader::New("vertexSrc", "fragmentSrc");
+ Renderer renderer = Renderer::New(modelGeometry, shader);
+ Actor actor = Actor::New();
+
+ // Change the order up to get a fair test
+ Property::Map modelVF;
+ modelVF["aBoneIndex[0]"] = Property::INTEGER;
+ modelVF["aBoneIndex[1]"] = Property::INTEGER;
+ modelVF["aBoneIndex[2]"] = Property::INTEGER;
+ modelVF["aBoneIndex[3]"] = Property::INTEGER;
+ modelVF["aBoneWeights[0]"] = Property::FLOAT;
+ modelVF["aBoneWeights[1]"] = Property::FLOAT;
+ modelVF["aBoneWeights[2]"] = Property::FLOAT;
+ modelVF["aBoneWeights[3]"] = Property::FLOAT;
+ modelVF["aPosition"] = Property::VECTOR3;
+ modelVF["aNormal"] = Property::VECTOR3;
+ modelVF["aTexCoord1"] = Property::VECTOR3;
+ modelVF["aTexCoord2"] = Property::VECTOR3;
+
+ Property::Array vfs;
+ vfs.PushBack(modelVF);
+ TestGraphicsController& graphics = application.GetGraphicsController();
+ graphics.SetVertexFormats(vfs);
+
+ actor.AddRenderer(renderer);
+ actor.SetProperty(Actor::Property::SIZE, Vector2(400.0f, 400.0f));
+ actor.SetProperty(Actor::Property::COLOR, Color::WHITE);
+ application.GetScene().Add(actor);
+
+ TraceCallStack& cmdBufCallstack = graphics.mCommandBufferCallStack;
+ TraceCallStack& graphicsCallstack = graphics.mCallStack;
+ cmdBufCallstack.Enable(true);
+ graphicsCallstack.Enable(true);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK(graphicsCallstack.FindMethod("SubmitCommandBuffers"));
+ std::vector<Graphics::SubmitInfo>& submissions = graphics.mSubmitStack;
+ DALI_TEST_EQUALS(submissions.size(), 1, TEST_LOCATION);
+ DALI_TEST_EQUALS(submissions[0].cmdBuffer.size(), 1, TEST_LOCATION);
+ const TestGraphicsCommandBuffer* cmdBuf = static_cast<TestGraphicsCommandBuffer*>((submissions[0].cmdBuffer[0]));
+ auto pipeline = cmdBuf->mPipeline;
+ if(pipeline)
+ {
+ DALI_TEST_EQUALS(pipeline->vertexInputState.attributes.size(), 12, TEST_LOCATION);
+ DALI_TEST_EQUALS(pipeline->vertexInputState.attributes[3].location, // 4th requested attr: aTexCoord2
+ 11,
+ TEST_LOCATION);
+ DALI_TEST_EQUALS(pipeline->vertexInputState.attributes[3].format, // 4th requested attr: aTexCoord2
+ Graphics::VertexInputFormat::FVECTOR3,
+ TEST_LOCATION);
+ }
+
+ END_TEST;
+}
${DALI_TEST_SUITE_DIR}/test-graphics-texture.cpp
${DALI_TEST_SUITE_DIR}/test-graphics-buffer.cpp
${DALI_TEST_SUITE_DIR}/test-graphics-pipeline.cpp
+ ${DALI_TEST_SUITE_DIR}/test-graphics-program.cpp
+ ${DALI_TEST_SUITE_DIR}/test-graphics-shader.cpp
${DALI_TEST_SUITE_DIR}/test-graphics-reflection.cpp
${DALI_TEST_SUITE_DIR}/test-graphics-command-buffer.cpp
${DALI_TEST_SUITE_DIR}/test-platform-abstraction.cpp
${graphics_src_dir}/graphics-framebuffer-create-info.h
${graphics_src_dir}/graphics-framebuffer.h
${graphics_src_dir}/graphics-memory.h
- ${graphics_src_dir}/graphics-pipeline-create-info.h
+ ${graphics_src_dir}/graphics-pipeline-create-info.h
${graphics_src_dir}/graphics-pipeline.h
+ ${graphics_src_dir}/graphics-program-create-info.h
+ ${graphics_src_dir}/graphics-program.h
${graphics_src_dir}/graphics-reflection.h
${graphics_src_dir}/graphics-render-pass-create-info.h
${graphics_src_dir}/graphics-render-pass.h
#include "graphics-framebuffer-create-info.h"
#include "graphics-memory.h"
#include "graphics-pipeline-create-info.h"
+#include "graphics-program-create-info.h"
#include "graphics-reflection.h"
#include "graphics-render-pass-create-info.h"
#include "graphics-render-target-create-info.h"
* @return pointer to the Pipeline object
*/
virtual UniquePtr<Pipeline> CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, UniquePtr<Pipeline>&& oldPipeline) = 0;
+
+ /**
+ * @brief Creates new Program object
+ *
+ * @param[in] ProgramCreateInfo The valid ProgramCreateInfo structure
+ * @param[in] oldProgram The valid pointer to the old object or nullptr. The object will be reused or destroyed.
+ * @return pointer to the Program object
+ */
+ virtual UniquePtr<Program> CreateProgram(const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram) = 0;
+
/**
* @brief Creates new Shader object
*
virtual const TextureProperties& GetTextureProperties(const Texture& texture) = 0;
/**
- * @brief Returns the reflection of the given pipeline
+ * @brief Returns the reflection of the given program
*
- * @param[in] pipeline The pipeline
- * @return The reflection of the pipeline
+ * @param[in] program The program
+ * @return The reflection of the program
*/
- virtual const Reflection& GetPipelineReflection(const Pipeline& pipeline) = 0;
+ virtual const Reflection& GetProgramReflection(const Program& program) = 0;
/**
* @brief Tests whether two Pipelines are the same.
}
/**
- * @brief Sets the color blend state
- * param[in] pointer to valid color blend state structure
- * @return reference to this structure
+ * @brief Set the program
+ *
+ * @param[in] value
+ * @return a reference to this structure
*/
- auto& SetColorBlendState(ColorBlendState* value)
+ auto& SetProgramState(ProgramState* value)
{
- colorBlendState = value;
+ programState = value;
return *this;
}
/**
- * @brief Sets the shader state for the pipeline
- *
- * The function takes an array of shader states in order to compile
- * the pipeline. Each ShaderState structure determines the pipeline stage
- * the shader should be executed on. The Shader object may be already created
- * with a specific stage. Then the ShaderState::inheritPipelineStage must be
- * set to true.
- *
- * Sample:
- * SetShaderState( { ShaderState().SetShader( vertexShader)
- * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
- * ShaderState().SetShader( fragmentShader )
- * .SetPipelineStage( PipelineStage::FRAGMENT_SHADER )
- * } );
- *
- * In modern graphics API it is possible to attach more than one Shader to a single
- * stage. For example, one Shader may be just a library of functions:
- * SetShaderState( { ShaderState().SetShader( vertexShader)
- * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
- * ShaderState().SetShader( shaderCommons )
- * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
- * ShaderState().SetShader( fragmentShader )
- * .SetPipelineStage( PipelineStage::FRAGMENT_SHADER )
- * } );
- *
- * The Pipeline will compile and link all given shaders.
- *
- * param[in] value Valid array of shder states
+ * @brief Sets the color blend state
+ * @param[in] value pointer to valid color blend state structure
* @return reference to this structure
*/
- auto& SetShaderState(const std::vector<ShaderState>& value)
+ auto& SetColorBlendState(ColorBlendState* value)
{
- shaderState = &value;
+ colorBlendState = value;
return *this;
}
/**
- * @brief Sets the viewport state
- * param[in] pointer to valid viewport state structure
+ * @brief Sets the viewport state.
+ *
+ * @param[in] value pointer to valid viewport state structure
* @return reference to this structure
*/
auto& SetViewportState(ViewportState* value)
}
/**
- * @brief Sets the framebuffer state
- * param[in] pointer to valid framebuffer state structure
+ * @brief Sets the framebuffer state.
+ *
+ * @param[in] value pointer to valid framebuffer state structure
* @return reference to this structure
*/
auto& SetFramebufferState(FramebufferState* value)
}
/**
- * @brief Sets the base pipeline
+ * @brief Sets the base pipeline.
*
* Setting base pipeline allows inheriting that pipeline state
* and build the new pipeline from it. The base pipeline
* must stay valid until derived pipeline needs it.
*
- * param[in] pointer to valid pipeline object
+ * @param[in] value pointer to valid pipeline object
* @return reference to this structure
*/
auto& SetBasePipeline(Pipeline* value)
}
/**
- * @brief Sets the depth/stencil state
- * param[in] pointer to valid depth/stencil state structure
+ * @brief Sets the depth/stencil state.
+ *
+ * @param[in] pointer to valid depth/stencil state structure
* @return reference to this structure
*/
auto& SetDepthStencilState(DepthStencilState* value)
}
/**
- * @brief Sets the rasterization state
- * param[in] pointer to valid rasterization state structure
+ * @brief Sets the rasterization state.
+ *
+ * @param[in] pointer to valid rasterization state structure
* @return reference to this structure
*/
auto& SetRasterizationState(RasterizationState* value)
}
/**
- * @brief Sets the vertex input state
+ * @brief Sets the vertex input state.
*
* Vertex input state describes format of vertices and must
* be compatible with attached shaders.
*
- * param[in] pointer to vertex input state structure
+ * @param[in] pointer to vertex input state structure
* @return reference to this structure
*/
auto& SetVertexInputState(VertexInputState* value)
}
/**
- * @brief Sets the input assembly state
+ * @brief Sets the input assembly state.
*
* This state describes the topology of the pipeline.
*
- * param[in] pointer to valid input assembly state structure
+ * @param[in] pointer to valid input assembly state structure
* @return reference to this structure
*/
auto& SetInputAssemblyState(InputAssemblyState* value)
}
/**
- * @brief Sets the dynamic state mask
+ * @brief Sets the dynamic state mask.
*
* Certain states can be modified on fly without a need of
* creating new pipeline. The commands which modify particular
* states may be issued later by executing command buffers.
*
- * param[in] pointer to valid color blend state structure
+ * @param[in] pointer to valid color blend state structure
* @return reference to this structure
*/
auto& SetDynamicStateMask(PipelineDynamicStateMask value)
GraphicsStructureType type{GraphicsStructureType::PIPELINE_CREATE_INFO_STRUCT};
ExtensionCreateInfo* nextExtension{nullptr};
- ColorBlendState* colorBlendState{nullptr};
- const std::vector<ShaderState>* shaderState{nullptr};
- ViewportState* viewportState{nullptr};
- FramebufferState* framebufferState{nullptr};
- Pipeline* basePipeline{nullptr};
- DepthStencilState* depthStencilState{nullptr};
- RasterizationState* rasterizationState{nullptr};
- VertexInputState* vertexInputState{nullptr};
- InputAssemblyState* inputAssemblyState{nullptr};
- PipelineDynamicStateMask dynamicStateMask{0u};
+ ProgramState* programState{nullptr};
+ ColorBlendState* colorBlendState{nullptr};
+ ViewportState* viewportState{nullptr};
+ FramebufferState* framebufferState{nullptr};
+ Pipeline* basePipeline{nullptr};
+ DepthStencilState* depthStencilState{nullptr};
+ RasterizationState* rasterizationState{nullptr};
+ VertexInputState* vertexInputState{nullptr};
+ InputAssemblyState* inputAssemblyState{nullptr};
+ PipelineDynamicStateMask dynamicStateMask{0u};
const AllocationCallbacks* allocationCallbacks{nullptr};
};
--- /dev/null
+#ifndef DALI_GRAPHICS_PROGRAM_CREATE_INFO_H
+#define DALI_GRAPHICS_PROGRAM_CREATE_INFO_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graphics-program.h"
+#include "graphics-types.h"
+
+namespace Dali
+{
+namespace Graphics
+{
+/**
+ * This structure represents the information needed to generate a program.
+ */
+struct ProgramCreateInfo
+{
+ /**
+ * @brief Sets pointer to the extension
+ *
+ * The pointer to the extension must be set either to nullptr
+ * or to the valid structure. The structures may create
+ * a chain. The last structure in a chain must point at
+ * nullptr.
+ *
+ * @param[in] value pointer to the valid extension structure
+ * @return reference to this structure
+ */
+ auto& SetNextExtension(ExtensionCreateInfo* value)
+ {
+ nextExtension = value;
+ return *this;
+ }
+
+ /**
+ * @brief Set the allocation callbacks.
+ *
+ * @param[in] value set of allocation callbacks
+ * @return reference to this structure.
+ */
+ auto& SetAllocationCallbacks(const AllocationCallbacks& value)
+ {
+ allocationCallbacks = &value;
+ return *this;
+ }
+
+ /**
+ * @brief Sets the shader state for the program
+ *
+ * The function takes an array of shader states in order to compile
+ * the program. Each ShaderState structure determines the pipeline stage
+ * the shader should be executed on. The Shader object may be already created
+ * with a specific stage.
+ *
+ * Sample:
+ * SetShaderState( { ShaderState().SetShader( vertexShader)
+ * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
+ * ShaderState().SetShader( fragmentShader )
+ * .SetPipelineStage( PipelineStage::FRAGMENT_SHADER )
+ * } );
+ *
+ * In modern graphics API it is possible to attach more than one Shader to a single
+ * stage. For example, one Shader may be just a library of functions:
+ * SetShaderState( { ShaderState().SetShader( vertexShader)
+ * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
+ * ShaderState().SetShader( shaderCommons )
+ * .SetPipelineStage( PipelineStage::VERTEX_SHADER ),
+ * ShaderState().SetShader( fragmentShader )
+ * .SetPipelineStage( PipelineStage::FRAGMENT_SHADER )
+ * } );
+ *
+ * The Program will compile and link all given shaders.
+ *
+ * param[in] value Valid array of shder states
+ * @return reference to this structure
+ */
+ auto& SetShaderState(const std::vector<ShaderState>& value)
+ {
+ shaderState = &value;
+ return *this;
+ }
+
+ GraphicsStructureType type{GraphicsStructureType::PROGRAM_CREATE_INFO_STRUCT};
+ ExtensionCreateInfo* nextExtension{nullptr};
+
+ const std::vector<ShaderState>* shaderState{nullptr};
+ const AllocationCallbacks* allocationCallbacks{nullptr};
+};
+
+} // namespace Graphics
+
+} // namespace Dali
+
+#endif //DALI_GRAPHICS_PROGRAM_CREATE_INFO_H
--- /dev/null
+#ifndef DALI_GRAPHICS_PROGRAM_H
+#define DALI_GRAPHICS_PROGRAM_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Dali
+{
+namespace Graphics
+{
+/**
+ * @brief The Program class represents a set of shaders that can
+ * be compiled into a complete program and attached to a Pipeline.
+ * It can be used to fetch a Reflection object to determine the
+ * structure of attributes, uniforms and samplers within the shaders.
+ */
+class Program
+{
+public:
+ Program() = default;
+ virtual ~Program() = default;
+
+ // not copyable
+ Program(const Program&) = delete;
+ Program& operator=(const Program&) = delete;
+
+protected:
+ Program(Program&&) = default;
+ Program& operator=(Program&&) = default;
+};
+
+} // namespace Graphics
+
+} // namespace Dali
+
+#endif //DALI_GRAPHICS_PROGRAM_H
} // namespace Graphics
} // namespace Dali
-#endif
\ No newline at end of file
+#endif
{
namespace Graphics
{
-class CommandBuffer;
-class Texture;
class Buffer;
-class Shader;
+class CommandBuffer;
class Framebuffer;
+class Program;
+class Shader;
+class Texture;
/**
* @brief Structure describes 2D offset
};
/**
- * @brief Framebuffer state
+ * @brief Program State
+ */
+struct ProgramState
+{
+ const Program* program{nullptr};
+
+ auto& SetProgram(const Program& value)
+ {
+ program = &value;
+ return *this;
+ }
+};
+
+/**
+ * @brief Framebuffer state.
*/
struct FramebufferState
{
BUFFER_CREATE_INFO_STRUCT,
COMMAND_BUFFER_CREATE_INFO_STRUCT,
FRAMEBUFFER_CREATE_INFO_STRUCT,
+ PROGRAM_CREATE_INFO_STRUCT,
PIPELINE_CREATE_INFO_STRUCT,
RENDERPASS_CREATE_INFO_STRUCT,
SAMPLER_CREATE_INFO_STRUCT,
#include <dali/internal/render/renderers/render-renderer.h>
// INTERNAL INCLUDES
+#include <dali/graphics-api/graphics-program.h>
#include <dali/graphics-api/graphics-types.h>
#include <dali/internal/common/image-sampler.h>
#include <dali/internal/render/common/render-instruction.h>
Graphics::PipelineStage::FRAGMENT_SHADER,
shaderData->GetSourceMode());
- mShaderStates.clear();
- mShaderStates.push_back(Graphics::ShaderState().SetShader(vertexShader).SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER));
- mShaderStates.push_back(Graphics::ShaderState().SetShader(fragmentShader).SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER));
+ std::vector<Graphics::ShaderState> shaderStates{
+ Graphics::ShaderState().SetShader(vertexShader).SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
+ Graphics::ShaderState().SetShader(fragmentShader).SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
+ auto createInfo = Graphics::ProgramCreateInfo();
+ createInfo.SetShaderState(shaderStates);
- auto createInfo = Graphics::PipelineCreateInfo().SetShaderState(mShaderStates).SetNextExtension(&mLegacyProgram);
+ mGraphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
// Temporarily create a pipeline here - this will be used for transporting
// topology, vertex format, attrs, rasterization state
- mGraphicsPipeline = std::move(PrepareGraphicsPipeline(*program, instruction, blend, createInfo)); // WRONG: @todo FIXME. Renderer can't own a pipeline.
+ mGraphicsPipeline = std::move(PrepareGraphicsPipeline(*program, instruction, blend));
+
commandBuffer->BindPipeline(*mGraphicsPipeline.get());
if(DALI_LIKELY(BindTextures(*program, *commandBuffer.get(), boundTextures)))
Graphics::UniquePtr<Graphics::Pipeline> Renderer::PrepareGraphicsPipeline(
Program& program,
const Dali::Internal::SceneGraph::RenderInstruction& instruction,
- bool blend,
- Graphics::PipelineCreateInfo& createInfo)
+ bool blend)
{
Graphics::InputAssemblyState inputAssemblyState{};
Graphics::VertexInputState vertexInputState{};
+ Graphics::ProgramState programState{};
uint32_t bindingIndex{0u};
if(mUpdateAttributeLocations || mGeometry->AttributesChanged())
mUpdateAttributeLocations = true;
}
+ auto& reflection = mGraphicsController->GetProgramReflection(*mGraphicsProgram.get());
+
/**
* Bind Attributes
*/
{
if(mUpdateAttributeLocations)
{
- auto attributeName = vertexBuffer->GetAttributeName(i);
- uint32_t index = program.RegisterCustomAttribute(attributeName);
- int32_t pLocation = program.GetCustomAttributeLocation(index);
+ auto attributeName = vertexBuffer->GetAttributeName(i);
+ int32_t pLocation = reflection.GetVertexAttributeLocation(std::string(attributeName.GetStringView()));
if(-1 == pLocation)
{
DALI_LOG_WARNING("Attribute not found in the shader: %s\n", attributeName.GetCString());
// Get the topology
inputAssemblyState.SetTopology(mGeometry->GetTopology());
+ // Get the program
+ programState.SetProgram(*mGraphicsProgram.get());
+
Graphics::RasterizationState rasterizationState{};
//Set cull face mode
// Create a new pipeline
return mGraphicsController->CreatePipeline(
- createInfo
+ Graphics::PipelineCreateInfo()
.SetInputAssemblyState(&inputAssemblyState) // Passed as pointers - shallow copy will break. TOO C LIKE
.SetVertexInputState(&vertexInputState)
.SetRasterizationState(&rasterizationState)
- .SetColorBlendState(&colorBlendState),
+ .SetColorBlendState(&colorBlendState)
+ .SetProgramState(&programState)
+ .SetNextExtension(&mLegacyProgram),
nullptr);
}
Graphics::UniquePtr<Graphics::Pipeline> PrepareGraphicsPipeline(
Program& program,
const Dali::Internal::SceneGraph::RenderInstruction& instruction,
- bool blend,
- Graphics::PipelineCreateInfo& createInfo);
+ bool blend);
private:
Graphics::Controller* mGraphicsController;
Context* mContext;
Render::Geometry* mGeometry;
- ProgramCache* mProgramCache{};
- Render::ShaderCache* mShaderCache{};
- Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{}; ///< The graphics pipeline. @todo MOVE TO RenderManager
+ ProgramCache* mProgramCache{};
+ Render::ShaderCache* mShaderCache{};
+
+ Graphics::UniquePtr<Graphics::Program> mGraphicsProgram{}; ///< The graphics program. (Cached implementation)
+ Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation)
std::vector<Graphics::ShaderState> mShaderStates{};
struct UniformIndexMap