Fixed memory leaks
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-toolkit / dali-toolkit-test-utils / test-graphics-controller.cpp
index 578285b..51f0721 100644 (file)
@@ -18,7 +18,9 @@
 
 #include "test-graphics-buffer.h"
 #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>
@@ -52,6 +54,12 @@ T* Uncast(const Graphics::Buffer* object)
   return const_cast<T*>(static_cast<const T*>(object));
 }
 
+template<typename T>
+T* Uncast(const Graphics::Shader* object)
+{
+  return const_cast<T*>(static_cast<const T*>(object));
+}
+
 std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo)
 {
   return o << "usage:" << std::hex << bufferCreateInfo.usage << ", size:" << std::dec << bufferCreateInfo.size;
@@ -172,7 +180,9 @@ public:
   : mCallStack(callStack),
     mBuffer(buffer),
     mMappedOffset(mappedOffset),
-    mMappedSize(mappedSize)
+    mMappedSize(mappedSize),
+    mLockedOffset(0u),
+    mLockedSize(0u)
   {
   }
 
@@ -318,88 +328,321 @@ GLenum GetTopology(Graphics::PrimitiveTopology topology)
   return GL_TRIANGLES;
 }
 
+GLenum GetCullFace(Graphics::CullMode cullMode)
+{
+  switch(cullMode)
+  {
+    case Graphics::CullMode::NONE:
+      return GL_NONE;
+    case Graphics::CullMode::FRONT:
+      return GL_FRONT;
+    case Graphics::CullMode::BACK:
+      return GL_BACK;
+    case Graphics::CullMode::FRONT_AND_BACK:
+      return GL_FRONT_AND_BACK;
+  }
+  return GL_NONE;
+}
+
+GLenum GetFrontFace(Graphics::FrontFace frontFace)
+{
+  if(frontFace == Graphics::FrontFace::CLOCKWISE)
+  {
+    return GL_CW;
+  }
+  return GL_CCW;
+}
+
+GLenum GetBlendFactor(Graphics::BlendFactor blendFactor)
+{
+  GLenum glFactor = GL_ZERO;
+
+  switch(blendFactor)
+  {
+    case Graphics::BlendFactor::ZERO:
+      glFactor = GL_ZERO;
+      break;
+    case Graphics::BlendFactor::ONE:
+      glFactor = GL_ONE;
+      break;
+    case Graphics::BlendFactor::SRC_COLOR:
+      glFactor = GL_SRC_COLOR;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_SRC_COLOR:
+      glFactor = GL_ONE_MINUS_SRC_COLOR;
+      break;
+    case Graphics::BlendFactor::DST_COLOR:
+      glFactor = GL_DST_COLOR;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_DST_COLOR:
+      glFactor = GL_ONE_MINUS_DST_COLOR;
+      break;
+    case Graphics::BlendFactor::SRC_ALPHA:
+      glFactor = GL_SRC_ALPHA;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_SRC_ALPHA:
+      glFactor = GL_ONE_MINUS_SRC_ALPHA;
+      break;
+    case Graphics::BlendFactor::DST_ALPHA:
+      glFactor = GL_DST_ALPHA;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_DST_ALPHA:
+      glFactor = GL_ONE_MINUS_DST_ALPHA;
+      break;
+    case Graphics::BlendFactor::CONSTANT_COLOR:
+      glFactor = GL_CONSTANT_COLOR;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_CONSTANT_COLOR:
+      glFactor = GL_ONE_MINUS_CONSTANT_COLOR;
+      break;
+    case Graphics::BlendFactor::CONSTANT_ALPHA:
+      glFactor = GL_CONSTANT_ALPHA;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_CONSTANT_ALPHA:
+      glFactor = GL_ONE_MINUS_CONSTANT_ALPHA;
+      break;
+    case Graphics::BlendFactor::SRC_ALPHA_SATURATE:
+      glFactor = GL_SRC_ALPHA_SATURATE;
+      break;
+      // GLES doesn't appear to have dual source blending.
+    case Graphics::BlendFactor::SRC1_COLOR:
+      glFactor = GL_SRC_COLOR;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_SRC1_COLOR:
+      glFactor = GL_ONE_MINUS_SRC_COLOR;
+      break;
+    case Graphics::BlendFactor::SRC1_ALPHA:
+      glFactor = GL_SRC_ALPHA;
+      break;
+    case Graphics::BlendFactor::ONE_MINUS_SRC1_ALPHA:
+      glFactor = GL_ONE_MINUS_SRC_ALPHA;
+      break;
+  }
+  return glFactor;
+}
+
+GLenum GetBlendOp(Graphics::BlendOp blendOp)
+{
+  GLenum op = GL_FUNC_ADD;
+  switch(blendOp)
+  {
+    case Graphics::BlendOp::ADD:
+      op = GL_FUNC_ADD;
+      break;
+    case Graphics::BlendOp::SUBTRACT:
+      op = GL_FUNC_SUBTRACT;
+      break;
+    case Graphics::BlendOp::REVERSE_SUBTRACT:
+      op = GL_FUNC_REVERSE_SUBTRACT;
+      break;
+    case Graphics::BlendOp::MIN:
+      op = GL_MIN;
+      break;
+    case Graphics::BlendOp::MAX:
+      op = GL_MAX;
+      break;
+
+      // @todo Add advanced blend equations
+  }
+  return op;
+}
+
 void TestGraphicsController::SubmitCommandBuffers(const Graphics::SubmitInfo& submitInfo)
 {
   TraceCallStack::NamedParams namedParams;
   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)
   {
     auto commandBuffer = Uncast<TestGraphicsCommandBuffer>(graphicsCommandBuffer);
-    for(auto& binding : commandBuffer->mTextureBindings)
+
+    auto value = commandBuffer->GetCommandsByType(0 | CommandType::BIND_TEXTURES);
+    if(!value.empty())
     {
-      if(binding.texture)
+      // must be fixed
+      for(auto& binding : value[0]->data.bindTextures.textureBindings)
       {
-        auto texture = Uncast<TestGraphicsTexture>(binding.texture);
+        if(binding.texture)
+        {
+          auto texture = Uncast<TestGraphicsTexture>(binding.texture);
 
-        texture->Bind(binding.binding);
+          texture->Bind(binding.binding);
 
-        if(binding.sampler)
-        {
-          auto sampler = Uncast<TestGraphicsSampler>(binding.sampler);
-          if(sampler)
+          if(binding.sampler)
           {
-            sampler->Apply(texture->GetTarget());
+            auto sampler = Uncast<TestGraphicsSampler>(binding.sampler);
+            if(sampler)
+            {
+              sampler->Apply(texture->GetTarget());
+            }
           }
-        }
 
-        texture->Prepare(); // Ensure native texture is ready
+          texture->Prepare(); // Ensure native texture is ready
+        }
       }
     }
 
     // IndexBuffer binding,
-    auto& indexBufferBinding = commandBuffer->mIndexBufferBinding;
-    if(indexBufferBinding.buffer)
+    auto bindIndexBufferCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_INDEX_BUFFER);
+    if(!bindIndexBufferCmds.empty())
     {
-      auto buffer = Uncast<TestGraphicsBuffer>(indexBufferBinding.buffer);
-      buffer->Bind();
+      auto& indexBufferBinding = bindIndexBufferCmds[0]->data.bindIndexBuffer;
+      if(indexBufferBinding.buffer)
+      {
+        auto buffer = Uncast<TestGraphicsBuffer>(indexBufferBinding.buffer);
+        buffer->Bind();
+      }
     }
 
     // VertexBuffer binding,
