51f0721432e4e1cd728b3a2769d944ab1139b006
[platform/core/uifw/dali-toolkit.git] / automated-tests / src / dali-toolkit / dali-toolkit-test-utils / test-graphics-controller.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "test-graphics-controller.h"
18
19 #include "test-graphics-buffer.h"
20 #include "test-graphics-command-buffer.h"
21 #include "test-graphics-reflection.h"
22 #include "test-graphics-sampler.h"
23 #include "test-graphics-shader.h"
24 #include "test-graphics-texture.h"
25
26 #include <dali/integration-api/gl-defines.h>
27 #include <cstdio>
28 #include <iostream>
29 #include <sstream>
30
31 namespace Dali
32 {
33 template<typename T>
34 T* Uncast(const Graphics::CommandBuffer* object)
35 {
36   return const_cast<T*>(static_cast<const T*>(object));
37 }
38
39 template<typename T>
40 T* Uncast(const Graphics::Texture* object)
41 {
42   return const_cast<T*>(static_cast<const T*>(object));
43 }
44
45 template<typename T>
46 T* Uncast(const Graphics::Sampler* object)
47 {
48   return const_cast<T*>(static_cast<const T*>(object));
49 }
50
51 template<typename T>
52 T* Uncast(const Graphics::Buffer* object)
53 {
54   return const_cast<T*>(static_cast<const T*>(object));
55 }
56
57 template<typename T>
58 T* Uncast(const Graphics::Shader* object)
59 {
60   return const_cast<T*>(static_cast<const T*>(object));
61 }
62
63 std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo)
64 {
65   return o << "usage:" << std::hex << bufferCreateInfo.usage << ", size:" << std::dec << bufferCreateInfo.size;
66 }
67
68 std::ostream& operator<<(std::ostream& o, const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo)
69 {
70   return o << "level:" << (commandBufferCreateInfo.level == Graphics::CommandBufferLevel::PRIMARY ? "PRIMARY" : "SECONDARY")
71            << ", fixedCapacity:" << std::dec << commandBufferCreateInfo.fixedCapacity;
72 }
73
74 std::ostream& operator<<(std::ostream& o, const Graphics::TextureType& textureType)
75 {
76   switch(textureType)
77   {
78     case Graphics::TextureType::TEXTURE_2D:
79       o << "TEXTURE_2D";
80       break;
81     case Graphics::TextureType::TEXTURE_3D:
82       o << "TEXTURE_3D";
83       break;
84     case Graphics::TextureType::TEXTURE_CUBEMAP:
85       o << "TEXTURE_CUBEMAP";
86       break;
87   }
88   return o;
89 }
90
91 std::ostream& operator<<(std::ostream& o, const Graphics::Extent2D extent)
92 {
93   o << "width:" << extent.width << ", height:" << extent.height;
94   return o;
95 }
96
97 std::ostream& operator<<(std::ostream& o, const Graphics::TextureCreateInfo& createInfo)
98 {
99   o << "textureType:" << createInfo.textureType
100     << " size:" << createInfo.size
101     << " format:" << static_cast<uint32_t>(createInfo.format)
102     << " mipMapFlag:" << createInfo.mipMapFlag
103     << " layout:" << (createInfo.layout == Graphics::TextureLayout::LINEAR ? "LINEAR" : "OPTIMAL")
104     << " usageFlags:" << std::hex << createInfo.usageFlags
105     << " data:" << std::hex << createInfo.data
106     << " dataSize:" << std::dec << createInfo.dataSize
107     << " nativeImagePtr:" << std::hex << createInfo.nativeImagePtr;
108   return o;
109 }
110
111 std::ostream& operator<<(std::ostream& o, Graphics::SamplerAddressMode addressMode)
112 {
113   switch(addressMode)
114   {
115     case Graphics::SamplerAddressMode::REPEAT:
116       o << "REPEAT";
117       break;
118     case Graphics::SamplerAddressMode::MIRRORED_REPEAT:
119       o << "MIRRORED_REPEAT";
120       break;
121     case Graphics::SamplerAddressMode::CLAMP_TO_EDGE:
122       o << "CLAMP_TO_EDGE";
123       break;
124     case Graphics::SamplerAddressMode::CLAMP_TO_BORDER:
125       o << "CLAMP_TO_BORDER";
126       break;
127     case Graphics::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE:
128       o << "MIRROR_CLAMP_TO_EDGE";
129       break;
130   }
131   return o;
132 }
133
134 std::ostream& operator<<(std::ostream& o, Graphics::SamplerFilter filterMode)
135 {
136   switch(filterMode)
137   {
138     case Graphics::SamplerFilter::LINEAR:
139       o << "LINEAR";
140       break;
141     case Graphics::SamplerFilter::NEAREST:
142       o << "NEAREST";
143       break;
144   }
145   return o;
146 }
147
148 std::ostream& operator<<(std::ostream& o, Graphics::SamplerMipmapMode mipmapMode)
149 {
150   switch(mipmapMode)
151   {
152     case Graphics::SamplerMipmapMode::NONE:
153       o << "NONE";
154       break;
155     case Graphics::SamplerMipmapMode::LINEAR:
156       o << "LINEAR";
157       break;
158     case Graphics::SamplerMipmapMode::NEAREST:
159       o << "NEAREST";
160       break;
161   }
162   return o;
163 }
164
165 std::ostream& operator<<(std::ostream& o, const Graphics::SamplerCreateInfo& createInfo)
166 {
167   o << "minFilter:" << createInfo.minFilter
168     << " magFilter:" << createInfo.magFilter
169     << " wrapModeU:" << createInfo.addressModeU
170     << " wrapModeV:" << createInfo.addressModeV
171     << " wrapModeW:" << createInfo.addressModeW
172     << " mipMapMode:" << createInfo.mipMapMode;
173   return o;
174 }
175
176 class TestGraphicsMemory : public Graphics::Memory
177 {
178 public:
179   TestGraphicsMemory(TraceCallStack& callStack, TestGraphicsBuffer& buffer, uint32_t mappedOffset, uint32_t mappedSize)
180   : mCallStack(callStack),
181     mBuffer(buffer),
182     mMappedOffset(mappedOffset),
183     mMappedSize(mappedSize),
184     mLockedOffset(0u),
185     mLockedSize(0u)
186   {
187   }
188
189   void* LockRegion(uint32_t offset, uint32_t size) override
190   {
191     std::ostringstream o;
192     o << offset << ", " << size;
193     mCallStack.PushCall("Memory::LockRegion", o.str());
194
195     if(offset > mMappedOffset + mMappedSize ||
196        size + offset > mMappedOffset + mMappedSize)
197     {
198       fprintf(stderr, "TestGraphics.Memory::LockRegion() Out of bounds");
199       mBuffer.memory.resize(mMappedOffset + offset + size); // Grow to prevent memcpy from crashing
200     }
201     mLockedOffset = offset;
202     mLockedSize   = size;
203     return &mBuffer.memory[mMappedOffset + offset];
204   }
205
206   void Unlock(bool flush) override
207   {
208     mCallStack.PushCall("Memory::Unlock", (flush ? "Flush" : "NoFlush"));
209     if(flush)
210     {
211       Flush();
212     }
213   }
214
215   void Flush() override
216   {
217     mCallStack.PushCall("Memory::Flush", "");
218     mBuffer.Bind();
219     mBuffer.Upload(mMappedOffset + mLockedOffset, mLockedSize);
220     mBuffer.Unbind();
221   }
222
223   TraceCallStack&     mCallStack;
224   TestGraphicsBuffer& mBuffer;
225   uint32_t            mMappedOffset;
226   uint32_t            mMappedSize;
227   uint32_t            mLockedOffset;
228   uint32_t            mLockedSize;
229 };
230
231 TestGraphicsController::TestGraphicsController()
232 : mCallStack(true, "TestGraphicsController."),
233   mCommandBufferCallStack(true, "TestCommandBuffer.")
234 {
235   mCallStack.Enable(true);
236   mCommandBufferCallStack.Enable(true);
237   auto& trace = mGl.GetTextureTrace();
238   trace.Enable(true);
239   trace.EnableLogging(true);
240 }
241
242 int GetNumComponents(Graphics::VertexInputFormat vertexFormat)
243 {
244   switch(vertexFormat)
245   {
246     case Graphics::VertexInputFormat::UNDEFINED:
247     case Graphics::VertexInputFormat::FLOAT:
248     case Graphics::VertexInputFormat::INTEGER:
249       return 1;
250     case Graphics::VertexInputFormat::IVECTOR2:
251     case Graphics::VertexInputFormat::FVECTOR2:
252       return 2;
253     case Graphics::VertexInputFormat::IVECTOR3:
254     case Graphics::VertexInputFormat::FVECTOR3:
255       return 3;
256     case Graphics::VertexInputFormat::FVECTOR4:
257     case Graphics::VertexInputFormat::IVECTOR4:
258       return 4;
259   }
260   return 1;
261 }
262
263 GLint GetSize(Graphics::VertexInputFormat vertexFormat)
264 {
265   switch(vertexFormat)
266   {
267     case Graphics::VertexInputFormat::UNDEFINED:
268       return 1u;
269     case Graphics::VertexInputFormat::INTEGER:
270     case Graphics::VertexInputFormat::IVECTOR2:
271     case Graphics::VertexInputFormat::IVECTOR3:
272     case Graphics::VertexInputFormat::IVECTOR4:
273       return 2u;
274     case Graphics::VertexInputFormat::FLOAT:
275     case Graphics::VertexInputFormat::FVECTOR2:
276     case Graphics::VertexInputFormat::FVECTOR3:
277     case Graphics::VertexInputFormat::FVECTOR4:
278       return 4u;
279   }
280   return 1u;
281 }
282
283 GLint GetGlType(Graphics::VertexInputFormat vertexFormat)
284 {
285   switch(vertexFormat)
286   {
287     case Graphics::VertexInputFormat::UNDEFINED:
288       return GL_BYTE;
289     case Graphics::VertexInputFormat::INTEGER:
290     case Graphics::VertexInputFormat::IVECTOR2:
291     case Graphics::VertexInputFormat::IVECTOR3:
292     case Graphics::VertexInputFormat::IVECTOR4:
293       return GL_SHORT;
294     case Graphics::VertexInputFormat::FLOAT:
295     case Graphics::VertexInputFormat::FVECTOR2:
296     case Graphics::VertexInputFormat::FVECTOR3:
297     case Graphics::VertexInputFormat::FVECTOR4:
298       return GL_FLOAT;
299   }
300   return GL_BYTE;
301 }
302
303 GLenum GetTopology(Graphics::PrimitiveTopology topology)
304 {
305   switch(topology)
306   {
307     case Graphics::PrimitiveTopology::POINT_LIST:
308       return GL_POINTS;
309
310     case Graphics::PrimitiveTopology::LINE_LIST:
311       return GL_LINES;
312
313     case Graphics::PrimitiveTopology::LINE_LOOP:
314       return GL_LINE_LOOP;
315
316     case Graphics::PrimitiveTopology::LINE_STRIP:
317       return GL_LINE_STRIP;
318
319     case Graphics::PrimitiveTopology::TRIANGLE_LIST:
320       return GL_TRIANGLES;
321
322     case Graphics::PrimitiveTopology::TRIANGLE_STRIP:
323       return GL_TRIANGLE_STRIP;
324
325     case Graphics::PrimitiveTopology::TRIANGLE_FAN:
326       return GL_TRIANGLE_FAN;
327   }
328   return GL_TRIANGLES;
329 }
330
331 GLenum GetCullFace(Graphics::CullMode cullMode)
332 {
333   switch(cullMode)
334   {
335     case Graphics::CullMode::NONE:
336       return GL_NONE;
337     case Graphics::CullMode::FRONT:
338       return GL_FRONT;
339     case Graphics::CullMode::BACK:
340       return GL_BACK;
341     case Graphics::CullMode::FRONT_AND_BACK:
342       return GL_FRONT_AND_BACK;
343   }
344   return GL_NONE;
345 }
346
347 GLenum GetFrontFace(Graphics::FrontFace frontFace)
348 {
349   if(frontFace == Graphics::FrontFace::CLOCKWISE)
350   {
351     return GL_CW;
352   }
353   return GL_CCW;
354 }
355
356 GLenum GetBlendFactor(Graphics::BlendFactor blendFactor)
357 {
358   GLenum glFactor = GL_ZERO;
359
360   switch(blendFactor)
361   {
362     case Graphics::BlendFactor::ZERO:
363       glFactor = GL_ZERO;
364       break;
365     case Graphics::BlendFactor::ONE:
366       glFactor = GL_ONE;
367       break;
368     case Graphics::BlendFactor::SRC_COLOR:
369       glFactor = GL_SRC_COLOR;
370       break;
371     case Graphics::BlendFactor::ONE_MINUS_SRC_COLOR:
372       glFactor = GL_ONE_MINUS_SRC_COLOR;
373       break;
374     case Graphics::BlendFactor::DST_COLOR:
375       glFactor = GL_DST_COLOR;
376       break;
377     case Graphics::BlendFactor::ONE_MINUS_DST_COLOR:
378       glFactor = GL_ONE_MINUS_DST_COLOR;
379       break;
380     case Graphics::BlendFactor::SRC_ALPHA:
381       glFactor = GL_SRC_ALPHA;
382       break;
383     case Graphics::BlendFactor::ONE_MINUS_SRC_ALPHA:
384       glFactor = GL_ONE_MINUS_SRC_ALPHA;
385       break;
386     case Graphics::BlendFactor::DST_ALPHA:
387       glFactor = GL_DST_ALPHA;
388       break;
389     case Graphics::BlendFactor::ONE_MINUS_DST_ALPHA:
390       glFactor = GL_ONE_MINUS_DST_ALPHA;
391       break;
392     case Graphics::BlendFactor::CONSTANT_COLOR:
393       glFactor = GL_CONSTANT_COLOR;
394       break;
395     case Graphics::BlendFactor::ONE_MINUS_CONSTANT_COLOR:
396       glFactor = GL_ONE_MINUS_CONSTANT_COLOR;
397       break;
398     case Graphics::BlendFactor::CONSTANT_ALPHA:
399       glFactor = GL_CONSTANT_ALPHA;
400       break;
401     case Graphics::BlendFactor::ONE_MINUS_CONSTANT_ALPHA:
402       glFactor = GL_ONE_MINUS_CONSTANT_ALPHA;
403       break;
404     case Graphics::BlendFactor::SRC_ALPHA_SATURATE:
405       glFactor = GL_SRC_ALPHA_SATURATE;
406       break;
407       // GLES doesn't appear to have dual source blending.
408     case Graphics::BlendFactor::SRC1_COLOR:
409       glFactor = GL_SRC_COLOR;
410       break;
411     case Graphics::BlendFactor::ONE_MINUS_SRC1_COLOR:
412       glFactor = GL_ONE_MINUS_SRC_COLOR;
413       break;
414     case Graphics::BlendFactor::SRC1_ALPHA:
415       glFactor = GL_SRC_ALPHA;
416       break;
417     case Graphics::BlendFactor::ONE_MINUS_SRC1_ALPHA:
418       glFactor = GL_ONE_MINUS_SRC_ALPHA;
419       break;
420   }
421   return glFactor;
422 }
423
424 GLenum GetBlendOp(Graphics::BlendOp blendOp)
425 {
426   GLenum op = GL_FUNC_ADD;
427   switch(blendOp)
428   {
429     case Graphics::BlendOp::ADD:
430       op = GL_FUNC_ADD;
431       break;
432     case Graphics::BlendOp::SUBTRACT:
433       op = GL_FUNC_SUBTRACT;
434       break;
435     case Graphics::BlendOp::REVERSE_SUBTRACT:
436       op = GL_FUNC_REVERSE_SUBTRACT;
437       break;
438     case Graphics::BlendOp::MIN:
439       op = GL_MIN;
440       break;
441     case Graphics::BlendOp::MAX:
442       op = GL_MAX;
443       break;
444
445       // @todo Add advanced blend equations
446   }
447   return op;
448 }
449
450 void TestGraphicsController::SubmitCommandBuffers(const Graphics::SubmitInfo& submitInfo)
451 {
452   TraceCallStack::NamedParams namedParams;
453   namedParams["submitInfo"] << "cmdBuffer[" << submitInfo.cmdBuffer.size()
454                             << "], flags:" << std::hex << submitInfo.flags;
455
456   mCallStack.PushCall("SubmitCommandBuffers", "", namedParams);
457
458   mSubmitStack.emplace_back(submitInfo);
459
460   for(auto& graphicsCommandBuffer : submitInfo.cmdBuffer)
461   {
462     auto commandBuffer = Uncast<TestGraphicsCommandBuffer>(graphicsCommandBuffer);
463
464     auto value = commandBuffer->GetCommandsByType(0 | CommandType::BIND_TEXTURES);
465     if(!value.empty())
466     {
467       // must be fixed
468       for(auto& binding : value[0]->data.bindTextures.textureBindings)
469       {
470         if(binding.texture)
471         {
472           auto texture = Uncast<TestGraphicsTexture>(binding.texture);
473
474           texture->Bind(binding.binding);
475
476           if(binding.sampler)
477           {
478             auto sampler = Uncast<TestGraphicsSampler>(binding.sampler);
479             if(sampler)
480             {
481               sampler->Apply(texture->GetTarget());
482             }
483           }
484
485           texture->Prepare(); // Ensure native texture is ready
486         }
487       }
488     }
489
490     // IndexBuffer binding,
491     auto bindIndexBufferCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_INDEX_BUFFER);
492     if(!bindIndexBufferCmds.empty())
493     {
494       auto& indexBufferBinding = bindIndexBufferCmds[0]->data.bindIndexBuffer;
495       if(indexBufferBinding.buffer)
496       {
497         auto buffer = Uncast<TestGraphicsBuffer>(indexBufferBinding.buffer);
498         buffer->Bind();
499       }
500     }
501
502     // VertexBuffer binding,
503     auto bindVertexBufferCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_VERTEX_BUFFERS);
504     if(!bindVertexBufferCmds.empty())
505     {
506       for(auto& binding : bindVertexBufferCmds[0]->data.bindVertexBuffers.vertexBufferBindings)
507       {
508         auto graphicsBuffer = binding.buffer;
509         auto vertexBuffer   = Uncast<TestGraphicsBuffer>(graphicsBuffer);
510         vertexBuffer->Bind();
511       }
512     }
513
514     bool scissorEnabled = false;
515
516     auto scissorTestList = commandBuffer->GetCommandsByType(0 | CommandType::SET_SCISSOR_TEST);
517     if(!scissorTestList.empty())
518     {
519       if(scissorTestList[0]->data.scissorTest.enable)
520       {
521         mGl.Enable(GL_SCISSOR_TEST);
522         scissorEnabled = true;
523       }
524       else
525       {
526         mGl.Disable(GL_SCISSOR_TEST);
527       }
528     }
529
530     auto scissorList = commandBuffer->GetCommandsByType(0 | CommandType::SET_SCISSOR);
531     if(!scissorList.empty() && scissorEnabled)
532     {
533       auto& rect = scissorList[0]->data.scissor.region;
534       mGl.Scissor(rect.x, rect.y, rect.width, rect.height);
535     }
536
537     auto viewportList = commandBuffer->GetCommandsByType(0 | CommandType::SET_VIEWPORT);
538     if(!viewportList.empty())
539     {
540       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);
541     }
542
543     // ignore viewport enable
544
545     // Pipeline attribute setup
546     auto bindPipelineCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_PIPELINE);
547     if(!bindPipelineCmds.empty())
548     {
549       auto  pipeline = bindPipelineCmds[0]->data.bindPipeline.pipeline;
550       auto& vi       = pipeline->vertexInputState;
551       for(auto& attribute : vi.attributes)
552       {
553         mGl.EnableVertexAttribArray(attribute.location);
554         uint32_t attributeOffset = attribute.offset;
555         GLsizei  stride          = vi.bufferBindings[attribute.binding].stride;
556
557         mGl.VertexAttribPointer(attribute.location,
558                                 GetNumComponents(attribute.format),
559                                 GetGlType(attribute.format),
560                                 GL_FALSE, // Not normalized
561                                 stride,
562                                 reinterpret_cast<void*>(attributeOffset));
563       }
564
565       // Cull face setup
566       auto& rasterizationState = pipeline->rasterizationState;
567       if(rasterizationState.cullMode == Graphics::CullMode::NONE)
568       {
569         mGl.Disable(GL_CULL_FACE);
570       }
571       else
572       {
573         mGl.Enable(GL_CULL_FACE);
574         mGl.CullFace(GetCullFace(rasterizationState.cullMode));
575       }
576
577       mGl.FrontFace(GetFrontFace(rasterizationState.frontFace));
578       // We don't modify glPolygonMode in our context/abstraction from GL_FILL (the GL default),
579       // so it isn't present in the API (and won't have any tests!)
580
581       // Blending setup
582       auto& colorBlendState = pipeline->colorBlendState;
583       if(colorBlendState.blendEnable)
584       {
585         mGl.Enable(GL_BLEND);
586
587         mGl.BlendFuncSeparate(GetBlendFactor(colorBlendState.srcColorBlendFactor),
588                               GetBlendFactor(colorBlendState.dstColorBlendFactor),
589                               GetBlendFactor(colorBlendState.srcAlphaBlendFactor),
590                               GetBlendFactor(colorBlendState.dstAlphaBlendFactor));
591         if(colorBlendState.colorBlendOp != colorBlendState.alphaBlendOp)
592         {
593           mGl.BlendEquationSeparate(GetBlendOp(colorBlendState.colorBlendOp), GetBlendOp(colorBlendState.alphaBlendOp));
594         }
595         else
596         {
597           mGl.BlendEquation(GetBlendOp(colorBlendState.colorBlendOp));
598         }
599         mGl.BlendColor(colorBlendState.blendConstants[0],
600                        colorBlendState.blendConstants[1],
601                        colorBlendState.blendConstants[2],
602                        colorBlendState.blendConstants[3]);
603       }
604       else
605       {
606         mGl.Disable(GL_BLEND);
607       }
608
609       // draw call
610       auto topology = pipeline->inputAssemblyState.topology;
611
612       // UniformBuffer binding (once we know pipeline)
613       auto bindUniformBuffersCmds = commandBuffer->GetCommandsByType(0 | CommandType::BIND_UNIFORM_BUFFER);
614       if(!bindUniformBuffersCmds.empty())
615       {
616         auto buffer = bindUniformBuffersCmds[0]->data.bindUniformBuffers.standaloneUniformsBufferBinding;
617
618         // based on reflection, issue gl calls
619         buffer.buffer->BindAsUniformBuffer(static_cast<const TestGraphicsProgram*>(pipeline->programState.program));
620       }
621
622       auto drawCmds = commandBuffer->GetCommandsByType(0 |
623                                                        CommandType::DRAW |
624                                                        CommandType::DRAW_INDEXED_INDIRECT |
625                                                        CommandType::DRAW_INDEXED);
626
627       if(!drawCmds.empty())
628       {
629         if(drawCmds[0]->data.draw.type == DrawCallDescriptor::Type::DRAW_INDEXED)
630         {
631           mGl.DrawElements(GetTopology(topology),
632                            static_cast<GLsizei>(drawCmds[0]->data.draw.drawIndexed.indexCount),
633                            GL_UNSIGNED_SHORT,
634                            reinterpret_cast<void*>(drawCmds[0]->data.draw.drawIndexed.firstIndex));
635         }
636         else
637         {
638           mGl.DrawArrays(GetTopology(topology), 0, drawCmds[0]->data.draw.draw.vertexCount);
639         }
640       }
641       // attribute clear
642       for(auto& attribute : vi.attributes)
643       {
644         mGl.DisableVertexAttribArray(attribute.location);
645       }
646     }
647   }
648 }
649
650 /**
651  * @brief Presents render target
652  * @param renderTarget render target to present
653  */
654 void TestGraphicsController::PresentRenderTarget(Graphics::RenderTarget* renderTarget)
655 {
656   TraceCallStack::NamedParams namedParams;
657   namedParams["renderTarget"] << std::hex << renderTarget;
658   mCallStack.PushCall("PresentRenderTarget", "", namedParams);
659 }
660
661 /**
662  * @brief Waits until the GPU is idle
663  */
664 void TestGraphicsController::WaitIdle()
665 {
666   mCallStack.PushCall("WaitIdle", "");
667 }
668
669 /**
670  * @brief Lifecycle pause event
671  */
672 void TestGraphicsController::Pause()
673 {
674   mCallStack.PushCall("Pause", "");
675 }
676
677 /**
678  * @brief Lifecycle resume event
679  */
680 void TestGraphicsController::Resume()
681 {
682   mCallStack.PushCall("Resume", "");
683 }
684
685 void TestGraphicsController::Shutdown()
686 {
687   mCallStack.PushCall("Shutdown", "");
688 }
689
690 void TestGraphicsController::Destroy()
691 {
692   mCallStack.PushCall("Destroy", "");
693 }
694
695 void TestGraphicsController::UpdateTextures(const std::vector<Graphics::TextureUpdateInfo>&       updateInfoList,
696                                             const std::vector<Graphics::TextureUpdateSourceInfo>& sourceList)
697 {
698   TraceCallStack::NamedParams namedParams;
699   namedParams["updateInfoList"] << "[" << updateInfoList.size() << "]:";
700   namedParams["sourceList"] << "[" << sourceList.size() << "]:";
701
702   mCallStack.PushCall("UpdateTextures", "", namedParams);
703
704   // Call either TexImage2D or TexSubImage2D
705   for(unsigned int i = 0; i < updateInfoList.size(); ++i)
706   {
707     auto& updateInfo = updateInfoList[i];
708     auto& source     = sourceList[i];
709
710     auto texture = static_cast<TestGraphicsTexture*>(updateInfo.dstTexture);
711     texture->Bind(0); // Use first texture unit during resource update
712     texture->Update(updateInfo, source);
713   }
714 }
715
716 bool TestGraphicsController::EnableDepthStencilBuffer(bool enableDepth, bool enableStencil)
717 {
718   TraceCallStack::NamedParams namedParams;
719   namedParams["enableDepth"] << (enableDepth ? "T" : "F");
720   namedParams["enableStencil"] << (enableStencil ? "T" : "F");
721   mCallStack.PushCall("EnableDepthStencilBuffer", "", namedParams);
722   return false;
723 }
724
725 void TestGraphicsController::RunGarbageCollector(size_t numberOfDiscardedRenderers)
726 {
727   TraceCallStack::NamedParams namedParams;
728   namedParams["numberOfDiscardedRenderers"] << numberOfDiscardedRenderers;
729   mCallStack.PushCall("RunGarbageCollector", "", namedParams);
730 }
731
732 void TestGraphicsController::DiscardUnusedResources()
733 {
734   mCallStack.PushCall("DiscardUnusedResources", "");
735 }
736
737 bool TestGraphicsController::IsDiscardQueueEmpty()
738 {
739   mCallStack.PushCall("IsDiscardQueueEmpty", "");
740   return isDiscardQueueEmptyResult;
741 }
742
743 /**
744  * @brief Test if the graphics subsystem has resumed & should force a draw
745  *
746  * @return true if the graphics subsystem requires a re-draw
747  */
748 bool TestGraphicsController::IsDrawOnResumeRequired()
749 {
750   mCallStack.PushCall("IsDrawOnResumeRequired", "");
751   return isDrawOnResumeRequiredResult;
752 }
753
754 Graphics::UniquePtr<Graphics::Buffer> TestGraphicsController::CreateBuffer(const Graphics::BufferCreateInfo& createInfo, Graphics::UniquePtr<Graphics::Buffer>&& oldBuffer)
755 {
756   std::ostringstream oss;
757   oss << "bufferCreateInfo:" << createInfo;
758   mCallStack.PushCall("CreateBuffer", oss.str());
759   return Graphics::MakeUnique<TestGraphicsBuffer>(mCallStack, mGl, createInfo.size, createInfo.usage);
760 }
761
762 Graphics::UniquePtr<Graphics::CommandBuffer> TestGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, Graphics::UniquePtr<Graphics::CommandBuffer>&& oldCommandBuffer)
763 {
764   std::ostringstream oss;
765   oss << "commandBufferCreateInfo:" << commandBufferCreateInfo;
766   mCallStack.PushCall("CreateCommandBuffer", oss.str());
767   return Graphics::MakeUnique<TestGraphicsCommandBuffer>(mCommandBufferCallStack, mGl);
768 }
769
770 Graphics::UniquePtr<Graphics::RenderPass> TestGraphicsController::CreateRenderPass(const Graphics::RenderPassCreateInfo& renderPassCreateInfo, Graphics::UniquePtr<Graphics::RenderPass>&& oldRenderPass)
771 {
772   mCallStack.PushCall("CreateRenderPass", "");
773   return nullptr;
774 }
775
776 Graphics::UniquePtr<Graphics::Texture> TestGraphicsController::CreateTexture(const Graphics::TextureCreateInfo& textureCreateInfo, Graphics::UniquePtr<Graphics::Texture>&& oldTexture)
777 {
778   TraceCallStack::NamedParams namedParams;
779   namedParams["textureCreateInfo"] << textureCreateInfo;
780   mCallStack.PushCall("CreateTexture", namedParams.str(), namedParams);
781
782   return Graphics::MakeUnique<TestGraphicsTexture>(mGl, textureCreateInfo);
783 }
784
785 Graphics::UniquePtr<Graphics::Framebuffer> TestGraphicsController::CreateFramebuffer(const Graphics::FramebufferCreateInfo& framebufferCreateInfo, Graphics::UniquePtr<Graphics::Framebuffer>&& oldFramebuffer)
786 {
787   mCallStack.PushCall("CreateFramebuffer", "");
788   return nullptr;
789 }
790
791 Graphics::UniquePtr<Graphics::Pipeline> TestGraphicsController::CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
792 {
793   mCallStack.PushCall("CreatePipeline", "");
794   return std::make_unique<TestGraphicsPipeline>(mGl, pipelineCreateInfo);
795 }
796
797 Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram)
798 {
799   mCallStack.PushCall("CreateProgram", "");
800
801   for(auto cacheEntry : mProgramCache)
802   {
803     bool found = true;
804     for(auto& shader : *(programCreateInfo.shaderState))
805     {
806       auto                 graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
807       std::vector<uint8_t> source;
808       source.resize(graphicsShader->mCreateInfo.sourceSize);
809       memcpy(&source[0], graphicsShader->mCreateInfo.sourceData, graphicsShader->mCreateInfo.sourceSize);
810
811       if(!std::equal(source.begin(), source.end(), cacheEntry.shaders[shader.pipelineStage].begin()))
812       {
813         found = false;
814         break;
815       }
816     }
817     if(found)
818     {
819       return Graphics::MakeUnique<TestGraphicsProgram>(cacheEntry.programImpl);
820     }
821   }
822
823   mProgramCache.emplace_back();
824   mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms);
825   for(auto& shader : *(programCreateInfo.shaderState))
826   {
827     auto graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
828     mProgramCache.back().shaders[shader.pipelineStage].resize(graphicsShader->mCreateInfo.sourceSize);
829     memcpy(&mProgramCache.back().shaders[shader.pipelineStage][0], graphicsShader->mCreateInfo.sourceData, graphicsShader->mCreateInfo.sourceSize);
830   }
831   return Graphics::MakeUnique<TestGraphicsProgram>(mProgramCache.back().programImpl);
832 }
833
834 Graphics::UniquePtr<Graphics::Shader> TestGraphicsController::CreateShader(const Graphics::ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader)
835 {
836   mCallStack.PushCall("CreateShader", "");
837   return Graphics::MakeUnique<TestGraphicsShader>(mGl, shaderCreateInfo);
838 }
839
840 Graphics::UniquePtr<Graphics::Sampler> TestGraphicsController::CreateSampler(const Graphics::SamplerCreateInfo& samplerCreateInfo, Graphics::UniquePtr<Graphics::Sampler>&& oldSampler)
841 {
842   TraceCallStack::NamedParams namedParams;
843   namedParams["samplerCreateInfo"] << samplerCreateInfo;
844   mCallStack.PushCall("CreateSampler", namedParams.str(), namedParams);
845
846   return Graphics::MakeUnique<TestGraphicsSampler>(mGl, samplerCreateInfo);
847 }
848
849 Graphics::UniquePtr<Graphics::RenderTarget> TestGraphicsController::CreateRenderTarget(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo, Graphics::UniquePtr<Graphics::RenderTarget>&& oldRenderTarget)
850 {
851   mCallStack.PushCall("CreateRenderTarget", "");
852   return nullptr;
853 }
854
855 Graphics::UniquePtr<Graphics::Memory> TestGraphicsController::MapBufferRange(const Graphics::MapBufferInfo& mapInfo)
856 {
857   mCallStack.PushCall("MapBufferRange", "");
858
859   auto buffer = static_cast<TestGraphicsBuffer*>(mapInfo.buffer);
860   buffer->memory.resize(mapInfo.offset + mapInfo.size); // For initial testing, allow writes past capacity
861
862   return std::make_unique<TestGraphicsMemory>(mCallStack, *buffer, mapInfo.offset, mapInfo.size);
863 }
864
865 Graphics::UniquePtr<Graphics::Memory> TestGraphicsController::MapTextureRange(const Graphics::MapTextureInfo& mapInfo)
866 {
867   mCallStack.PushCall("MapTextureRange", "");
868   return nullptr;
869 }
870
871 void TestGraphicsController::UnmapMemory(Graphics::UniquePtr<Graphics::Memory> memory)
872 {
873   mCallStack.PushCall("UnmapMemory", "");
874 }
875
876 Graphics::MemoryRequirements TestGraphicsController::GetTextureMemoryRequirements(Graphics::Texture& texture) const
877 {
878   mCallStack.PushCall("GetTextureMemoryRequirements", "");
879   return Graphics::MemoryRequirements{};
880 }
881
882 Graphics::MemoryRequirements TestGraphicsController::GetBufferMemoryRequirements(Graphics::Buffer& buffer) const
883 {
884   mCallStack.PushCall("GetBufferMemoryRequirements", "");
885   return Graphics::MemoryRequirements{};
886 }
887
888 const Graphics::TextureProperties& TestGraphicsController::GetTextureProperties(const Graphics::Texture& texture)
889 {
890   static Graphics::TextureProperties textureProperties{};
891   mCallStack.PushCall("GetTextureProperties", "");
892
893   return textureProperties;
894 }
895
896 const Graphics::Reflection& TestGraphicsController::GetProgramReflection(const Graphics::Program& program)
897 {
898   mCallStack.PushCall("GetProgramReflection", "");
899
900   return static_cast<const TestGraphicsProgram*>(&program)->GetReflection();
901 }
902
903 bool TestGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const
904 {
905   mCallStack.PushCall("PipelineEquals", "");
906   return false;
907 }
908
909 bool TestGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
910 {
911   mCallStack.PushCall("GetProgramParameter", "");
912   auto graphicsProgram = Uncast<TestGraphicsProgram>(&program);
913   return graphicsProgram->GetParameter(parameterId, outData);
914 }
915
916 } // namespace Dali