shaderData.cpp

Engine/source/materials/shaderData.cpp

More...

Public Functions

ConsoleDocClass(ShaderData , "@brief Special type of data block that stores information about <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> handwritten <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shader.\n\n</a>" "To use hand written shaders, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classshaderdata/">ShaderData</a> datablock must be used. This datablock " "refers only <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the vertex and pixel shader filenames and <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hardware target level. " "Shaders are API specific, so DirectX and <a href="/coding/file/gfxenums_8h/#gfxenums_8h_1a56a87c3b5eae9ccbc914a79dfcd0e5f8adfde619af40503997cc57fc8af21825c">OpenGL</a> shaders must be explicitly <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">identified.\n\n</a> " " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the procedural clould <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system\n</a>" "singleton <a href="/coding/class/classshaderdata/">ShaderData</a>(CloudLayerShader)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " DXVertexShaderFile=$Core::CommonShaderPath @ \"/cloudLayerV.hlsl\";\n" "	DXPixelShaderFile    = $Core::CommonShaderPath @ \"/cloudLayerP.hlsl\";\n" "	OGLVertexShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerV.glsl\";\n" "	OGLPixelShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerP.glsl\";\n" "	pixVersion = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaders\n</a>" )
DefineEngineMethod(ShaderData , reload , void , () , "@brief Rebuilds all the vertex and pixel shader instances created from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShaderData.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Rebuild the shader instances from <a href="/coding/class/classshaderdata/">ShaderData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">CloudLayerShader\n</a>" "CloudLayerShader.reload();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(ShaderData , "@brief Special type of data block that stores information about <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> handwritten <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shader.\n\n</a>" "To use hand written shaders, <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classshaderdata/">ShaderData</a> datablock must be used. This datablock " "refers only <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the vertex and pixel shader filenames and <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> hardware target level. " "Shaders are API specific, so DirectX and <a href="/coding/file/gfxenums_8h/#gfxenums_8h_1a56a87c3b5eae9ccbc914a79dfcd0e5f8adfde619af40503997cc57fc8af21825c">OpenGL</a> shaders must be explicitly <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">identified.\n\n</a> " " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "//Used <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the procedural clould <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">system\n</a>" "singleton <a href="/coding/class/classshaderdata/">ShaderData</a>(CloudLayerShader)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " DXVertexShaderFile=$Core::CommonShaderPath @ \"/cloudLayerV.hlsl\";\n" "	DXPixelShaderFile    = $Core::CommonShaderPath @ \"/cloudLayerP.hlsl\";\n" "	OGLVertexShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerV.glsl\";\n" "	OGLPixelShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerP.glsl\";\n" "	pixVersion = 2.0;\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Shaders\n</a>" )

DefineEngineMethod(ShaderData , reload , void , () , "@brief Rebuilds all the vertex and pixel shader instances created from this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ShaderData.\n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Rebuild the shader instances from <a href="/coding/class/classshaderdata/">ShaderData</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">CloudLayerShader\n</a>" "CloudLayerShader.reload();\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" )

IMPLEMENT_CONOBJECT(ShaderData )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "materials/shaderData.h"
 26
 27#include "console/consoleTypes.h"
 28#include "gfx/gfxDevice.h"
 29#include "core/strings/stringUnit.h"
 30#include "lighting/lightManager.h"
 31#include "console/engineAPI.h"
 32
 33using namespace Torque;
 34
 35
 36Vector<ShaderData*> ShaderData::smAllShaderData;
 37
 38
 39IMPLEMENT_CONOBJECT( ShaderData );
 40
 41ConsoleDocClass( ShaderData,
 42   "@brief Special type of data block that stores information about a handwritten shader.\n\n"
 43
 44   "To use hand written shaders, a ShaderData datablock must be used. This datablock "
 45   "refers only to the vertex and pixel shader filenames and a hardware target level. "
 46   "Shaders are API specific, so DirectX and OpenGL shaders must be explicitly identified.\n\n "
 47
 48   "@tsexample\n"
 49   "// Used for the procedural clould system\n"
 50   "singleton ShaderData( CloudLayerShader )\n"
 51   "{\n"
 52   "  DXVertexShaderFile   = $Core::CommonShaderPath @ \"/cloudLayerV.hlsl\";\n"
 53   "  DXPixelShaderFile    = $Core::CommonShaderPath @ \"/cloudLayerP.hlsl\";\n"
 54   "  OGLVertexShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerV.glsl\";\n"
 55   "  OGLPixelShaderFile = $Core::CommonShaderPath @ \"/gl/cloudLayerP.glsl\";\n"
 56   "  pixVersion = 2.0;\n"
 57   "};\n"
 58   "@endtsexample\n\n"
 59
 60   "@ingroup Shaders\n");
 61
 62ShaderData::ShaderData()
 63{
 64   VECTOR_SET_ASSOCIATION( mShaderMacros );
 65
 66   mUseDevicePixVersion = false;
 67   mPixVersion = 1.0;
 68
 69   for( int i = 0; i < NumTextures; ++i)
 70      mRTParams[i] = false;
 71}
 72
 73void ShaderData::initPersistFields()
 74{
 75   addField("DXVertexShaderFile",   TypeStringFilename,  Offset(mDXVertexShaderName,   ShaderData),
 76      "@brief %Path to the DirectX vertex shader file to use for this ShaderData.\n\n"
 77      "It must contain only one program and no pixel shader, just the vertex shader."
 78      "It can be either an HLSL or assembly level shader. HLSL's must have a "
 79      "filename extension of .hlsl, otherwise its assumed to be an assembly file.");
 80
 81   addField("DXPixelShaderFile",    TypeStringFilename,  Offset(mDXPixelShaderName,  ShaderData),
 82      "@brief %Path to the DirectX pixel shader file to use for this ShaderData.\n\n"
 83      "It must contain only one program and no vertex shader, just the pixel "
 84      "shader. It can be either an HLSL or assembly level shader. HLSL's "
 85      "must have a filename extension of .hlsl, otherwise its assumed to be an assembly file.");
 86
 87   addField("OGLVertexShaderFile",  TypeStringFilename,  Offset(mOGLVertexShaderName,   ShaderData),
 88      "@brief %Path to an OpenGL vertex shader file to use for this ShaderData.\n\n"
 89      "It must contain only one program and no pixel shader, just the vertex shader.");
 90
 91   addField("OGLPixelShaderFile",   TypeStringFilename,  Offset(mOGLPixelShaderName,  ShaderData),
 92      "@brief %Path to an OpenGL pixel shader file to use for this ShaderData.\n\n"
 93      "It must contain only one program and no vertex shader, just the pixel "
 94      "shader.");
 95
 96   addField("useDevicePixVersion",  TypeBool,            Offset(mUseDevicePixVersion,   ShaderData),
 97      "@brief If true, the maximum pixel shader version offered by the graphics card will be used.\n\n"
 98      "Otherwise, the script-defined pixel shader version will be used.\n\n");
 99
100   addField("pixVersion",           TypeF32,             Offset(mPixVersion,   ShaderData),
101      "@brief Indicates target level the shader should be compiled.\n\n"
102      "Valid numbers at the time of this writing are 1.1, 1.4, 2.0, and 3.0. "
103      "The shader will not run properly if the hardware does not support the "
104      "level of shader compiled.");
105   
106   addField("defines",              TypeRealString,      Offset(mDefines,   ShaderData), 
107      "@brief String of case-sensitive defines passed to the shader compiler.\n\n"
108      "The string should be delimited by a semicolon, tab, or newline character."
109      
110      "@tsexample\n"
111       "singleton ShaderData( FlashShader )\n"
112          "{\n"
113              "DXVertexShaderFile   = $shaderGen::cachePath @ \"/postFx/flashV.hlsl\";\n"
114              "DXPixelShaderFile    = $shaderGen::cachePath @ \"/postFx/flashP.hlsl\";\n\n"
115              " //Define setting the color of WHITE_COLOR.\n"
116              "defines = \"WHITE_COLOR=float4(1.0,1.0,1.0,0.0)\";\n\n"
117              "pixVersion = 2.0\n"
118          "}\n"
119      "@endtsexample\n\n"
120      );
121
122   addField("samplerNames",              TypeRealString,      Offset(mSamplerNames,   ShaderData), NumTextures, 
123      "@brief Indicates names of samplers present in shader. Order is important.\n\n"
124      "Order of sampler names are used to assert correct sampler register/location"
125      "Other objects (GFXStateBlockData, PostEffect...) use index number to link samplers."
126      );
127
128   addField("rtParams",              TypeBool,      Offset(mRTParams,   ShaderData), NumTextures, "");
129
130   Parent::initPersistFields();
131
132   // Make sure we get activation signals.
133   LightManager::smActivateSignal.notify( &ShaderData::_onLMActivate );
134}
135
136bool ShaderData::onAdd()
137{
138   if( !Parent::onAdd() )
139      return false;
140
141   mShaderMacros.clear();
142
143   // Keep track of it.
144   smAllShaderData.push_back( this );
145
146   // NOTE: We initialize the shader on request.
147
148   for(int i = 0; i < NumTextures; ++i)
149   {
150      if( mSamplerNames[i].isNotEmpty() && !mSamplerNames[i].startsWith("$") )      
151         mSamplerNames[i].insert(0, "$");      
152   }
153
154   return true;
155}
156
157void ShaderData::onRemove()
158{
159   // Remove it from the all shaders list.
160   smAllShaderData.remove( this );
161
162   Parent::onRemove();
163}
164
165const Vector<GFXShaderMacro>& ShaderData::_getMacros()
166{
167   // If they have already been processed then 
168   // return the cached result.
169   if ( mShaderMacros.size() != 0 || mDefines.isEmpty() )
170      return mShaderMacros;
171
172   mShaderMacros.clear();  
173   GFXShaderMacro macro;
174   const U32 defineCount = StringUnit::getUnitCount( mDefines, ";\n\t" );
175   for ( U32 i=0; i < defineCount; i++ )
176   {
177      String define = StringUnit::getUnit( mDefines, i, ";\n\t" );
178
179      macro.name   = StringUnit::getUnit( define, 0, "=" );
180      macro.value  = StringUnit::getUnit( define, 1, "=" );
181      mShaderMacros.push_back( macro );
182   }
183
184   return mShaderMacros;
185}
186
187GFXShader* ShaderData::getShader( const Vector<GFXShaderMacro> &macros )
188{
189   PROFILE_SCOPE( ShaderData_GetShader );
190
191   // Combine the dynamic macros with our script defined macros.
192   Vector<GFXShaderMacro> finalMacros;
193   finalMacros.merge( _getMacros() );
194   finalMacros.merge( macros );
195
196   // Convert the final macro list to a string.
197   String cacheKey;
198   GFXShaderMacro::stringize( macros, &cacheKey );   
199
200   // Lookup the shader for this instance.
201   ShaderCache::Iterator iter = mShaders.find( cacheKey );
202   if ( iter != mShaders.end() )
203      return iter->value;
204
205   // Create the shader instance... if it fails then
206   // bail out and return nothing to the caller.
207   GFXShader *shader = _createShader( finalMacros );
208   if ( !shader )
209      return NULL;
210
211   _checkDefinition(shader);
212
213   // Store the shader in the cache and return it.
214   mShaders.insertUnique( cacheKey, shader );
215   return shader;
216}
217
218GFXShader* ShaderData::_createShader( const Vector<GFXShaderMacro> &macros )
219{
220   F32 pixver = mPixVersion;
221   if ( mUseDevicePixVersion )
222      pixver = getMax( pixver, GFX->getPixelShaderVersion() );
223
224   // Enable shader error logging.
225   GFXShader::setLogging( true, true );
226
227   GFXShader *shader = GFX->createShader();
228   bool success = false;
229
230   Vector<String> samplers;
231   samplers.setSize(ShaderData::NumTextures);
232   for(int i = 0; i < ShaderData::NumTextures; ++i)
233      samplers[i] = mSamplerNames[i][0] == '$' ? mSamplerNames[i] : "$"+mSamplerNames[i];
234
235   // Initialize the right shader type.
236   switch( GFX->getAdapterType() )
237   {
238      case Direct3D11:
239      {
240         success = shader->init( mDXVertexShaderName, 
241                                 mDXPixelShaderName, 
242                                 pixver,
243                                 macros,
244                                 samplers);
245         break;
246      }
247
248      case OpenGL:
249      {
250         success = shader->init( mOGLVertexShaderName,
251                                 mOGLPixelShaderName,
252                                 pixver,
253                                 macros,
254                                 samplers);
255         break;
256      }
257         
258      default:
259         // Other device types are assumed to not support shaders.
260         success = false;
261         break;
262   }
263
264#if defined(TORQUE_DEBUG)
265   //Assert Sampler registers
266   const Vector<GFXShaderConstDesc>& descs = shader->getShaderConstDesc();
267   for(int i = 0; i < descs.size(); ++i)
268   {
269      if(descs[i].constType != GFXSCT_Sampler && descs[i].constType != GFXSCT_SamplerCube)
270         continue;
271      
272      GFXShaderConstHandle *handle = shader->findShaderConstHandle(descs[i].name);
273      if(!handle || !handle->isValid())
274         continue;
275
276      int reg = handle->getSamplerRegister();
277      if( descs[i].name != samplers[reg] )
278      {
279         const char *err = avar("ShaderData(%s): samplerNames[%d] = \"%s\" are diferent to sampler in shader: %s : register(S%d)"
280            ,getName(), reg, samplers[reg].c_str(), handle->getName().c_str(), reg);
281         Con::printf(err);
282         GFXAssertFatal(0, err);
283      }
284   }
285#endif
286
287   // If we failed to load the shader then
288   // cleanup and return NULL.
289   if ( !success )
290      SAFE_DELETE( shader );
291
292   return shader;
293}
294
295void ShaderData::reloadShaders()
296{
297   ShaderCache::Iterator iter = mShaders.begin();
298   for ( ; iter != mShaders.end(); iter++ )
299      iter->value->reload();
300}
301
302void ShaderData::reloadAllShaders()
303{
304   Vector<ShaderData*>::iterator iter = smAllShaderData.begin();
305   for ( ; iter != smAllShaderData.end(); iter++ )
306      (*iter)->reloadShaders();
307}
308
309void ShaderData::_onLMActivate( const char *lm, bool activate )
310{
311   // Only on activations do we do anything.
312   if ( !activate )
313      return;
314
315   // Since the light manager usually swaps shadergen features
316   // and changes system wide shader defines we need to completely
317   // flush and rebuild all shaders.
318
319   reloadAllShaders();
320}
321
322bool ShaderData::hasSamplerDef(const String &_samplerName, int &pos) const
323{
324   String samplerName = _samplerName.startsWith("$") ? _samplerName : "$"+_samplerName;   
325   for(int i = 0; i < NumTextures; ++i)
326   {
327      if( mSamplerNames[i].equal(samplerName, String::NoCase ) )
328      {
329         pos = i;
330         return true;
331      }
332   }
333
334   pos = -1;
335   return false;
336}
337
338bool ShaderData::_checkDefinition(GFXShader *shader)
339{
340   bool error = false;
341   Vector<String> samplers;
342   samplers.reserve(NumTextures);
343   bool rtParams[NumTextures];
344   for(int i = 0; i < NumTextures; ++i)
345      rtParams[i] = false;   
346
347   const Vector<GFXShaderConstDesc> &shaderConstDesc = shader->getShaderConstDesc(); 
348
349   for(int i = 0; i < shaderConstDesc.size(); ++i)
350   {
351      const GFXShaderConstDesc &desc = shaderConstDesc[i];
352      if(desc.constType == GFXSCT_Sampler)
353      {
354         samplers.push_back(desc.name );
355      }      
356   }
357
358   for(int i = 0; i < samplers.size(); ++i)
359   {
360      int pos;
361      bool find = hasSamplerDef(samplers[i], pos);
362
363      if(find && pos >= 0 && mRTParams[pos])
364      {              
365         if( !shader->findShaderConstHandle( String::ToString("$rtParams%d", pos)) )
366         {
367            String errStr = String::ToString("ShaderData(%s) sampler[%d] used but rtParams%d not used in shader compilation. Possible error", shader->getPixelShaderFile().c_str(), pos, pos);
368            Con::errorf(errStr);
369            error = true;
370         }
371      }     
372
373      if(!find)
374      {
375         String errStr = String::ToString("ShaderData(%s) sampler %s not defined", shader->getPixelShaderFile().c_str(), samplers[i].c_str());
376         Con::errorf(errStr);
377         GFXAssertFatal(0, errStr);
378         error = true;
379      }
380   }  
381
382   return !error;
383}
384
385DefineEngineMethod( ShaderData, reload, void, (),,
386               "@brief Rebuilds all the vertex and pixel shader instances created from this ShaderData.\n\n"
387
388               "@tsexample\n"
389               "// Rebuild the shader instances from ShaderData CloudLayerShader\n"
390               "CloudLayerShader.reload();\n"
391               "@endtsexample\n\n")
392{
393   object->reloadShaders();
394}
395