-    for(auto graphicsBuffer : commandBuffer->mVertexBufferBindings.buffers)
+    auto bindVertexBufferCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_VERTEX_BUFFERS);
+    if(!bindVertexBufferCmds.empty())
     {
-      auto vertexBuffer = Uncast<TestGraphicsBuffer>(graphicsBuffer);
-      vertexBuffer->Bind();
+      for(auto& binding : bindVertexBufferCmds[0]->data.bindVertexBuffers.vertexBufferBindings)
+      {
+        auto graphicsBuffer = binding.buffer;
+        auto vertexBuffer   = Uncast<TestGraphicsBuffer>(graphicsBuffer);
+        vertexBuffer->Bind();
+      }
     }
 
-    // Pipeline attribute setup
-    auto& vi = commandBuffer->mPipeline->vertexInputState;
-    for(auto& attribute : vi.attributes)
+    bool scissorEnabled = false;
+
+    auto scissorTestList = commandBuffer->GetCommandsByType(0 | CommandType::SET_SCISSOR_TEST);
+    if(!scissorTestList.empty())
     {
-      mGl.EnableVertexAttribArray(attribute.location);
-      uint32_t attributeOffset = attribute.offset;
-      GLsizei  stride          = vi.bufferBindings[attribute.binding].stride;
-
-      mGl.VertexAttribPointer(attribute.location,
-                              GetNumComponents(attribute.format),
-                              GetGlType(attribute.format),
-                              GL_FALSE, // Not normalized
-                              stride,
-                              reinterpret_cast<void*>(attributeOffset));
+      if(scissorTestList[0]->data.scissorTest.enable)
+      {
+        mGl.Enable(GL_SCISSOR_TEST);
+        scissorEnabled = true;
+      }
+      else
+      {
+        mGl.Disable(GL_SCISSOR_TEST);
+      }
     }
 
-    // draw call
-    auto topology = commandBuffer->mPipeline->inputAssemblyState.topology;
-
-    if(commandBuffer->drawCommand.drawType == TestGraphicsCommandBuffer::Draw::DrawType::Indexed)
+    auto scissorList = commandBuffer->GetCommandsByType(0 | CommandType::SET_SCISSOR);
+    if(!scissorList.empty() && scissorEnabled)
     {
-      mGl.DrawElements(GetTopology(topology),
-                       static_cast<GLsizei>(commandBuffer->drawCommand.u.indexedDraw.indexCount),
-                       GL_UNSIGNED_SHORT,
-                       reinterpret_cast<void*>(commandBuffer->drawCommand.u.indexedDraw.firstIndex));
+      auto& rect = scissorList[0]->data.scissor.region;
+      mGl.Scissor(rect.x, rect.y, rect.width, rect.height);
     }
-    else
+
+    auto viewportList = commandBuffer->GetCommandsByType(0 | CommandType::SET_VIEWPORT);
+    if(!viewportList.empty())
     {
-      mGl.DrawArrays(GetTopology(topology), 0, commandBuffer->drawCommand.u.unindexedDraw.vertexCount);
+      mGl.Viewport(viewportList[0]->data.viewport.region.x, viewportList[0]->data.viewport.region.y, viewportList[0]->data.viewport.region.width, viewportList[0]->data.viewport.region.height);
     }
 
-    // attribute clear
-    for(auto& attribute : vi.attributes)
+    // ignore viewport enable
+
+    // Pipeline attribute setup
+    auto bindPipelineCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_PIPELINE);
+    if(!bindPipelineCmds.empty())
     {
-      mGl.DisableVertexAttribArray(attribute.location);
+      auto  pipeline = bindPipelineCmds[0]->data.bindPipeline.pipeline;
+      auto& vi       = pipeline->vertexInputState;
+      for(auto& attribute : vi.attributes)
+      {
+        mGl.EnableVertexAttribArray(attribute.location);
+        uint32_t attributeOffset = attribute.offset;
+        GLsizei  stride          = vi.bufferBindings[attribute.binding].stride;
+
+        mGl.VertexAttribPointer(attribute.location,
+                                GetNumComponents(attribute.format),
+                                GetGlType(attribute.format),
+                                GL_FALSE, // Not normalized
+                                stride,
+                                reinterpret_cast<void*>(attributeOffset));
+      }
+
+      // Cull face setup
+      auto& rasterizationState = pipeline->rasterizationState;
+      if(rasterizationState.cullMode == Graphics::CullMode::NONE)
+      {
+        mGl.Disable(GL_CULL_FACE);
+      }
+      else
+      {
+        mGl.Enable(GL_CULL_FACE);
+        mGl.CullFace(GetCullFace(rasterizationState.cullMode));
+      }
+
+      mGl.FrontFace(GetFrontFace(rasterizationState.frontFace));
+      // We don't modify glPolygonMode in our context/abstraction from GL_FILL (the GL default),
+      // so it isn't present in the API (and won't have any tests!)
+
+      // Blending setup
+      auto& colorBlendState = pipeline->colorBlendState;
+      if(colorBlendState.blendEnable)
+      {
+        mGl.Enable(GL_BLEND);
+
+        mGl.BlendFuncSeparate(GetBlendFactor(colorBlendState.srcColorBlendFactor),
+                              GetBlendFactor(colorBlendState.dstColorBlendFactor),
+                              GetBlendFactor(colorBlendState.srcAlphaBlendFactor),
+                              GetBlendFactor(colorBlendState.dstAlphaBlendFactor));
+        if(colorBlendState.colorBlendOp != colorBlendState.alphaBlendOp)
+        {
+          mGl.BlendEquationSeparate(GetBlendOp(colorBlendState.colorBlendOp), GetBlendOp(colorBlendState.alphaBlendOp));
+        }
+        else
+        {
+          mGl.BlendEquation(GetBlendOp(colorBlendState.colorBlendOp));
+        }
+        mGl.BlendColor(colorBlendState.blendConstants[0],
+                       colorBlendState.blendConstants[1],
+                       colorBlendState.blendConstants[2],
+                       colorBlendState.blendConstants[3]);
+      }
+      else
+      {
+        mGl.Disable(GL_BLEND);
+      }
+
+      // draw call
+      auto topology = pipeline->inputAssemblyState.topology;
+
+      // UniformBuffer binding (once we know pipeline)
+      auto bindUniformBuffersCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_UNIFORM_BUFFER);
+      if(!bindUniformBuffersCmds.empty())
+      {
+        auto buffer = bindUniformBuffersCmds[0]->data.bindUniformBuffers.standaloneUniformsBufferBinding;
+
+        // based on reflection, issue gl calls
+        buffer.buffer->BindAsUniformBuffer(static_cast<const TestGraphicsProgram*>(pipeline->programState.program));
+      }
+
+      auto drawCmds = commandBuffer->GetCommandsByType(0 |
+                                                       CommandType::DRAW |
+                                                       CommandType::DRAW_INDEXED_INDIRECT |
+                                                       CommandType::DRAW_INDEXED);
+
+      if(!drawCmds.empty())
+      {
+        if(drawCmds[0]->data.draw.type == DrawCallDescriptor::Type::DRAW_INDEXED)
+        {
+          mGl.DrawElements(GetTopology(topology),
+                           static_cast<GLsizei>(drawCmds[0]->data.draw.drawIndexed.indexCount),
+                           GL_UNSIGNED_SHORT,
+                           reinterpret_cast<void*>(drawCmds[0]->data.draw.drawIndexed.firstIndex));
+        }
+        else
+        {
+          mGl.DrawArrays(GetTopology(topology), 0, drawCmds[0]->data.draw.draw.vertexCount);
+        }
+      }
+      // attribute clear
+      for(auto& attribute : vi.attributes)
+      {
+        mGl.DisableVertexAttribArray(attribute.location);
+      }
     }
   }
 }
@@ -412,7 +655,7 @@ void TestGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderT
 {
   TraceCallStack::NamedParams namedParams;
   namedParams["renderTarget"] << std::hex << renderTarget;
-  mCallStack.PushCall("Controller::PresentRenderTarget", "", namedParams);
+  mCallStack.PushCall("PresentRenderTarget", "", namedParams);
 }
 
 /**
@@ -420,7 +663,7 @@ void TestGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderT
  */
 void TestGraphicsController::WaitIdle()
 {
-  mCallStack.PushCall("Controller::WaitIdle", "");
+  mCallStack.PushCall("WaitIdle", "");
 }
 
 /**
@@ -428,7 +671,7 @@ void TestGraphicsController::WaitIdle()
  */
 void TestGraphicsController::Pause()
 {
-  mCallStack.PushCall("Controller::Pause", "");
+  mCallStack.PushCall("Pause", "");
 }
 
 /**
@@ -436,7 +679,17 @@ void TestGraphicsController::Pause()
  */
 void TestGraphicsController::Resume()
 {
-  mCallStack.PushCall("Controller::Resume", "");
+  mCallStack.PushCall("Resume", "");
+}
+
+void TestGraphicsController::Shutdown()
+{
+  mCallStack.PushCall("Shutdown", "");
+}
+
+void TestGraphicsController::Destroy()
+{
+  mCallStack.PushCall("Destroy", "");
 }
 
 void TestGraphicsController::UpdateTextures(const std::vector<Graphics::TextureUpdateInfo>&       updateInfoList,
@@ -446,7 +699,7 @@ void TestGraphicsController::UpdateTextures(const std::vector<Graphics::TextureU
   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)
@@ -465,7 +718,7 @@ bool TestGraphicsController::EnableDepthStencilBuffer(bool enableDepth, bool ena
   TraceCallStack::NamedParams namedParams;
   namedParams["enableDepth"] << (enableDepth ? "T" : "F");
   namedParams["enableStencil"] << (enableStencil ? "T" : "F");
-  mCallStack.PushCall("Controller::EnableDepthStencilBuffer", "", namedParams);
+  mCallStack.PushCall("EnableDepthStencilBuffer", "", namedParams);
   return false;
 }
 
@@ -473,17 +726,17 @@ void TestGraphicsController::RunGarbageCollector(size_t numberOfDiscardedRendere
 {
   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;
 }
 
@@ -494,7 +747,7 @@ bool TestGraphicsController::IsDiscardQueueEmpty()
  */
 bool TestGraphicsController::IsDrawOnResumeRequired()
 {
-  mCallStack.PushCall("Controller::IsDrawOnResumeRequired", "");
+  mCallStack.PushCall("IsDrawOnResumeRequired", "");
   return isDrawOnResumeRequiredResult;
 }
 
@@ -502,7 +755,7 @@ Graphics::UniquePtr<Graphics::Buffer> TestGraphicsController::CreateBuffer(const
 {
   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);
 }
 
@@ -510,13 +763,13 @@ Graphics::UniquePtr<Graphics::CommandBuffer> TestGraphicsController::CreateComma
 {
   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;
 }
 
@@ -524,47 +777,84 @@ Graphics::UniquePtr<Graphics::Texture> TestGraphicsController::CreateTexture(con
 {
   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", "");
+
+  for(auto cacheEntry : mProgramCache)
+  {
+    bool found = true;
+    for(auto& shader : *(programCreateInfo.shaderState))
+    {
+      auto                 graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
+      std::vector<uint8_t> source;
+      source.resize(graphicsShader->mCreateInfo.sourceSize);
+      memcpy(&source[0], graphicsShader->mCreateInfo.sourceData, graphicsShader->mCreateInfo.sourceSize);
+
+      if(!std::equal(source.begin(), source.end(), cacheEntry.shaders[shader.pipelineStage].begin()))
+      {
+        found = false;
+        break;
+      }
+    }
+    if(found)
+    {
+      return Graphics::MakeUnique<TestGraphicsProgram>(cacheEntry.programImpl);
+    }
+  }
+
+  mProgramCache.emplace_back();
+  mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms);
+  for(auto& shader : *(programCreateInfo.shaderState))
+  {
+    auto graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
+    mProgramCache.back().shaders[shader.pipelineStage].resize(graphicsShader->mCreateInfo.sourceSize);
+    memcpy(&mProgramCache.back().shaders[shader.pipelineStage][0], graphicsShader->mCreateInfo.sourceData, graphicsShader->mCreateInfo.sourceSize);
+  }
+  return Graphics::MakeUnique<TestGraphicsProgram>(mProgramCache.back().programImpl);
+}
+
 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
@@ -574,39 +864,53 @@ Graphics::UniquePtr<Graphics::Memory> TestGraphicsController::MapBufferRange(con
 
 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::GetProgramReflection(const Graphics::Program& program)
+{
+  mCallStack.PushCall("GetProgramReflection", "");
+
+  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;
 }
 
+bool TestGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
+{
+  mCallStack.PushCall("GetProgramParameter", "");
+  auto graphicsProgram = Uncast<TestGraphicsProgram>(&program);
+  return graphicsProgram->GetParameter(parameterId, outData);
+}
+
 } // namespace Dali